--- /dev/null
+diff -Nur busybox-1.00/AUTHORS busybox/AUTHORS
+--- busybox-1.00/AUTHORS 2004-07-26 20:57:49.000000000 +0200
++++ busybox/AUTHORS 2005-06-04 08:20:22.000000000 +0200
+@@ -2,12 +2,15 @@
+
+ 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
+-incorect, _please_ let me know.
++incorrect, _please_ let me know.
+
+ -Erik
+
+ -----------
+
++Peter Willis <psyphreak@phreaker.net>
++ eject
++
+ Emanuele Aina <emanuele.aina@tiscali.it>
+ run-parts
+
+@@ -29,6 +32,9 @@
+ John Beppu <beppu@codepoet.org>
+ du, nslookup, sort
+
++David Brownell <dbrownell@users.sourceforge.net>
++ zcip
++
+ Brian Candler <B.Candler@pobox.com>
+ tiny-ls(ls)
+
+@@ -69,6 +75,12 @@
+ Matt Kraai <kraai@alumni.cmu.edu>
+ documentation, bugfixes, test suite
+
++Rob Landley <rob@landley.net>
++ 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).
++ I've patched lots of other applets, but don't maintain 'em.
++
+ Stephan Linz <linz@li-pro.net>
+ ipcalc, Red Hat equivalence
+
+@@ -76,13 +88,14 @@
+ tr
+
+ Glenn McGrath <bug1@iinet.net.au>
+- Common unarchving code and unarchiving applets, ifupdown, ftpgetput,
+- nameif, sed, patch, fold, install, uudecode.
+- Various bugfixes, review and apply numerous patches.
++ Common unarchiving code and unarchiving applets, ifupdown, ftpgetput,
++ nameif, sed, patch, fold, install, uudecode.
++ Various bugfixes, review and apply numerous patches.
+
+ Manuel Novoa III <mjn3@codepoet.org>
+ cat, head, mkfifo, mknod, rmdir, sleep, tee, tty, uniq, usleep, wc, yes,
+- mesg, vconfig, make_directory, parse_mode, dirname, mode_string,
++ 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
+@@ -115,6 +128,9 @@
+ Gyepi Sam <gyepi@praxis-sw.com>
+ Remote logging feature for syslogd
+
++Rob Sullivan <cogito.ergo.cogito@gmail.com>
++ comm
++
+ Linus Torvalds <torvalds@transmeta.com>
+ mkswap, fsck.minix, mkfs.minix
+
+@@ -129,5 +145,6 @@
+ tarcat (since removed), loadkmap, various fixes, Debian maintenance
+
+ Tito Ragusa <farmatito@tiscali.it>
+- devfsd and size optimizations in strings, openvt, chvt, deallocvt, hdparm and fdformat.
++ devfsd and size optimizations in strings, openvt, chvt, deallocvt, hdparm,
++ fdformat, lsattr, chattr, id and eject.
+
+diff -Nur busybox-1.00/Makefile busybox/Makefile
+--- busybox-1.00/Makefile 2004-10-08 09:45:08.000000000 +0200
++++ busybox/Makefile 2005-06-04 08:20:22.000000000 +0200
+@@ -22,7 +22,7 @@
+ #--------------------------------------------------------------
+ noconfig_targets := menuconfig config oldconfig randconfig \
+ defconfig allyesconfig allnoconfig clean distclean \
+- release tags
++ release tags
+
+ ifndef TOPDIR
+ TOPDIR=$(CURDIR)/
+@@ -42,13 +42,12 @@
+ DIRS:=applets archival archival/libunarchive coreutils console-tools \
+ debianutils editors findutils init miscutils modutils networking \
+ networking/libiproute networking/udhcp procps loginutils shell \
+- sysklogd util-linux libpwdgrp coreutils/libcoreutils libbb
++ sysklogd util-linux e2fsprogs libpwdgrp coreutils/libcoreutils libbb
+
+ SRC_DIRS:=$(patsubst %,$(top_srcdir)/%,$(DIRS))
+
+ ifeq ($(strip $(CONFIG_SELINUX)),y)
+-CFLAGS += -I/usr/include/selinux
+-LIBRARIES += -lsecure
++LIBRARIES += -lselinux
+ endif
+
+ CONFIG_CONFIG_IN = $(top_srcdir)/sysdeps/$(TARGET_OS)/Config.in
+@@ -130,7 +129,7 @@
+ busybox.links: $(top_srcdir)/applets/busybox.mkll include/config.h $(top_srcdir)/include/applets.h
+ - $(SHELL) $^ >$@
+
+-install: applets/install.sh busybox busybox.links
++install: $(top_srcdir)/applets/install.sh busybox busybox.links
+ $(SHELL) $< $(PREFIX)
+ ifeq ($(strip $(CONFIG_FEATURE_SUID)),y)
+ @echo
+@@ -147,7 +146,7 @@
+ rm -f $(PREFIX)/bin/busybox
+ for i in `cat busybox.links` ; do rm -f $(PREFIX)$$i; done
+
+-install-hardlinks: applets/install.sh busybox busybox.links
++install-hardlinks: $(top_srcdir)/applets/install.sh busybox busybox.links
+ $(SHELL) $< $(PREFIX) --hardlinks
+
+ check: busybox
+diff -Nur busybox-1.00/TODO busybox/TODO
+--- busybox-1.00/TODO 2004-05-01 02:49:49.000000000 +0200
++++ busybox/TODO 2005-06-04 08:20:22.000000000 +0200
+@@ -2,10 +2,106 @@
+
+ Stuff that needs to be done
+
+-----
+-tr - missing SuS3 features in busybox 1.0pre10
++tr - missing SuS3 features in busybox 1.0pre10
+
+ tr doesnt support [:blank:], [:digit:] or other predefined classes, [=equiv=]
+ support is also missing.
+ ----
+-
++find
++ doesn't understand () or -exec, and these are actually used out in the real
++ world. The "make uninstall" of lots of things (including busybox itself)
++ breaks because of this, and sometimes even "make install" (like udev).
++----
++comm
++ Perl needs "comm" to build. It's small and simple, but we haven't got it.
++---
++sh
++ The command shell situation is a big mess. We have three or four 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. Unifying the various shells and figuring out a configurable
++ way of adding the minimal set of bash features a given script uses is a big
++ job, but it be a big improvement.
++---
++gzip
++ Can't handle compressing multiple files at once. (I don't mean making a
++ multiple file archive, I mean compressing more than one file at a time.)
++ Some global variables aren't re-initialized between runs.
++---
++gunzip
++ same problem as gzip. "gunzip one.gz two.gz three.gz" doesn't work for
++ two.gz and three.gz due to global variables not getting reset.
++---
++diff
++ We should have a diff -u command. We have patch, we should have diff
++ (we only need to support unified diffs though).
++---
++patch
++ should have -i support, and simple fuzz factor support to apply patches
++ at an offset shouldn't take up too much space.
++---
++man
++ It would be nice to have a man command. Not one that handles troff or
++ anything, just one that can handle preformatted ascii man pages, possibly
++ compressed. This could probably be a script in the extras directory that
++ calls cat/zcatbzcat | more
++---
++less
++ More sucks if you're used to less. A tiny less implementation would be
++ very nice.
++---
++bzip2
++ Compression-side support.
++
++
++Architectural issues:
++
++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.
++--
++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 someday,
++ if it becomes relevant.
++---
++Text buffer support.
++ Several existing applets and potential additions (sort, vi, less...) read
++ a whole file into memory and act on it. There might be an opportunity
++ for shared code in there that could be moved into libbb...
++---
++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 is now capable of replacing most gnu packages for real world use,
++ such as developing software or in a live CD. A system built from busybox
++ (1.00 with updated sort.c), uclibc 0.9.27, gcc, binutils, make, and a few
++ other development tools (http://www.landley.net/code/firmware has an example
++ system using autoconf, automake, bison, flex, libtools, m4, zlib,
++ and groff: dunno what subset of that is actually necessary) is capable of
++ rebuilding itself, from scratch, under itself.
++
++ It would be a good "eating our own dogfood" test if buildroot had the option
++ of using busybox instead of bzip2, coreutils, file, findutils, gawk, grep,
++ inetutils, modutils, net-tools, procps, sed, shadow, sysklogd, sysvinit, tar,
++ util-linux, and vim. 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, diffutils, gzip, less, and patch as well.)
+diff -Nur busybox-1.00/applets/busybox.c busybox/applets/busybox.c
+--- busybox-1.00/applets/busybox.c 2004-03-15 09:28:15.000000000 +0100
++++ busybox/applets/busybox.c 2005-06-04 08:20:20.000000000 +0200
+@@ -144,25 +144,25 @@
+ output_width -= 20;
+ #endif
+
+- fprintf(stderr, "%s\n\n"
+- "Usage: busybox [function] [arguments]...\n"
+- " or: [function] [arguments]...\n\n"
+- "\tBusyBox is a multi-call binary that combines many common Unix\n"
+- "\tutilities into a single executable. Most people will create a\n"
+- "\tlink to busybox for each function they wish to use, and BusyBox\n"
+- "\twill act like whatever it was invoked as.\n"
+- "\nCurrently defined functions:\n", bb_msg_full_version);
++ printf("%s\n\n"
++ "Usage: busybox [function] [arguments]...\n"
++ " or: [function] [arguments]...\n\n"
++ "\tBusyBox is a multi-call binary that combines many common Unix\n"
++ "\tutilities into a single executable. Most people will create a\n"
++ "\tlink to busybox for each function they wish to use and BusyBox\n"
++ "\twill act like whatever it was invoked as!\n"
++ "\nCurrently defined functions:\n", bb_msg_full_version);
+
+ while (a->name != 0) {
+ col +=
+- fprintf(stderr, "%s%s", ((col == 0) ? "\t" : ", "),
+- (a++)->name);
++ printf("%s%s", ((col == 0) ? "\t" : ", "),
++ (a++)->name);
+ if (col > output_width && a->name != 0) {
+- fprintf(stderr, ",\n");
++ printf(",\n");
+ col = 0;
+ }
+ }
+- fprintf(stderr, "\n\n");
++ printf("\n\n");
+ exit(0);
+ }
+
+diff -Nur busybox-1.00/archival/ar.c busybox/archival/ar.c
+--- busybox-1.00/archival/ar.c 2004-10-07 02:35:59.000000000 +0200
++++ busybox/archival/ar.c 2005-06-04 08:20:08.000000000 +0200
+@@ -56,19 +56,21 @@
+ #define AR_OPT_PRESERVE_DATE 0x08
+ #define AR_OPT_VERBOSE 0x10
+ #define AR_OPT_CREATE 0x20
++#define AR_OPT_INSERT 0x40
+
+ extern int ar_main(int argc, char **argv)
+ {
+ archive_handle_t *archive_handle;
+ unsigned long opt;
++ char *msg_unsupported_err = "Archive %s not supported. Install binutils 'ar'.";
+ char magic[8];
+
+ archive_handle = init_handle();
+
+ bb_opt_complementaly = "p~tx:t~px:x~pt";
+- opt = bb_getopt_ulflags(argc, argv, "ptxovc");
++ opt = bb_getopt_ulflags(argc, argv, "ptxovcr");
+
+- if ((opt & 0x80000000UL) || (optind == argc)) {
++ if ((opt & BB_GETOPT_ERROR) || (opt == 0) || (optind == argc)) {
+ bb_show_usage();
+ }
+
+@@ -88,7 +90,10 @@
+ archive_handle->action_header = header_verbose_list_ar;
+ }
+ if (opt & AR_OPT_CREATE) {
+- bb_error_msg_and_die("Archive creation not supported. Install binutils 'ar'.");
++ bb_error_msg_and_die(msg_unsupported_err, "creation");
++ }
++ if (opt & AR_OPT_INSERT) {
++ bb_error_msg_and_die(msg_unsupported_err, "insertion");
+ }
+
+ archive_handle->src_fd = bb_xopen(argv[optind++], O_RDONLY);
+diff -Nur busybox-1.00/archival/dpkg.c busybox/archival/dpkg.c
+--- busybox-1.00/archival/dpkg.c 2004-04-14 19:51:08.000000000 +0200
++++ busybox/archival/dpkg.c 2005-06-04 08:20:08.000000000 +0200
+@@ -58,7 +58,7 @@
+ * 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
+-char *name_hashtable[NAME_HASH_PRIME + 1];
++static char *name_hashtable[NAME_HASH_PRIME + 1];
+
+ /* PACKAGE_HASH_PRIME, Maximum number of unique packages,
+ * It must not be smaller than STATUS_HASH_PRIME,
+@@ -82,7 +82,7 @@
+ unsigned int num_of_edges:14;
+ edge_t **edge;
+ } common_node_t;
+-common_node_t *package_hashtable[PACKAGE_HASH_PRIME + 1];
++static common_node_t *package_hashtable[PACKAGE_HASH_PRIME + 1];
+
+ /* 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
+@@ -92,7 +92,7 @@
+ unsigned int package:14; /* has to fit PACKAGE_HASH_PRIME */
+ unsigned int status:14; /* has to fit STATUS_HASH_PRIME */
+ } status_node_t;
+-status_node_t *status_hashtable[STATUS_HASH_PRIME + 1];
++static status_node_t *status_hashtable[STATUS_HASH_PRIME + 1];
+
+ /* Even numbers are for 'extras', like ored dependencies or null */
+ enum edge_type_e {
+@@ -137,7 +137,7 @@
+ } deb_file_t;
+
+
+-void make_hash(const char *key, unsigned int *start, unsigned int *decrement, const int hash_prime)
++static void make_hash(const char *key, unsigned int *start, unsigned int *decrement, const int hash_prime)
+ {
+ unsigned long int hash_num = key[0];
+ int len = strlen(key);
+@@ -157,7 +157,7 @@
+ }
+
+ /* this adds the key to the hash table */
+-int search_name_hashtable(const char *key)
++static int search_name_hashtable(const char *key)
+ {
+ unsigned int probe_address = 0;
+ unsigned int probe_decrement = 0;
+@@ -181,7 +181,7 @@
+ /* this DOESNT add the key to the hashtable
+ * TODO make it consistent with search_name_hashtable
+ */
+-unsigned int search_status_hashtable(const char *key)
++static unsigned int search_status_hashtable(const char *key)
+ {
+ unsigned int probe_address = 0;
+ unsigned int probe_decrement = 0;
+@@ -201,7 +201,7 @@
+ }
+
+ /* Need to rethink version comparison, maybe the official dpkg has something i can use ? */
+-int version_compare_part(const char *version1, const char *version2)
++static int version_compare_part(const char *version1, const char *version2)
+ {
+ int upstream_len1 = 0;
+ int upstream_len2 = 0;
+@@ -268,7 +268,7 @@
+ * if ver1 = ver2 return 0,
+ * if ver1 > ver2 return 1,
+ */
+-int version_compare(const unsigned int ver1, const unsigned int ver2)
++static int version_compare(const unsigned int ver1, const unsigned int ver2)
+ {
+ char *ch_ver1 = name_hashtable[ver1];
+ char *ch_ver2 = name_hashtable[ver2];
+@@ -330,7 +330,7 @@
+ return(version_compare_part(deb_ver1, deb_ver2));
+ }
+
+-int test_version(const unsigned int version1, const unsigned int version2, const unsigned int operator)
++static int test_version(const unsigned int version1, const unsigned int version2, const unsigned int operator)
+ {
+ const int version_result = version_compare(version1, version2);
+ switch(operator) {
+@@ -366,7 +366,7 @@
+ }
+
+
+-int search_package_hashtable(const unsigned int name, const unsigned int version, const unsigned int operator)
++static int search_package_hashtable(const unsigned int name, const unsigned int version, const unsigned int operator)
+ {
+ unsigned int probe_address = 0;
+ unsigned int probe_decrement = 0;
+@@ -405,7 +405,7 @@
+ * 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.
+ */
+-int search_for_provides(int needle, int start_at) {
++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++) {
+@@ -421,7 +421,7 @@
+ /*
+ * Add an edge to a node
+ */
+-void add_edge_to_node(common_node_t *node, edge_t *edge)
++static void add_edge_to_node(common_node_t *node, edge_t *edge)
+ {
+ node->num_of_edges++;
+ node->edge = xrealloc(node->edge, sizeof(edge_t) * (node->num_of_edges + 1));
+@@ -438,7 +438,7 @@
+ * field contains the number of EDGE nodes which follow as part of
+ * this alternative.
+ */
+-void add_split_dependencies(common_node_t *parent_node, const char *whole_line, unsigned int edge_type)
++static void add_split_dependencies(common_node_t *parent_node, const char *whole_line, unsigned int edge_type)
+ {
+ char *line = bb_xstrdup(whole_line);
+ char *line2;
+@@ -537,7 +537,7 @@
+ return;
+ }
+
+-void free_package(common_node_t *node)
++static void free_package(common_node_t *node)
+ {
+ unsigned short i;
+ if (node) {
+@@ -550,7 +550,7 @@
+ }
+ }
+
+-unsigned int fill_package_struct(char *control_buffer)
++static unsigned int fill_package_struct(char *control_buffer)
+ {
+ common_node_t *new_node = (common_node_t *) xcalloc(1, sizeof(common_node_t));
+ const char *field_names[] = { "Package", "Version", "Pre-Depends", "Depends",
+@@ -624,7 +624,7 @@
+ }
+
+ /* if num = 1, it returns the want status, 2 returns flag, 3 returns status */
+-unsigned int get_status(const unsigned int status_node, const int num)
++static unsigned int get_status(const unsigned int status_node, const int num)
+ {
+ char *status_string = name_hashtable[status_hashtable[status_node]->status];
+ char *state_sub_string;
+@@ -646,7 +646,7 @@
+ return(state_sub_num);
+ }
+
+-void set_status(const unsigned int status_node_num, const char *new_value, const int position)
++static void set_status(const unsigned int status_node_num, const char *new_value, const int position)
+ {
+ const unsigned int new_value_len = strlen(new_value);
+ const unsigned int new_value_num = search_name_hashtable(new_value);
+@@ -682,7 +682,7 @@
+ return;
+ }
+
+-const char *describe_status(int status_num) {
++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\n";
+@@ -707,7 +707,7 @@
+ }
+
+
+-void index_status_file(const char *filename)
++static void index_status_file(const char *filename)
+ {
+ FILE *status_file;
+ char *control_buffer;
+@@ -812,7 +812,7 @@
+ }
+ #endif
+
+-void write_buffer_no_status(FILE *new_status_file, const char *control_buffer)
++static void write_buffer_no_status(FILE *new_status_file, const char *control_buffer)
+ {
+ char *name;
+ char *value;
+@@ -830,7 +830,7 @@
+ }
+
+ /* This could do with a cleanup */
+-void write_status_file(deb_file_t **deb_file)
++static void write_status_file(deb_file_t **deb_file)
+ {
+ FILE *old_status_file = bb_xfopen("/var/lib/dpkg/status", "r");
+ FILE *new_status_file = bb_xfopen("/var/lib/dpkg/status.udeb", "w");
+@@ -978,7 +978,7 @@
+ * which a regular depends can be satisfied by a package which we want
+ * to install.
+ */
+-int package_satisfies_dependency(int package, int depend_type)
++static int package_satisfies_dependency(int package, int depend_type)
+ {
+ int status_num = search_status_hashtable(name_hashtable[package_hashtable[package]->name]);
+
+@@ -995,7 +995,7 @@
+ return 0;
+ }
+
+-int check_deps(deb_file_t **deb_file, int deb_start, int dep_max_count)
++static int check_deps(deb_file_t **deb_file, int deb_start, int dep_max_count)
+ {
+ int *conflicts = NULL;
+ int conflicts_num = 0;
+@@ -1204,7 +1204,7 @@
+ return(TRUE);
+ }
+
+-char **create_list(const char *filename)
++static char **create_list(const char *filename)
+ {
+ FILE *list_stream;
+ char **file_list = NULL;
+@@ -1233,7 +1233,7 @@
+ }
+
+ /* maybe i should try and hook this into remove_file.c somehow */
+-int remove_file_array(char **remove_names, char **exclude_names)
++static int remove_file_array(char **remove_names, char **exclude_names)
+ {
+ struct stat path_stat;
+ int match_flag;
+@@ -1271,7 +1271,7 @@
+ return(remove_flag);
+ }
+
+-int run_package_script(const char *package_name, const char *script_type)
++static int run_package_script(const char *package_name, const char *script_type)
+ {
+ struct stat path_stat;
+ char *script_path;
+@@ -1290,10 +1290,10 @@
+ return(result);
+ }
+
+-const char *all_control_files[] = {"preinst", "postinst", "prerm", "postrm",
++static const char *all_control_files[] = {"preinst", "postinst", "prerm", "postrm",
+ "list", "md5sums", "shlibs", "conffiles", "config", "templates", NULL };
+
+-char **all_control_list(const char *package_name)
++static char **all_control_list(const char *package_name)
+ {
+ unsigned short i = 0;
+ char **remove_files;
+@@ -1310,7 +1310,7 @@
+ return(remove_files);
+ }
+
+-void free_array(char **array)
++static void free_array(char **array)
+ {
+
+ if (array) {
+@@ -1327,7 +1327,7 @@
+ * the status_hashtable to retrieve the info. This results in smaller code than
+ * scanning the status file. The resulting list, however, is unsorted.
+ */
+-void list_packages(void)
++static void list_packages(void)
+ {
+ int i;
+
+@@ -1364,7 +1364,7 @@
+ }
+ }
+
+-void remove_package(const unsigned int package_num, int noisy)
++static void remove_package(const unsigned int 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];
+@@ -1418,7 +1418,7 @@
+ set_status(status_num, "config-files", 3);
+ }
+
+-void purge_package(const unsigned int package_num)
++static void purge_package(const unsigned int package_num)
+ {
+ const char *package_name = name_hashtable[package_hashtable[package_num]->name];
+ const char *package_version = name_hashtable[package_hashtable[package_num]->version];
+@@ -1614,7 +1614,7 @@
+ free(info_prefix);
+ }
+
+-void configure_package(deb_file_t *deb_file)
++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];
+diff -Nur busybox-1.00/archival/dpkg_deb.c busybox/archival/dpkg_deb.c
+--- busybox-1.00/archival/dpkg_deb.c 2004-03-15 09:28:16.000000000 +0100
++++ busybox/archival/dpkg_deb.c 2005-06-04 08:20:08.000000000 +0200
+@@ -88,7 +88,7 @@
+ argcount = 2;
+ }
+
+- if ((optind + argcount != argc) || (opt & 0x80000000UL)) {
++ if ((optind + argcount != argc) || (opt & BB_GETOPT_ERROR)) {
+ bb_show_usage();
+ }
+
+diff -Nur busybox-1.00/archival/gzip.c busybox/archival/gzip.c
+--- busybox-1.00/archival/gzip.c 2004-04-14 19:51:08.000000000 +0200
++++ busybox/archival/gzip.c 2005-06-04 08:20:08.000000000 +0200
+@@ -51,12 +51,6 @@
+ #include <time.h>
+ #include "busybox.h"
+
+-#define memzero(s, n) memset ((void *)(s), 0, (n))
+-
+-#ifndef RETSIGTYPE
+-# define RETSIGTYPE void
+-#endif
+-
+ typedef unsigned char uch;
+ typedef unsigned short ush;
+ typedef unsigned long ulg;
+@@ -214,9 +208,6 @@
+ static int zip(int in, int out);
+ static int file_read(char *buf, unsigned size);
+
+- /* from gzip.c */
+-static RETSIGTYPE abort_gzip(void);
+-
+ /* from deflate.c */
+ static void lm_init(ush * flags);
+ static ulg deflate(void);
+@@ -335,7 +326,7 @@
+ /* ========================================================================
+ * Signal and error handler.
+ */
+-static void abort_gzip()
++static void abort_gzip(int ignored)
+ {
+ exit(ERROR);
+ }
+@@ -350,13 +341,6 @@
+ bytes_in = 0L;
+ }
+
+-static void write_bb_error_msg(void)
+-{
+- fputc('\n', stderr);
+- bb_perror_nomsg();
+- abort_gzip();
+-}
+-
+ /* ===========================================================================
+ * Does the same as write(), but also handles partial pipe writes and checks
+ * for error return.
+@@ -366,9 +350,7 @@
+ unsigned n;
+
+ while ((n = write(fd, buf, cnt)) != cnt) {
+- if (n == (unsigned) (-1)) {
+- write_bb_error_msg();
+- }
++ if (n == (unsigned) (-1)) bb_error_msg_and_die("can't write");
+ cnt -= n;
+ buf = (void *) ((char *) buf + n);
+ }
+@@ -559,7 +541,7 @@
+ /* ===========================================================================
+ * Write out any remaining bits in an incomplete byte.
+ */
+-static void bi_windup()
++static void bi_windup(void)
+ {
+ if (bi_valid > 8) {
+ put_short(bi_buf);
+@@ -846,7 +828,7 @@
+ register unsigned j;
+
+ /* Initialize the hash table. */
+- memzero((char *) head, HASH_SIZE * sizeof(*head));
++ memset(head, 0, HASH_SIZE * sizeof(*head));
+ /* prev will be initialized on the fly */
+
+ *flags |= SLOW;
+@@ -996,7 +978,7 @@
+ * file reads are performed for at least two bytes (required for the
+ * translate_eol option).
+ */
+-static void fill_window()
++static void fill_window(void)
+ {
+ register unsigned n, m;
+ unsigned more =
+@@ -1060,7 +1042,7 @@
+ * evaluation for matches: a match is finally adopted only if there is
+ * no better match at the next window position.
+ */
+-static ulg deflate()
++static ulg deflate(void)
+ {
+ IPos hash_head; /* head of hash chain */
+ IPos prev_match; /* previous match */
+@@ -1188,8 +1170,6 @@
+
+ typedef struct dirent dir_type;
+
+-typedef RETSIGTYPE(*sig_type) (int);
+-
+ /* ======================================================================== */
+ int gzip_main(int argc, char **argv)
+ {
+@@ -1235,16 +1215,16 @@
+
+ foreground = signal(SIGINT, SIG_IGN) != SIG_IGN;
+ if (foreground) {
+- (void) signal(SIGINT, (sig_type) abort_gzip);
++ (void) signal(SIGINT, abort_gzip);
+ }
+ #ifdef SIGTERM
+ if (signal(SIGTERM, SIG_IGN) != SIG_IGN) {
+- (void) signal(SIGTERM, (sig_type) abort_gzip);
++ (void) signal(SIGTERM, abort_gzip);
+ }
+ #endif
+ #ifdef SIGHUP
+ if (signal(SIGHUP, SIG_IGN) != SIG_IGN) {
+- (void) signal(SIGHUP, (sig_type) abort_gzip);
++ (void) signal(SIGHUP, abort_gzip);
+ }
+ #endif
+
+@@ -1271,6 +1251,7 @@
+ for (i = optind; i < argc; i++) {
+ char *path = NULL;
+
++ clear_bufs();
+ if (strcmp(argv[i], "-") == 0) {
+ time_stamp = 0;
+ ifile_size = -1L;
+@@ -1749,7 +1730,7 @@
+ /* ===========================================================================
+ * Initialize a new block.
+ */
+-static void init_block()
++static void init_block(void)
+ {
+ int n; /* iterates over tree elements */
+
+@@ -2162,7 +2143,7 @@
+ * Construct the Huffman tree for the bit lengths and return the index in
+ * bl_order of the last bit length code to send.
+ */
+-static const int build_bl_tree()
++static int build_bl_tree(void)
+ {
+ int max_blindex; /* index of last bit length code of non zero freq */
+
+@@ -2425,7 +2406,7 @@
+ * IN assertion: the fields freq of dyn_ltree are set and the total of all
+ * frequencies does not exceed 64K (to fit in an int on 16 bit machines).
+ */
+-static void set_file_type()
++static void set_file_type(void)
+ {
+ int n = 0;
+ unsigned ascii_freq = 0;
+@@ -2538,7 +2519,7 @@
+ * Write the output buffer outbuf[0..outcnt-1] and update bytes_out.
+ * (used for the compressed data only)
+ */
+-static void flush_outbuf()
++static void flush_outbuf(void)
+ {
+ if (outcnt == 0)
+ return;
+diff -Nur busybox-1.00/archival/libunarchive/archive_xread_all_eof.c busybox/archival/libunarchive/archive_xread_all_eof.c
+--- busybox-1.00/archival/libunarchive/archive_xread_all_eof.c 2003-11-21 23:24:48.000000000 +0100
++++ busybox/archival/libunarchive/archive_xread_all_eof.c 2005-06-04 08:20:08.000000000 +0200
+@@ -26,7 +26,7 @@
+
+ size = bb_full_read(archive_handle->src_fd, buf, count);
+ if ((size != 0) && (size != count)) {
+- bb_perror_msg_and_die("Short read, read %d of %d", size, count);
++ bb_perror_msg_and_die("Short read, read %ld of %ld", (long)size, (long)count);
+ }
+ return(size);
+ }
+diff -Nur busybox-1.00/archival/libunarchive/check_header_gzip.c busybox/archival/libunarchive/check_header_gzip.c
+--- busybox-1.00/archival/libunarchive/check_header_gzip.c 2003-03-19 10:11:25.000000000 +0100
++++ busybox/archival/libunarchive/check_header_gzip.c 2005-06-04 08:20:08.000000000 +0200
+@@ -1,6 +1,7 @@
+ #include <stdlib.h>
+ #include <unistd.h>
+ #include "libbb.h"
++#include "unarchive.h" /* for external decl of check_header_gzip */
+
+ extern void check_header_gzip(int src_fd)
+ {
+diff -Nur busybox-1.00/archival/libunarchive/decompress_bunzip2.c busybox/archival/libunarchive/decompress_bunzip2.c
+--- busybox-1.00/archival/libunarchive/decompress_bunzip2.c 2004-08-28 02:43:05.000000000 +0200
++++ busybox/archival/libunarchive/decompress_bunzip2.c 2005-06-04 08:20:08.000000000 +0200
+@@ -134,8 +134,6 @@
+
+ static int get_next_block(bunzip_data *bd)
+ {
+- /* Note: Ignore the warning about hufGroup, base and limit being used uninitialized.
+- * They will be initialized on the fist pass of the loop. */
+ struct group_data *hufGroup;
+ int dbufCount,nextSym,dbufSize,groupCount,*base,*limit,selector,
+ i,j,k,t,runPos,symCount,symTotal,nSelectors,byteCount[256];
+@@ -286,16 +284,15 @@
+ mtfSymbol[i]=(unsigned char)i;
+ }
+ /* Loop through compressed symbols. */
+- runPos=dbufCount=symCount=selector=0;
++ runPos=dbufCount=selector=0;
+ for(;;) {
+- /* Determine which Huffman coding group to use. */
+- if(!(symCount--)) {
+- symCount=GROUP_SIZE-1;
+- if(selector>=nSelectors) return RETVAL_DATA_ERROR;
+- hufGroup=bd->groups+selectors[selector++];
+- base=hufGroup->base-1;
+- limit=hufGroup->limit-1;
+- }
++ /* 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 an additional bit at a time, testing
+@@ -346,7 +343,7 @@
+ context). Thus space is saved. */
+ t += (runPos << nextSym); /* +runPos if RUNA; +2*runPos if RUNB */
+ runPos <<= 1;
+- continue;
++ 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
+@@ -384,6 +381,10 @@
+ /* We have our literal byte. Save it into dbuf. */
+ byteCount[uc]++;
+ dbuf[dbufCount++] = (unsigned int)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--) 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
+diff -Nur busybox-1.00/archival/libunarchive/decompress_uncompress.c busybox/archival/libunarchive/decompress_uncompress.c
+--- busybox-1.00/archival/libunarchive/decompress_uncompress.c 2004-04-14 19:51:08.000000000 +0200
++++ busybox/archival/libunarchive/decompress_uncompress.c 2005-06-04 08:20:08.000000000 +0200
+@@ -65,23 +65,23 @@
+ #define MAXCODE(n) (1L << (n))
+
+ /* Block compress mode -C compatible with 2.0 */
+-int block_mode = BLOCK_MODE;
++static int block_mode = BLOCK_MODE;
+
+ /* user settable max # bits/code */
+-int maxbits = BITS;
++static int maxbits = BITS;
+
+ /* Exitcode of compress (-1 no file compressed) */
+-int exit_code = -1;
++static int exit_code = -1;
+
+ /* Input buffer */
+-unsigned char inbuf[IBUFSIZ + 64];
++static unsigned char inbuf[IBUFSIZ + 64];
+
+ /* Output buffer */
+-unsigned char outbuf[OBUFSIZ + 2048];
++static unsigned char outbuf[OBUFSIZ + 2048];
+
+
+-long int htab[HSIZE];
+-unsigned short codetab[HSIZE];
++static long int htab[HSIZE];
++static unsigned short codetab[HSIZE];
+
+ #define htabof(i) htab[i]
+ #define codetabof(i) codetab[i]
+diff -Nur busybox-1.00/archival/libunarchive/decompress_unzip.c busybox/archival/libunarchive/decompress_unzip.c
+--- busybox-1.00/archival/libunarchive/decompress_unzip.c 2004-04-25 07:11:13.000000000 +0200
++++ busybox/archival/libunarchive/decompress_unzip.c 2005-06-04 08:20:08.000000000 +0200
+@@ -151,7 +151,10 @@
+ /* Leave the first 4 bytes empty so we can always unwind the bitbuffer
+ * to the front of the bytebuffer, leave 4 bytes free at end of tail
+ * so we can easily top up buffer in check_trailer_gzip() */
+- bytebuffer_size = 4 + bb_xread(gunzip_src_fd, &bytebuffer[4], bytebuffer_max - 8);
++ if (!(bytebuffer_size = bb_xread(gunzip_src_fd, &bytebuffer[4], bytebuffer_max - 8))) {
++ bb_error_msg_and_die("unexpected end of file");
++ }
++ bytebuffer_size += 4;
+ bytebuffer_offset = 4;
+ }
+ bitbuffer |= ((unsigned int) bytebuffer[bytebuffer_offset]) << *current;
+diff -Nur busybox-1.00/archival/rpm.c busybox/archival/rpm.c
+--- busybox-1.00/archival/rpm.c 2004-03-15 09:28:16.000000000 +0100
++++ busybox/archival/rpm.c 2005-06-04 08:20:08.000000000 +0200
+@@ -260,12 +260,16 @@
+ int bsearch_rpmtag(const void *key, const void *item)
+ {
+ rpm_index **tmp = (rpm_index **) item;
++ /* gcc throws warnings here when sizeof(void*)!=sizeof(int) ...
++ * it's ok to ignore it because this isn't a 'real' pointer */
+ return ((int) key - tmp[0]->tag);
+ }
+
+ int rpm_getcount(int tag)
+ {
+ rpm_index **found;
++ /* 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((void *) tag, mytags, tagcount, sizeof(struct rpmtag *), bsearch_rpmtag);
+ if (!found) return 0;
+ else return found[0]->count;
+@@ -274,6 +278,8 @@
+ char *rpm_getstring(int tag, int itemindex)
+ {
+ rpm_index **found;
++ /* 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((void *) 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) {
+@@ -288,6 +294,8 @@
+ {
+ rpm_index **found;
+ int n, *tmpint;
++ /* 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((void *) tag, mytags, tagcount, sizeof(struct rpmtag *), bsearch_rpmtag);
+ if (!found || itemindex >= found[0]->count) return -1;
+ tmpint = (int *) (map + found[0]->offset);
+diff -Nur busybox-1.00/archival/rpm2cpio.c busybox/archival/rpm2cpio.c
+--- busybox-1.00/archival/rpm2cpio.c 2004-03-27 11:02:41.000000000 +0100
++++ busybox/archival/rpm2cpio.c 2005-06-04 08:20:08.000000000 +0200
+@@ -48,7 +48,7 @@
+ uint32_t size; /* Size of store (4 bytes) */
+ };
+
+-void skip_header(int rpm_fd)
++static void skip_header(int rpm_fd)
+ {
+ struct rpm_header header;
+
+diff -Nur busybox-1.00/archival/tar.c busybox/archival/tar.c
+--- busybox-1.00/archival/tar.c 2004-08-27 00:18:56.000000000 +0200
++++ busybox/archival/tar.c 2005-06-04 08:20:08.000000000 +0200
+@@ -724,7 +724,7 @@
+ );
+
+ /* Check one and only one context option was given */
+- if(opt & 0x80000000UL) {
++ if(opt & BB_GETOPT_ERROR) {
+ bb_show_usage();
+ }
+ #ifdef CONFIG_FEATURE_TAR_CREATE
+diff -Nur busybox-1.00/coreutils/Config.in busybox/coreutils/Config.in
+--- busybox-1.00/coreutils/Config.in 2004-08-11 04:45:47.000000000 +0200
++++ busybox/coreutils/Config.in 2005-06-04 08:20:13.000000000 +0200
+@@ -59,6 +59,13 @@
+ cmp is used to compare two files and returns the result
+ to standard output.
+
++config CONFIG_COMM
++ bool "comm"
++ default n
++ help
++ comm is used to compare two files line by line and return
++ a three-column output.
++
+ config CONFIG_CP
+ bool "cp"
+ default n
+@@ -164,6 +171,12 @@
+ a command; without options it displays the current
+ environment.
+
++config CONFIG_PRINTENV
++ bool "printenv"
++ default n
++ help
++ printenv is used to print all or part of environment.
++
+ config CONFIG_EXPR
+ bool "expr"
+ default n
+@@ -329,6 +342,12 @@
+ help
+ mv is used to move or rename files or directories.
+
++config CONFIG_NICE
++ bool "nice"
++ default n
++ help
++ nice runs a program with modified scheduling priority.
++
+ config CONFIG_OD
+ bool "od"
+ default n
+@@ -398,12 +417,45 @@
+ help
+ sort is used to sort lines of text in specified files.
+
++config CONFIG_SORT_BIG
++ bool " full SuSv3 compliant sort (Support -ktcsbdfiozgM)"
++ default y
++ depends on CONFIG_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 CONFIG_STAT
++ bool "stat"
++ default n
++ help
++ display file or filesystem status.
++
++config CONFIG_FEATURE_STAT_FORMAT
++ bool " Enable custom formats (-c)"
++ default n
++ depends on CONFIG_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 CONFIG_STTY
+ bool "stty"
+ default n
+ help
+ stty is used to change and print terminal line settings.
+
++config CONFIG_SUM
++ bool "sum"
++ default n
++ help
++ checksum and count the blocks in a file
++
+ config CONFIG_SYNC
+ bool "sync"
+ default n
+diff -Nur busybox-1.00/coreutils/Makefile.in busybox/coreutils/Makefile.in
+--- busybox-1.00/coreutils/Makefile.in 2004-10-08 09:45:14.000000000 +0200
++++ busybox/coreutils/Makefile.in 2005-06-04 08:20:13.000000000 +0200
+@@ -24,69 +24,74 @@
+ srcdir=$(top_srcdir)/coreutils
+
+ COREUTILS-y:=
+-COREUTILS-$(CONFIG_BASENAME) += basename.o
+-COREUTILS-$(CONFIG_CAL) += cal.o
+-COREUTILS-$(CONFIG_CAT) += cat.o
+-COREUTILS-$(CONFIG_CHGRP) += chgrp.o
+-COREUTILS-$(CONFIG_CHMOD) += chmod.o
+-COREUTILS-$(CONFIG_CHOWN) += chown.o
+-COREUTILS-$(CONFIG_CHROOT) += chroot.o
+-COREUTILS-$(CONFIG_CMP) += cmp.o
+-COREUTILS-$(CONFIG_CP) += cp.o
+-COREUTILS-$(CONFIG_CUT) += cut.o
+-COREUTILS-$(CONFIG_DATE) += date.o
+-COREUTILS-$(CONFIG_DD) += dd.o
+-COREUTILS-$(CONFIG_DF) += df.o
+-COREUTILS-$(CONFIG_DIRNAME) += dirname.o
+-COREUTILS-$(CONFIG_DOS2UNIX) += dos2unix.o
+-COREUTILS-$(CONFIG_DU) += du.o
+-COREUTILS-$(CONFIG_ECHO) += echo.o
+-COREUTILS-$(CONFIG_ENV) += env.o
+-COREUTILS-$(CONFIG_EXPR) += expr.o
+-COREUTILS-$(CONFIG_FALSE) += false.o
+-COREUTILS-$(CONFIG_FOLD) += fold.o
+-COREUTILS-$(CONFIG_HEAD) += head.o
+-COREUTILS-$(CONFIG_HOSTID) += hostid.o
+-COREUTILS-$(CONFIG_ID) += id.o
+-COREUTILS-$(CONFIG_INSTALL) += install.o
+-COREUTILS-$(CONFIG_LENGTH) += length.o
+-COREUTILS-$(CONFIG_LN) += ln.o
+-COREUTILS-$(CONFIG_LOGNAME) += logname.o
+-COREUTILS-$(CONFIG_LS) += ls.o
+-COREUTILS-$(CONFIG_MD5SUM) += md5_sha1_sum.o
+-COREUTILS-$(CONFIG_MKDIR) += mkdir.o
+-COREUTILS-$(CONFIG_MKFIFO) += mkfifo.o
+-COREUTILS-$(CONFIG_MKNOD) += mknod.o
+-COREUTILS-$(CONFIG_MV) += mv.o
+-COREUTILS-$(CONFIG_OD) += od.o
+-COREUTILS-$(CONFIG_PRINTF) += printf.o
+-COREUTILS-$(CONFIG_PWD) += pwd.o
+-COREUTILS-$(CONFIG_REALPATH) += realpath.o
+-COREUTILS-$(CONFIG_RM) += rm.o
+-COREUTILS-$(CONFIG_RMDIR) += rmdir.o
+-COREUTILS-$(CONFIG_SEQ) += seq.o
+-COREUTILS-$(CONFIG_SHA1SUM) += md5_sha1_sum.o
+-COREUTILS-$(CONFIG_SLEEP) += sleep.o
+-COREUTILS-$(CONFIG_SORT) += sort.o
+-COREUTILS-$(CONFIG_STTY) += stty.o
+-COREUTILS-$(CONFIG_SYNC) += sync.o
+-COREUTILS-$(CONFIG_TAIL) += tail.o
+-COREUTILS-$(CONFIG_TEE) += tee.o
+-COREUTILS-$(CONFIG_TEST) += test.o
+-COREUTILS-$(CONFIG_TOUCH) += touch.o
+-COREUTILS-$(CONFIG_TR) += tr.o
+-COREUTILS-$(CONFIG_TRUE) += true.o
+-COREUTILS-$(CONFIG_TTY) += tty.o
+-COREUTILS-$(CONFIG_UNAME) += uname.o
+-COREUTILS-$(CONFIG_UNIQ) += uniq.o
+-COREUTILS-$(CONFIG_USLEEP) += usleep.o
+-COREUTILS-$(CONFIG_UUDECODE) += uudecode.o
+-COREUTILS-$(CONFIG_UUENCODE) += uuencode.o
+-COREUTILS-$(CONFIG_WATCH) += watch.o
+-COREUTILS-$(CONFIG_WC) += wc.o
+-COREUTILS-$(CONFIG_WHO) += who.o
+-COREUTILS-$(CONFIG_WHOAMI) += whoami.o
+-COREUTILS-$(CONFIG_YES) += yes.o
++COREUTILS-$(CONFIG_BASENAME) += basename.o
++COREUTILS-$(CONFIG_CAL) += cal.o
++COREUTILS-$(CONFIG_CAT) += cat.o
++COREUTILS-$(CONFIG_CHGRP) += chgrp.o
++COREUTILS-$(CONFIG_CHMOD) += chmod.o
++COREUTILS-$(CONFIG_CHOWN) += chown.o
++COREUTILS-$(CONFIG_CHROOT) += chroot.o
++COREUTILS-$(CONFIG_CMP) += cmp.o
++COREUTILS-$(CONFIG_COMM) += comm.o
++COREUTILS-$(CONFIG_CP) += cp.o
++COREUTILS-$(CONFIG_CUT) += cut.o
++COREUTILS-$(CONFIG_DATE) += date.o
++COREUTILS-$(CONFIG_DD) += dd.o
++COREUTILS-$(CONFIG_DF) += df.o
++COREUTILS-$(CONFIG_DIRNAME) += dirname.o
++COREUTILS-$(CONFIG_DOS2UNIX) += dos2unix.o
++COREUTILS-$(CONFIG_DU) += du.o
++COREUTILS-$(CONFIG_ECHO) += echo.o
++COREUTILS-$(CONFIG_ENV) += env.o
++COREUTILS-$(CONFIG_EXPR) += expr.o
++COREUTILS-$(CONFIG_FALSE) += false.o
++COREUTILS-$(CONFIG_FOLD) += fold.o
++COREUTILS-$(CONFIG_HEAD) += head.o
++COREUTILS-$(CONFIG_HOSTID) += hostid.o
++COREUTILS-$(CONFIG_ID) += id.o
++COREUTILS-$(CONFIG_INSTALL) += install.o
++COREUTILS-$(CONFIG_LENGTH) += length.o
++COREUTILS-$(CONFIG_LN) += ln.o
++COREUTILS-$(CONFIG_LOGNAME) += logname.o
++COREUTILS-$(CONFIG_LS) += ls.o
++COREUTILS-$(CONFIG_MD5SUM) += md5_sha1_sum.o
++COREUTILS-$(CONFIG_MKDIR) += mkdir.o
++COREUTILS-$(CONFIG_MKFIFO) += mkfifo.o
++COREUTILS-$(CONFIG_MKNOD) += mknod.o
++COREUTILS-$(CONFIG_MV) += mv.o
++COREUTILS-$(CONFIG_NICE) += nice.o
++COREUTILS-$(CONFIG_OD) += od.o
++COREUTILS-$(CONFIG_PRINTENV) += printenv.o
++COREUTILS-$(CONFIG_PRINTF) += printf.o
++COREUTILS-$(CONFIG_PWD) += pwd.o
++COREUTILS-$(CONFIG_REALPATH) += realpath.o
++COREUTILS-$(CONFIG_RM) += rm.o
++COREUTILS-$(CONFIG_RMDIR) += rmdir.o
++COREUTILS-$(CONFIG_SEQ) += seq.o
++COREUTILS-$(CONFIG_SHA1SUM) += md5_sha1_sum.o
++COREUTILS-$(CONFIG_SLEEP) += sleep.o
++COREUTILS-$(CONFIG_SORT) += sort.o
++COREUTILS-$(CONFIG_STAT) += stat.o
++COREUTILS-$(CONFIG_STTY) += stty.o
++COREUTILS-$(CONFIG_SUM) += sum.o
++COREUTILS-$(CONFIG_SYNC) += sync.o
++COREUTILS-$(CONFIG_TAIL) += tail.o
++COREUTILS-$(CONFIG_TEE) += tee.o
++COREUTILS-$(CONFIG_TEST) += test.o
++COREUTILS-$(CONFIG_TOUCH) += touch.o
++COREUTILS-$(CONFIG_TR) += tr.o
++COREUTILS-$(CONFIG_TRUE) += true.o
++COREUTILS-$(CONFIG_TTY) += tty.o
++COREUTILS-$(CONFIG_UNAME) += uname.o
++COREUTILS-$(CONFIG_UNIQ) += uniq.o
++COREUTILS-$(CONFIG_USLEEP) += usleep.o
++COREUTILS-$(CONFIG_UUDECODE) += uudecode.o
++COREUTILS-$(CONFIG_UUENCODE) += uuencode.o
++COREUTILS-$(CONFIG_WATCH) += watch.o
++COREUTILS-$(CONFIG_WC) += wc.o
++COREUTILS-$(CONFIG_WHO) += who.o
++COREUTILS-$(CONFIG_WHOAMI) += whoami.o
++COREUTILS-$(CONFIG_YES) += yes.o
+
+ libraries-y+=$(COREUTILS_DIR)$(COREUTILS_AR)
+
+@@ -95,4 +100,3 @@
+
+ $(COREUTILS_DIR)%.o: $(srcdir)/%.c
+ $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $<
+-
+diff -Nur busybox-1.00/coreutils/comm.c busybox/coreutils/comm.c
+--- busybox-1.00/coreutils/comm.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/coreutils/comm.c 2005-06-04 08:20:13.000000000 +0200
+@@ -0,0 +1,144 @@
++/* vi: set sw=4 ts=4: */
++/*
++ * Mini comm implementation for busybox
++ *
++ * Copyright (C) 2005 by Robert Sullivan <cogito.ergo.cogito@gmail.com>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
++ * 02111-1307 USA
++ *
++ */
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include <unistd.h>
++#include "busybox.h"
++
++#define COMM_OPT_1 0x01
++#define COMM_OPT_2 0x02
++#define COMM_OPT_3 0x04
++
++/* These three variables control behaviour if non-zero */
++
++static int only_file_1;
++static int only_file_2;
++static int both;
++
++/* writeline outputs the input given, appropriately aligned according to class */
++static void writeline(char *line, int class)
++{
++ if (class == 0) {
++ if (!only_file_1)
++ return;
++ } else if (class == 1) {
++ if (!only_file_2)
++ return;
++ if (only_file_1)
++ putchar('\t');
++ }
++ else /*if (class == 2)*/ {
++ if (!both)
++ return;
++ if (only_file_1)
++ putchar('\t');
++ if (only_file_2)
++ putchar('\t');
++ }
++ fputs(line, stdout);
++}
++
++/* This is the real core of the program - lines are compared here */
++static void cmp_files(char **infiles)
++{
++#define LINE_LEN 100
++#define BB_EOF_0 0x1
++#define BB_EOF_1 0x2
++ char thisline[2][LINE_LEN];
++ FILE *streams[2];
++ int i;
++
++ for (i = 0; i < 2; ++i) {
++ streams[i] = ((infiles[i][0] == '=' && infiles[i][1]) ? stdin : bb_xfopen(infiles[i], "r"));
++ fgets(thisline[i], LINE_LEN, streams[i]);
++ }
++
++ while (thisline[0] || thisline[1]) {
++ int order = 0;
++
++ i = 0;
++ if (feof(streams[0])) i |= BB_EOF_0;
++ if (feof(streams[1])) i |= BB_EOF_1;
++
++ if (!thisline[0])
++ order = 1;
++ else if (!thisline[1])
++ order = -1;
++ else {
++ int tl0_len, tl1_len;
++ tl0_len = strlen(thisline[0]);
++ tl1_len = strlen(thisline[1]);
++ order = memcmp(thisline[0], thisline[1], tl0_len < tl1_len ? tl0_len : tl1_len);
++ if (!order)
++ order = tl0_len < tl1_len ? -1 : tl0_len != tl1_len;
++ }
++
++ if (order == 0 && !i)
++ writeline(thisline[1], 2);
++ else if (order > 0 && !(i & BB_EOF_1))
++ writeline(thisline[1], 1);
++ else if (order < 0 && !(i & BB_EOF_0))
++ writeline(thisline[0], 0);
++
++ if (i & BB_EOF_0 & BB_EOF_1) {
++ break;
++
++ } else if (i) {
++ i = (i & BB_EOF_0 ? 1 : 0);
++ while (!feof(streams[i])) {
++ if ((order < 0 && i) || (order > 0 && !i))
++ writeline(thisline[i], i);
++ fgets(thisline[i], LINE_LEN, streams[i]);
++ }
++ break;
++
++ } else {
++ if (order >= 0)
++ fgets(thisline[1], LINE_LEN, streams[1]);
++ if (order <= 0)
++ fgets(thisline[0], LINE_LEN, streams[0]);
++ }
++ }
++
++ fclose(streams[0]);
++ fclose(streams[1]);
++}
++
++int comm_main(int argc, char **argv)
++{
++ unsigned long flags;
++
++ flags = bb_getopt_ulflags(argc, argv, "123");
++
++ if (optind + 2 != argc)
++ bb_show_usage();
++
++ only_file_1 = !(flags & COMM_OPT_1);
++ only_file_2 = !(flags & COMM_OPT_2);
++ both = !(flags & COMM_OPT_3);
++
++ cmp_files(argv + optind);
++ exit(EXIT_SUCCESS);
++}
+diff -Nur busybox-1.00/coreutils/cp.c busybox/coreutils/cp.c
+--- busybox-1.00/coreutils/cp.c 2004-01-25 06:50:28.000000000 +0100
++++ busybox/coreutils/cp.c 2005-06-04 08:20:13.000000000 +0200
+@@ -42,7 +42,7 @@
+ #include "libcoreutils/coreutils.h"
+
+ /* WARNING!! ORDER IS IMPORTANT!! */
+-static const char cp_opts[] = "pdRfiar";
++static const char cp_opts[] = "pdRfiarPHL";
+
+ extern int cp_main(int argc, char **argv)
+ {
+@@ -70,9 +70,23 @@
+ if (flags & 64) {
+ /* Make -r a synonym for -R,
+ * -r was marked as obsolete in SUSv3, but is included for compatability
+- */
++ */
+ flags |= FILEUTILS_RECUR;
+ }
++ if (flags & 128) {
++ /* Make -P a synonym for -d,
++ * -d is the GNU option while -P is the POSIX 2003 option
++ */
++ flags |= FILEUTILS_DEREFERENCE;
++ }
++ /* Default behavior of cp is to dereference, so we don't have to do
++ * anything special when we are given -L.
++ * The behavior of -H is *almost* like -L, but not quite, so let's
++ * just ignore it too for fun.
++ if (flags & 256 || flags & 512) {
++ ;
++ }
++ */
+
+ flags ^= FILEUTILS_DEREFERENCE; /* The sense of this flag was reversed. */
+
+@@ -86,7 +100,7 @@
+ /* If there are only two arguments and... */
+ if (optind + 2 == argc) {
+ s_flags = cp_mv_stat2(*argv, &source_stat,
+- (flags & FILEUTILS_DEREFERENCE) ? stat : lstat);
++ (flags & FILEUTILS_DEREFERENCE) ? stat : lstat);
+ if ((s_flags < 0) || ((d_flags = cp_mv_stat(last, &dest_stat)) < 0)) {
+ exit(EXIT_FAILURE);
+ }
+@@ -98,8 +112,8 @@
+ ((((flags & FILEUTILS_RECUR) >> 1) & s_flags) && !d_flags)
+ ) {
+ /* ...do a simple copy. */
+- dest = last;
+- goto DO_COPY; /* Note: optind+2==argc implies argv[1]==last below. */
++ dest = last;
++ goto DO_COPY; /* Note: optind+2==argc implies argv[1]==last below. */
+ }
+ }
+
+diff -Nur busybox-1.00/coreutils/cut.c busybox/coreutils/cut.c
+--- busybox-1.00/coreutils/cut.c 2004-04-14 19:51:09.000000000 +0200
++++ busybox/coreutils/cut.c 2005-06-04 08:20:13.000000000 +0200
+@@ -300,7 +300,7 @@
+ part = opt & (OPT_BYTE_FLGS|OPT_CHAR_FLGS|OPT_FIELDS_FLGS);
+ if(part == 0)
+ bb_error_msg_and_die("you must specify a list of bytes, characters, or fields");
+- if(opt & 0x80000000UL)
++ if(opt & BB_GETOPT_ERROR)
+ bb_error_msg_and_die("only one type of list may be specified");
+ parse_lists(sopt);
+ if((opt & (OPT_DELIM_FLGS))) {
+diff -Nur busybox-1.00/coreutils/date.c busybox/coreutils/date.c
+--- busybox-1.00/coreutils/date.c 2004-10-11 22:52:16.000000000 +0200
++++ busybox/coreutils/date.c 2005-06-04 08:20:13.000000000 +0200
+@@ -136,7 +136,6 @@
+ {
+ char *date_str = NULL;
+ char *date_fmt = NULL;
+- char *t_buff;
+ int set_time;
+ int utc;
+ int use_arg = 0;
+@@ -166,7 +165,7 @@
+ bb_error_msg_and_die(bb_msg_memory_exhausted);
+ }
+ use_arg = opt & DATE_OPT_DATE;
+- if(opt & 0x80000000UL)
++ if(opt & BB_GETOPT_ERROR)
+ bb_show_usage();
+ #ifdef CONFIG_FEATURE_DATE_ISOFMT
+ if(opt & DATE_OPT_TIMESPEC) {
+@@ -283,10 +282,13 @@
+ date_fmt = "%Y.%m.%d-%H:%M:%S";
+ }
+
+- /* Print OUTPUT (after ALL that!) */
+- t_buff = xmalloc(201);
+- strftime(t_buff, 200, date_fmt, &tm_time);
+- puts(t_buff);
++ {
++ /* Print OUTPUT (after ALL that!) */
++ RESERVE_CONFIG_BUFFER(t_buff, 201);
++ strftime(t_buff, 200, date_fmt, &tm_time);
++ puts(t_buff);
++ RELEASE_CONFIG_BUFFER(t_buff);
++ }
+
+ return EXIT_SUCCESS;
+ }
+diff -Nur busybox-1.00/coreutils/expr.c busybox/coreutils/expr.c
+--- busybox-1.00/coreutils/expr.c 2004-04-14 19:51:09.000000000 +0200
++++ busybox/coreutils/expr.c 2005-06-04 08:20:13.000000000 +0200
+@@ -245,10 +245,9 @@
+ static VALUE *docolon (VALUE *sv, VALUE *pv)
+ {
+ VALUE *v;
+- const char *errmsg;
+- struct re_pattern_buffer re_buffer;
+- struct re_registers re_regs;
+- int len;
++ regex_t re_buffer;
++ const int NMATCH = 2;
++ regmatch_t re_regs[NMATCH];
+
+ tostring (sv);
+ tostring (pv);
+@@ -260,27 +259,22 @@
+ pv->u.s);
+ }
+
+- len = strlen (pv->u.s);
+ memset (&re_buffer, 0, sizeof (re_buffer));
+- memset (&re_regs, 0, sizeof (re_regs));
+- re_buffer.allocated = 2 * len;
+- re_buffer.buffer = (unsigned char *) xmalloc (re_buffer.allocated);
+- re_buffer.translate = 0;
+- re_syntax_options = RE_SYNTAX_POSIX_BASIC;
+- errmsg = re_compile_pattern (pv->u.s, len, &re_buffer);
+- if (errmsg) {
+- bb_error_msg_and_die("%s", errmsg);
+- }
+-
+- len = re_match (&re_buffer, sv->u.s, strlen (sv->u.s), 0, &re_regs);
+- if (len >= 0) {
++ memset (re_regs, 0, sizeof (*re_regs));
++ if( regcomp (&re_buffer, pv->u.s, 0) != 0 )
++ bb_error_msg_and_die("Invalid regular expression");
++
++ /* 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) { /* was (re_regs.start[1] >= 0) */
+- sv->u.s[re_regs.end[1]] = '\0';
+- v = str_value (sv->u.s + re_regs.start[1]);
++ if (re_buffer.re_nsub > 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 (len);
++ v = int_value (re_regs[0].rm_eo);
+ }
+ else {
+ /* Match failed -- return the right kind of null. */
+@@ -289,7 +283,6 @@
+ else
+ v = int_value (0);
+ }
+- free (re_buffer.buffer);
+ return v;
+ }
+
+diff -Nur busybox-1.00/coreutils/id.c busybox/coreutils/id.c
+--- busybox-1.00/coreutils/id.c 2004-09-15 05:04:07.000000000 +0200
++++ busybox/coreutils/id.c 2005-06-04 08:20:13.000000000 +0200
+@@ -32,8 +32,7 @@
+ #include <sys/types.h>
+
+ #ifdef CONFIG_SELINUX
+-#include <proc_secure.h>
+-#include <flask_util.h>
++#include <selinux/selinux.h> /* for is_selinux_enabled() */
+ #endif
+
+ #define PRINT_REAL 1
+@@ -61,14 +60,11 @@
+ gid_t gid;
+ unsigned long flags;
+ short status;
+-#ifdef CONFIG_SELINUX
+- int is_flask_enabled_flag = is_flask_enabled();
+-#endif
+
+ bb_opt_complementaly = "u~g:g~u";
+ flags = bb_getopt_ulflags(argc, argv, "rnug");
+
+- if ((flags & 0x80000000UL)
++ if ((flags & BB_GETOPT_ERROR)
+ /* Don't allow -n -r -nr */
+ || (flags <= 3 && flags > 0)
+ /* Don't allow more than one username */
+@@ -109,17 +105,26 @@
+ putchar(' ');
+ /* my_getgrgid doesn't exit on failure here */
+ status|=printf_full(gid, my_getgrgid(NULL, gid, 0), 'g');
++
+ #ifdef CONFIG_SELINUX
+- if(is_flask_enabled_flag) {
+- security_id_t mysid = getsecsid();
+- char context[80];
+- int len = sizeof(context);
+- context[0] = '\0';
+- if(security_sid_to_context(mysid, context, &len))
+- strcpy(context, "unknown");
++ if ( is_selinux_enabled() ) {
++ security_context_t mysid;
++ char context[80];
++ int len = sizeof(context);
++
++ getcon(&mysid);
++ context[0] = '\0';
++ if (mysid) {
++ len = strlen(mysid)+1;
++ safe_strncpy(context, mysid, len);
++ freecon(mysid);
++ }else{
++ safe_strncpy(context, "unknown",8);
++ }
+ bb_printf(" context=%s", context);
+ }
+ #endif
++
+ putchar('\n');
+ bb_fflush_stdout_and_exit(status);
+ }
+diff -Nur busybox-1.00/coreutils/install.c busybox/coreutils/install.c
+--- busybox-1.00/coreutils/install.c 2004-04-25 07:11:14.000000000 +0200
++++ busybox/coreutils/install.c 2005-06-04 08:20:13.000000000 +0200
+@@ -69,7 +69,7 @@
+ flags = bb_getopt_ulflags(argc, argv, "cdpsg:m:o:", &gid_str, &mode_str, &uid_str); /* 'a' must be 2nd */
+
+ /* Check valid options were given */
+- if(flags & 0x80000000UL) {
++ if(flags & BB_GETOPT_ERROR) {
+ bb_show_usage();
+ }
+
+diff -Nur busybox-1.00/coreutils/ln.c busybox/coreutils/ln.c
+--- busybox-1.00/coreutils/ln.c 2004-03-15 09:28:20.000000000 +0100
++++ busybox/coreutils/ln.c 2005-06-04 08:20:13.000000000 +0200
+@@ -21,21 +21,20 @@
+ */
+
+ /* BB_AUDIT SUSv3 compliant */
+-/* BB_AUDIT GNU options missing: -b, -d, -F, -i, -S, and -v. */
++/* BB_AUDIT GNU options missing: -d, -F, -i, and -v. */
+ /* http://www.opengroup.org/onlinepubs/007904975/utilities/ln.html */
+
+-/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
+- *
+- * Fixed bug involving -n option. Essentially, -n was always in effect.
+- */
+-
++#include <stdio.h>
+ #include <stdlib.h>
+ #include <unistd.h>
++#include <errno.h>
+ #include "busybox.h"
+
+ #define LN_SYMLINK 1
+ #define LN_FORCE 2
+ #define LN_NODEREFERENCE 4
++#define LN_BACKUP 8
++#define LN_SUFFIX 16
+
+ extern int ln_main(int argc, char **argv)
+ {
+@@ -44,10 +43,11 @@
+ char *last;
+ char *src_name;
+ char *src;
++ char *suffix = "~";
+ struct stat statbuf;
+ int (*link_func)(const char *, const char *);
+
+- flag = bb_getopt_ulflags(argc, argv, "sfn");
++ flag = bb_getopt_ulflags(argc, argv, "sfnbS:", &suffix);
+
+ if (argc == optind) {
+ bb_show_usage();
+@@ -80,7 +80,23 @@
+ continue;
+ }
+
+- if (flag & LN_FORCE) {
++ if (flag & LN_BACKUP) {
++ char *backup = NULL;
++ bb_xasprintf(&backup, "%s%s", src, suffix);
++ if (rename(src, backup) < 0 && errno != ENOENT) {
++ bb_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 (flag & LN_FORCE) {
+ unlink(src);
+ }
+
+diff -Nur busybox-1.00/coreutils/ls.c busybox/coreutils/ls.c
+--- busybox-1.00/coreutils/ls.c 2004-09-24 04:04:13.000000000 +0200
++++ busybox/coreutils/ls.c 2005-06-04 08:20:13.000000000 +0200
+@@ -64,9 +64,7 @@
+ #include <sys/sysmacros.h> /* major() and minor() */
+ #include "busybox.h"
+ #ifdef CONFIG_SELINUX
+-#include <fs_secure.h>
+-#include <flask_util.h>
+-#include <ss.h>
++#include <selinux/selinux.h> /* for is_selinux_enabled() */
+ #endif
+
+ #ifdef CONFIG_FEATURE_LS_TIMESTAMPS
+@@ -182,7 +180,7 @@
+ char *fullname; /* the dir entry name */
+ struct stat dstat; /* the file stat info */
+ #ifdef CONFIG_SELINUX
+- security_id_t sid;
++ security_context_t sid;
+ #endif
+ struct dnode *next; /* point at the next node */
+ };
+@@ -195,7 +193,7 @@
+ static unsigned int all_fmt;
+
+ #ifdef CONFIG_SELINUX
+-static int is_flask_enabled_flag;
++static int selinux_enabled= 0;
+ #endif
+
+ #ifdef CONFIG_FEATURE_AUTOWIDTH
+@@ -213,18 +211,19 @@
+ struct stat dstat;
+ struct dnode *cur;
+ #ifdef CONFIG_SELINUX
+- security_id_t sid;
++ security_context_t sid=NULL;
+ #endif
+ int rc;
+
+ #ifdef CONFIG_FEATURE_LS_FOLLOWLINKS
+ if (all_fmt & FOLLOW_LINKS) {
+ #ifdef CONFIG_SELINUX
+- if(is_flask_enabled_flag)
+- rc = stat_secure(fullname, &dstat, &sid);
+- else
++ if (is_selinux_enabled()) {
++ rc=0; /* Set the number which means success before hand. */
++ rc = getfilecon(fullname,&sid);
++ }
+ #endif
+- rc = stat(fullname, &dstat);
++ rc = stat(fullname, &dstat);
+ if(rc)
+ {
+ bb_perror_msg("%s", fullname);
+@@ -235,11 +234,12 @@
+ #endif
+ {
+ #ifdef CONFIG_SELINUX
+- if(is_flask_enabled_flag)
+- rc = lstat_secure(fullname, &dstat, &sid);
+- else
++ if (is_selinux_enabled()) {
++ rc=0; /* Set the number which means success before hand. */
++ rc = lgetfilecon(fullname,&sid);
++ }
+ #endif
+- rc = lstat(fullname, &dstat);
++ rc = lstat(fullname, &dstat);
+ if(rc)
+ {
+ bb_perror_msg("%s", fullname);
+@@ -736,12 +736,16 @@
+ #ifdef CONFIG_SELINUX
+ case LIST_CONTEXT:
+ {
+- char context[64];
+- int len = sizeof(context);
+- if(security_sid_to_context(dn->sid, context, &len))
+- {
+- strcpy(context, "unknown");
+- len = 7;
++ char context[80];
++ int len;
++
++ if (dn->sid) {
++ /* I assume sid initilized with NULL */
++ len = strlen(dn->sid)+1;
++ safe_strncpy(context, dn->sid, len);
++ freecon(dn->sid);
++ }else {
++ safe_strncpy(context, "unknown",8);
+ }
+ printf("%-32s ", context);
+ column += MAX(33, len);
+@@ -963,10 +967,6 @@
+ char *terminal_width_str = NULL;
+ #endif
+
+-#ifdef CONFIG_SELINUX
+- is_flask_enabled_flag = is_flask_enabled();
+-#endif
+-
+ all_fmt = LIST_SHORT | DISP_NORMAL | STYLE_AUTO
+ #ifdef CONFIG_FEATURE_LS_TIMESTAMPS
+ | TIME_MOD
+diff -Nur busybox-1.00/coreutils/md5_sha1_sum.c busybox/coreutils/md5_sha1_sum.c
+--- busybox-1.00/coreutils/md5_sha1_sum.c 2004-04-14 19:51:09.000000000 +0200
++++ busybox/coreutils/md5_sha1_sum.c 2005-06-04 08:20:13.000000000 +0200
+@@ -49,38 +49,25 @@
+
+ static uint8_t *hash_file(const char *filename, uint8_t hash_algo)
+ {
+- uint8_t *hash_value_bin;
+- uint8_t *hash_value = NULL;
+- uint8_t hash_length;
+- int src_fd;
+-
+- if (strcmp(filename, "-") == 0) {
+- src_fd = STDIN_FILENO;
+- } else {
+- src_fd = open(filename, O_RDONLY);
+- }
+-
+- if (hash_algo == HASH_MD5) {
+- hash_length = 16;
+- } else {
+- hash_length = 20;
+- }
+-
+- hash_value_bin = xmalloc(hash_length);
+-
+- if ((src_fd != -1) && (hash_fd(src_fd, -1, hash_algo, hash_value_bin) != -2)) {
+- hash_value = hash_bin_to_hex(hash_value_bin, hash_length);
+- } else {
++ int src_fd = strcmp(filename, "-") == 0 ? STDIN_FILENO :
++ open(filename, O_RDONLY);
++ if (src_fd == -1) {
+ bb_perror_msg("%s", filename);
++ return NULL;
++ } else {
++ uint8_t *hash_value;
++ RESERVE_CONFIG_UBUFFER(hash_value_bin, 20);
++ hash_value = hash_fd(src_fd, -1, hash_algo, hash_value_bin) != -2 ?
++ hash_bin_to_hex(hash_value_bin, hash_algo == HASH_MD5 ? 16 : 20) :
++ NULL;
++ RELEASE_CONFIG_BUFFER(hash_value_bin);
++ close(src_fd);
++ return hash_value;
+ }
+-
+- close(src_fd);
+-
+- return(hash_value);
+ }
+
+ /* This could become a common function for md5 as well, by using md5_stream */
+-extern int hash_files(int argc, char **argv, const uint8_t hash_algo)
++static int hash_files(int argc, char **argv, const uint8_t hash_algo)
+ {
+ int return_value = EXIT_SUCCESS;
+ uint8_t *hash_value;
+diff -Nur busybox-1.00/coreutils/nice.c busybox/coreutils/nice.c
+--- busybox-1.00/coreutils/nice.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/coreutils/nice.c 2005-06-04 08:20:13.000000000 +0200
+@@ -0,0 +1,86 @@
++/* vi: set sw=4 ts=4: */
++/*
++ * nice implementation for busybox
++ *
++ * Copyright (C) 2005 Manuel Novoa III <mjn3@codepoet.org>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ */
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include <limits.h>
++#include <errno.h>
++#include <unistd.h>
++#include <sys/time.h>
++#include <sys/resource.h>
++#include "busybox.h"
++
++static inline int int_add_no_wrap(int a, int b)
++{
++ int s = a + b;
++
++ if (b < 0) {
++ if (s > a) s = INT_MIN;
++ } else {
++ if (s < a) s = INT_MAX;
++ }
++
++ return s;
++}
++
++int nice_main(int argc, char **argv)
++{
++ static const char Xetpriority_msg[] = "cannot %cet priority";
++
++ int old_priority, adjustment;
++
++ errno = 0; /* Needed for getpriority error detection. */
++ old_priority = getpriority(PRIO_PROCESS, 0);
++ if (errno) {
++ bb_perror_msg_and_die(Xetpriority_msg, 'g');
++ }
++
++ if (!*++argv) { /* No args, so (GNU) output current nice value. */
++ bb_printf("%d\n", old_priority);
++ bb_fflush_stdout_and_exit(EXIT_SUCCESS);
++ }
++
++ adjustment = 10; /* Set default adjustment. */
++
++ if ((argv[0][0] == '-') && (argv[0][1] == 'n') && !argv[0][2]) { /* "-n" */
++ if (argc < 4) { /* Missing priority and/or utility! */
++ bb_show_usage();
++ }
++ adjustment = bb_xgetlarg(argv[1], 10, INT_MIN, INT_MAX);
++ argv += 2;
++ }
++
++ { /* Set our priority. Handle integer wrapping for old + adjust. */
++ int new_priority = int_add_no_wrap(old_priority, adjustment);
++
++ if (setpriority(PRIO_PROCESS, 0, new_priority) < 0) {
++ bb_perror_msg_and_die(Xetpriority_msg, 's');
++ }
++ }
++
++ execvp(*argv, argv); /* Now exec the desired program. */
++
++ /* The exec failed... */
++ bb_default_error_retval = (errno == ENOENT) ? 127 : 126; /* SUSv3 */
++ bb_perror_msg_and_die("%s", *argv);
++}
+diff -Nur busybox-1.00/coreutils/printenv.c busybox/coreutils/printenv.c
+--- busybox-1.00/coreutils/printenv.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/coreutils/printenv.c 2005-06-04 08:20:13.000000000 +0200
+@@ -0,0 +1,53 @@
++/*
++ * printenv implementation for busybox
++ *
++ * Copyright (C) 2005 by Erik Andersen <andersen@codepoet.org>
++ * Copyright (C) 2005 by Mike Frysinger <vapier@gentoo.org>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ */
++
++#include <stdio.h>
++#include <string.h>
++#include <stdlib.h>
++#include "busybox.h"
++
++int printenv_main(int argc, char **argv)
++{
++ extern char **environ;
++ int e = 0;
++
++ /* no variables specified, show whole env */
++ if (argc == 1)
++ while (environ[e])
++ puts(environ[e++]);
++
++ /* search for specified variables and print them out if found */
++ else {
++ int i;
++ size_t l;
++ char *arg, *env;
++
++ for (i=1; (arg = argv[i]); ++i)
++ for (; (env = environ[e]); ++e) {
++ l = strlen(arg);
++ if (!strncmp(env, arg, l) && env[l] == '=')
++ puts(env + l + 1);
++ }
++ }
++
++ bb_fflush_stdout_and_exit(0);
++}
+diff -Nur busybox-1.00/coreutils/sort.c busybox/coreutils/sort.c
+--- busybox-1.00/coreutils/sort.c 2003-03-19 10:11:34.000000000 +0100
++++ busybox/coreutils/sort.c 2005-06-04 08:20:13.000000000 +0200
+@@ -1,8 +1,8 @@
+ /* vi: set sw=4 ts=4: */
+ /*
+- * Mini sort implementation for busybox
++ * SuS3 compliant sort implementation for busybox
+ *
+- * Copyright (C) 2000 by Matt Kraai <kraai@alumni.carnegiemellon.edu>
++ * Copyright (C) 2004 by Rob Landley <rob@landley.net>
+ *
+ * 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
+@@ -18,83 +18,321 @@
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
++ * See SuS3 sort standard at:
++ * http://www.opengroup.org/onlinepubs/007904975/utilities/sort.html
+ */
+
+-/* BB_AUDIT SUSv3 _NOT_ compliant -- a number of options are not supported. */
+-/* http://www.opengroup.org/onlinepubs/007904975/utilities/sort.html */
+-
+-/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
+- *
+- * Now does proper error checking on i/o. Plus some space savings.
+- */
+-
++#include <ctype.h>
++#include <math.h>
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
++#include <time.h>
+ #include <unistd.h>
+ #include "busybox.h"
+-#include "libcoreutils/coreutils.h"
+
+-static int compare_ascii(const void *x, const void *y)
++static int global_flags;
++
++/*
++ sort [-m][-o output][-bdfinru][-t char][-k keydef]... [file...]
++ sort -c [-bdfinru][-t char][-k keydef][file]
++*/
++
++/* These are sort types */
++#define FLAG_n 1 /* Numeric sort */
++#define FLAG_g 2 /* Sort using strtod() */
++#define FLAG_M 4 /* Sort date */
++/* ucsz apply to root level only, not keys. b at root level implies bb */
++#define FLAG_u 8 /* Unique */
++#define FLAG_c 16 /* Check: no output, exit(!ordered) */
++#define FLAG_s 32 /* Stable sort, no ascii fallback at end */
++#define FLAG_z 64 /* Input is null terminated, not \n */
++/* These can be applied to search keys, the previous four can't */
++#define FLAG_b 128 /* Ignore leading blanks */
++#define FLAG_r 256 /* Reverse */
++#define FLAG_d 512 /* Ignore !(isalnum()|isspace()) */
++#define FLAG_f 1024 /* Force uppercase */
++#define FLAG_i 2048 /* Ignore !isprint() */
++#define FLAG_bb 32768 /* Ignore trailing blanks */
++
++
++#ifdef CONFIG_SORT_BIG
++static char key_separator;
++
++static struct sort_key
+ {
+- return strcmp(*(char **)x, *(char **)y);
+-}
++ struct sort_key *next_key; /* linked list */
++ unsigned short range[4]; /* start word, start char, end word, end char */
++ int flags;
++} *key_list;
+
+-static int compare_numeric(const void *x, const void *y)
++static char *get_key(char *str, struct sort_key *key, int flags)
+ {
+- int z = atoi(*(char **)x) - atoi(*(char **)y);
+- return z ? z : strcmp(*(char **)x, *(char **)y);
++ int start=0,end,len,i,j;
++
++ /* 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++) {
++ /* Skip leading blanks or first separator */
++ if(str[end]) {
++ if(key_separator) {
++ if(str[end]==key_separator) end++;
++ } else if(isspace(str[end]))
++ while(isspace(str[end])) end++;
++ }
++ /* Skip body of key */
++ for(;str[end];end++) {
++ if(key_separator) {
++ if(str[end]==key_separator) break;
++ } else if(isspace(str[end])) break;
++ }
++ }
++ }
++ if(!j) start=end;
++ }
++ /* Key with explicit separator starts after separator */
++ if(key_separator && str[start]==key_separator) start++;
++ /* Strip leading whitespace if necessary */
++ 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=bb_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(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;
+ }
+
+-int sort_main(int argc, char **argv)
++static struct sort_key *add_key(void)
+ {
+- FILE *fp;
+- char *line, **lines = NULL;
+- int i, nlines = 0, inc;
+- int (*compare)(const void *, const void *) = compare_ascii;
++ struct sort_key **pkey=&key_list;
++ while(*pkey) pkey=&((*pkey)->next_key);
++ return *pkey=xcalloc(1,sizeof(struct sort_key));
++}
+
+- int flags;
++#define GET_LINE(fp) (global_flags&FLAG_z) ? bb_get_chunk_from_file(fp) \
++ : bb_get_chomped_line_from_file(fp)
++#else
++#define GET_LINE(fp) bb_get_chomped_line_from_file(fp)
++#endif
+
+- bb_default_error_retval = 2;
++/* Iterate through keys list and perform comparisons */
++static int compare_keys(const void *xarg, const void *yarg)
++{
++ int flags=global_flags,retval=0;
++ char *x,*y;
+
+- flags = bb_getopt_ulflags(argc, argv, "nru");
+- if (flags & 1) {
+- compare = compare_numeric;
++#ifdef CONFIG_SORT_BIG
++ struct sort_key *key;
++
++ for(key=key_list;!retval && key;key=key->next_key) {
++ flags=(key->flags) ? key->flags : global_flags;
++ /* 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:
++ retval=strcmp(x,y);
++ break;
++#ifdef CONFIG_SORT_BIG
++ case FLAG_g:
++ {
++ char *xx,*yy;
++ double dx=strtod(x,&xx), dy=strtod(y,&yy);
++ /* not numbers < NaN < -infinity < numbers < +infinity) */
++ if(x==xx) retval=(y==yy ? 0 : -1);
++ else if(y==yy) retval=1;
++ else if(isnan(dx)) retval=isnan(dy) ? 0 : -1;
++ else if(isnan(dy)) retval=1;
++ else if(isinf(dx)) {
++ if(dx<0) retval=((isinf(dy) && dy<0) ? 0 : -1);
++ else retval=((isinf(dy) && dy>0) ? 0 : 1);
++ } else if(isinf(dy)) 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),dy=atof(y);
++ retval=dx>dy ? 1 : (dx<dy ? -1 : 0);
++ break;
++ }
++ }
++ /* Free key copies. */
++ if(x!=*(char **)xarg) free(x);
++ if(y!=*(char **)yarg) free(y);
++ if(retval) break;
++#else
++ /* Integer version of -n for tiny systems */
++ case FLAG_n:
++ retval=atoi(x)-atoi(y);
++ break;
++ }
++#endif
+ }
++ /* Perform fallback sort if necessary */
++ if(!retval && !(global_flags&FLAG_s))
++ retval=strcmp(*(char **)xarg, *(char **)yarg);
++ return ((flags&FLAG_r)?-1:1)*retval;
++}
+
+- argv += optind;
+- if (!*argv) {
+- *--argv = "-";
+- }
++int sort_main(int argc, char **argv)
++{
++ FILE *fp,*outfile=NULL;
++ int linecount=0,i,flag;
++ char *line,**lines=NULL,c,*optlist="ngMucszbrdfimS:T:o:k:t:";
+
+- do {
+- fp = xgetoptfile_sort_uniq(argv, "r");
+- while ((line = bb_get_chomped_line_from_file(fp)) != NULL) {
+- lines = xrealloc(lines, sizeof(char *) * (nlines + 1));
+- lines[nlines++] = line;
++ bb_default_error_retval = 2;
++ /* Parse command line options */
++ while((c=getopt(argc,argv,optlist))>0) {
++ line=index(optlist,c);
++ if(!line) bb_show_usage();
++ switch(*line) {
++#ifdef CONFIG_SORT_BIG
++ case 'o':
++ if(outfile) bb_error_msg_and_die("Too many -o.");
++ outfile=bb_xfopen(optarg,"w");
++ break;
++ case 't':
++ if(key_separator || optarg[1])
++ bb_error_msg_and_die("Too many -t.");
++ key_separator=*optarg;
++ break;
++ /* parse sort key */
++ case 'k':
++ {
++ struct sort_key *key=add_key();
++ char *temp, *temp2;
++
++ temp=optarg;
++ for(i=0;*temp;) {
++ /* Start of range */
++ key->range[2*i]=(unsigned short)strtol(temp,&temp,10);
++ if(*temp=='.')
++ key->range[(2*i)+1]=(unsigned short)strtol(temp+1,&temp,10);
++ for(;*temp;temp++) {
++ if(*temp==',' && !i++) {
++ temp++;
++ break;
++ } /* no else needed: fall through to syntax error
++ because comma isn't in optlist */
++ temp2=index(optlist,*temp);
++ flag=(1<<(temp2-optlist));
++ if(!temp2 || (flag>FLAG_M && flag<FLAG_b))
++ bb_error_msg_and_die("Unknown key option.");
++ /* b after , means strip _trailing_ space */
++ if(i && flag==FLAG_b) flag=FLAG_bb;
++ key->flags|=flag;
++ }
++ }
++ break;
++ }
++#endif
++ default:
++ global_flags|=(1<<(line-optlist));
++ /* global b strips leading and trailing spaces */
++ if(global_flags&FLAG_b) global_flags|=FLAG_bb;
++ break;
+ }
+- bb_xferror(fp, *argv);
+- bb_fclose_nonstdin(fp);
+- } while (*++argv);
+-
+- /* sort it */
+- qsort(lines, nlines, sizeof(char *), compare);
+-
+- /* print it */
+- i = 0;
+- --nlines;
+- if ((inc = 1 - (flags & 2)) < 0) { /* reverse */
+- i = nlines;
+ }
+- flags &= 4;
+-
+- while (nlines >= 0) {
+- if (!flags || !nlines || strcmp(lines[i+inc], lines[i])) {
+- puts(lines[i]);
++ /* Open input files and read data */
++ for(i=argv[optind] ? optind : optind-1;argv[i];i++) {
++ if(i<optind || (*argv[i]=='-' && !argv[i][1])) fp=stdin;
++ else fp=bb_xfopen(argv[i],"r");
++ for(;;) {
++ line=GET_LINE(fp);
++ if(!line) break;
++ if(!(linecount&63))
++ lines=xrealloc(lines, sizeof(char *)*(linecount+64));
++ lines[linecount++]=line;
+ }
+- i += inc;
+- --nlines;
++ fclose(fp);
+ }
+-
++#ifdef CONFIG_SORT_BIG
++ /* if no key, perform alphabetic sort */
++ if(!key_list) add_key()->range[0]=1;
++ /* handle -c */
++ if(global_flags&FLAG_c) {
++ int j=(global_flags&FLAG_u) ? -1 : 0;
++ for(i=1;i<linecount;i++)
++ if(compare_keys(&lines[i-1],&lines[i])>j) {
++ fprintf(stderr,"Check line %d\n",i);
++ return 1;
++ }
++ return 0;
++ }
++#endif
++ /* Perform the actual sort */
++ qsort(lines,linecount,sizeof(char *),compare_keys);
++ /* handle -u */
++ if(global_flags&FLAG_u) {
++ for(flag=0,i=1;i<linecount;i++) {
++ if(!compare_keys(&lines[flag],&lines[i])) free(lines[i]);
++ else lines[++flag]=lines[i];
++ }
++ if(linecount) linecount=flag+1;
++ }
++ /* Print it */
++ if(!outfile) outfile=stdout;
++ for(i=0;i<linecount;i++) fprintf(outfile,"%s\n",lines[i]);
+ bb_fflush_stdout_and_exit(EXIT_SUCCESS);
+ }
+diff -Nur busybox-1.00/coreutils/stat.c busybox/coreutils/stat.c
+--- busybox-1.00/coreutils/stat.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/coreutils/stat.c 2005-06-04 08:20:13.000000000 +0200
+@@ -0,0 +1,564 @@
++/*
++ * stat -- display file or file system status
++ *
++ * Copyright (C) 2001, 2002, 2003, 2004, 2005 Free Software Foundation.
++ * Copyright (C) 2005 by Erik Andersen <andersen@codepoet.org>
++ * Copyright (C) 2005 by Mike Frysinger <vapier@gentoo.org>
++ *
++ * Written by Michael Meskes
++ * Taken from coreutils and turned into a busybox applet by Mike Frysinger
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ */
++
++#include <stdio.h>
++#include <sys/types.h>
++#include <pwd.h>
++#include <grp.h>
++#include <sys/vfs.h>
++#include <time.h>
++#include <getopt.h>
++#include <sys/stat.h>
++#include <string.h>
++#include "busybox.h"
++
++/* vars to control behavior */
++#define OPT_TERSE 2
++#define OPT_DEREFERNCE 4
++static long flags;
++
++static char const *file_type(struct stat const *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 char const *human_time(time_t t)
++{
++ static char *str;
++ str = ctime(&t);
++ str[strlen(str)-1] = '\0';
++ return str;
++}
++
++/* 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 char const *human_fstype(long f_type)
++{
++ int i;
++ static struct types {
++ long type;
++ char *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" },
++ { 0, NULL }
++ };
++ for (i=0; humantypes[i].type; ++i)
++ if (humantypes[i].type == f_type)
++ return humantypes[i].fs;
++ return humantypes[i].fs;
++}
++
++#ifdef CONFIG_FEATURE_STAT_FORMAT
++/* print statfs info */
++static void print_statfs(char *pformat, size_t buf_len, char m,
++ char const *filename, void const *data)
++{
++ struct statfs const *statfsbuf = data;
++
++ switch (m) {
++ case 'n':
++ strncat(pformat, "s", buf_len);
++ printf(pformat, filename);
++ break;
++ case 'i':
++ strncat(pformat, "Lx", buf_len);
++ printf(pformat, statfsbuf->f_fsid);
++ break;
++ case 'l':
++ strncat(pformat, "lu", buf_len);
++ printf(pformat, statfsbuf->f_namelen);
++ break;
++ case 't':
++ strncat(pformat, "lx", buf_len);
++ printf(pformat, (unsigned long int) (statfsbuf->f_type)); /* no equiv. */
++ break;
++ case 'T':
++ strncat(pformat, "s", buf_len);
++ printf(pformat, human_fstype(statfsbuf->f_type));
++ break;
++ case 'b':
++ strncat(pformat, "ld", buf_len);
++ printf(pformat, (intmax_t) (statfsbuf->f_blocks));
++ break;
++ case 'f':
++ strncat(pformat, "ld", buf_len);
++ printf(pformat, (intmax_t) (statfsbuf->f_bfree));
++ break;
++ case 'a':
++ strncat(pformat, "ld", buf_len);
++ printf(pformat, (intmax_t) (statfsbuf->f_bavail));
++ break;
++ case 's':
++ strncat(pformat, "lu", buf_len);
++ printf(pformat, (unsigned long int) (statfsbuf->f_bsize));
++ break;
++ case 'S': {
++ unsigned long int frsize = statfsbuf->f_frsize;
++ if (!frsize)
++ frsize = statfsbuf->f_bsize;
++ strncat(pformat, "lu", buf_len);
++ printf(pformat, frsize);
++ break;
++ }
++ case 'c':
++ strncat(pformat, "ld", buf_len);
++ printf(pformat, (intmax_t) (statfsbuf->f_files));
++ break;
++ case 'd':
++ strncat(pformat, "ld", buf_len);
++ printf(pformat, (intmax_t) (statfsbuf->f_ffree));
++ break;
++ default:
++ strncat(pformat, "c", buf_len);
++ printf(pformat, m);
++ break;
++ }
++}
++
++/* print stat info */
++static void print_stat(char *pformat, size_t buf_len, char m,
++ char const *filename, void const *data)
++{
++#define TYPE_SIGNED(t) (! ((t) 0 < (t) -1))
++ struct stat *statbuf = (struct stat *) data;
++ struct passwd *pw_ent;
++ struct group *gw_ent;
++
++ switch (m) {
++ case 'n':
++ strncat(pformat, "s", buf_len);
++ printf(pformat, filename);
++ break;
++ case 'N':
++ strncat(pformat, "s", buf_len);
++ if (S_ISLNK(statbuf->st_mode)) {
++ char *linkname = xreadlink(filename);
++ if (linkname == NULL) {
++ bb_perror_msg("cannot read symbolic link '%s'", filename);
++ return;
++ }
++ /*printf("\"%s\" -> \"%s\"", filename, linkname); */
++ printf(pformat, filename);
++ printf(" -> ");
++ printf(pformat, linkname);
++ } else {
++ printf(pformat, filename);
++ }
++ break;
++ case 'd':
++ strncat(pformat, "lu", buf_len);
++ printf(pformat, (uintmax_t) statbuf->st_dev);
++ break;
++ case 'D':
++ strncat(pformat, "lx", buf_len);
++ printf(pformat, (uintmax_t) statbuf->st_dev);
++ break;
++ case 'i':
++ strncat(pformat, "lu", buf_len);
++ printf(pformat, (uintmax_t) statbuf->st_ino);
++ break;
++ case 'a':
++ strncat(pformat, "lo", buf_len);
++ printf(pformat, (unsigned long int) (statbuf->st_mode & (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO)));
++ break;
++ case 'A':
++ strncat(pformat, "s", buf_len);
++ printf(pformat, bb_mode_string(statbuf->st_mode));
++ break;
++ case 'f':
++ strncat(pformat, "lx", buf_len);
++ printf(pformat, (unsigned long int) statbuf->st_mode);
++ break;
++ case 'F':
++ strncat(pformat, "s", buf_len);
++ printf(pformat, file_type(statbuf));
++ break;
++ case 'h':
++ strncat(pformat, "lu", buf_len);
++ printf(pformat, (unsigned long int) statbuf->st_nlink);
++ break;
++ case 'u':
++ strncat(pformat, "lu", buf_len);
++ printf(pformat, (unsigned long int) statbuf->st_uid);
++ break;
++ case 'U':
++ strncat(pformat, "s", buf_len);
++ setpwent();
++ pw_ent = getpwuid(statbuf->st_uid);
++ printf(pformat, (pw_ent != 0L) ? pw_ent->pw_name : "UNKNOWN");
++ break;
++ case 'g':
++ strncat(pformat, "lu", buf_len);
++ printf(pformat, (unsigned long int) statbuf->st_gid);
++ break;
++ case 'G':
++ strncat(pformat, "s", buf_len);
++ setgrent();
++ gw_ent = getgrgid(statbuf->st_gid);
++ printf(pformat, (gw_ent != 0L) ? gw_ent->gr_name : "UNKNOWN");
++ break;
++ case 't':
++ strncat(pformat, "lx", buf_len);
++ printf(pformat, (unsigned long int) major(statbuf->st_rdev));
++ break;
++ case 'T':
++ strncat(pformat, "lx", buf_len);
++ printf(pformat, (unsigned long int) minor(statbuf->st_rdev));
++ break;
++ case 's':
++ strncat(pformat, "lu", buf_len);
++ printf(pformat, (uintmax_t) (statbuf->st_size));
++ break;
++ case 'B':
++ strncat(pformat, "lu", buf_len);
++ printf(pformat, (unsigned long int) 512); //ST_NBLOCKSIZE
++ break;
++ case 'b':
++ strncat(pformat, "lu", buf_len);
++ printf(pformat, (uintmax_t) statbuf->st_blocks);
++ break;
++ case 'o':
++ strncat(pformat, "lu", buf_len);
++ printf(pformat, (unsigned long int) statbuf->st_blksize);
++ break;
++ case 'x':
++ strncat(pformat, "s", buf_len);
++ printf(pformat, human_time(statbuf->st_atime));
++ break;
++ case 'X':
++ strncat(pformat, TYPE_SIGNED(time_t) ? "ld" : "lu", buf_len);
++ printf(pformat, (unsigned long int) statbuf->st_atime);
++ break;
++ case 'y':
++ strncat(pformat, "s", buf_len);
++ printf(pformat, human_time(statbuf->st_mtime));
++ break;
++ case 'Y':
++ strncat(pformat, TYPE_SIGNED(time_t) ? "ld" : "lu", buf_len);
++ printf(pformat, (unsigned long int) statbuf->st_mtime);
++ break;
++ case 'z':
++ strncat(pformat, "s", buf_len);
++ printf(pformat, human_time(statbuf->st_ctime));
++ break;
++ case 'Z':
++ strncat(pformat, TYPE_SIGNED(time_t) ? "ld" : "lu", buf_len);
++ printf(pformat, (unsigned long int) statbuf->st_ctime);
++ break;
++ default:
++ strncat(pformat, "c", buf_len);
++ printf(pformat, m);
++ break;
++ }
++}
++
++static void print_it(char const *masterformat, char const *filename,
++ void (*print_func) (char *, size_t, char, char const *, void const *),
++ void const *data)
++{
++ char *b;
++
++ /* create a working copy of the format string */
++ char *format = bb_xstrdup(masterformat);
++
++ /* Add 2 to accommodate our conversion of the stat `%s' format string
++ * to the printf `%llu' one. */
++ size_t n_alloc = strlen(format) + 2 + 1;
++ char *dest = xmalloc(n_alloc);
++
++ b = format;
++ while (b) {
++ char *p = strchr(b, '%');
++ if (p != NULL) {
++ size_t len;
++ *p++ = '\0';
++ fputs(b, stdout);
++
++ len = strspn(p, "#-+.I 0123456789");
++ dest[0] = '%';
++ memcpy(dest + 1, p, len);
++ dest[1 + len] = 0;
++ p += len;
++
++ b = p + 1;
++ switch (*p) {
++ case '\0':
++ b = NULL;
++ /* fall through */
++ case '%':
++ putchar('%');
++ break;
++ default:
++ print_func(dest, n_alloc, *p, filename, data);
++ break;
++ }
++
++ } else {
++ fputs(b, stdout);
++ b = NULL;
++ }
++ }
++
++ free(format);
++ free(dest);
++}
++#endif
++
++/* Stat the file system and print what we find. */
++static int do_statfs(char const *filename, char const *format)
++{
++ struct statfs statfsbuf;
++
++ if (statfs(filename, &statfsbuf) != 0) {
++ bb_perror_msg("cannot read file system information for '%s'", filename);
++ return 0;
++ }
++
++#ifdef CONFIG_FEATURE_STAT_FORMAT
++ if (format == NULL)
++ format = (flags & OPT_TERSE
++ ? "%n %i %l %t %s %S %b %f %a %c %d\n"
++ : " File: \"%n\"\n"
++ " ID: %-8i Namelen: %-7l Type: %T\n"
++ "Block size: %-10s Fundamental block size: %S\n"
++ "Blocks: Total: %-10b Free: %-10f Available: %a\n"
++ "Inodes: Total: %-10c Free: %d\n");
++ print_it(format, filename, print_statfs, &statfsbuf);
++#else
++
++ format = (flags & OPT_TERSE
++ ? "%s %Lx %lu "
++ : " File: \"%s\"\n"
++ " ID: %-8Lx Namelen: %-7lu ");
++ printf(format,
++ filename,
++ statfsbuf.f_fsid,
++ statfsbuf.f_namelen);
++
++ if (flags & OPT_TERSE)
++ printf("%lx ", (unsigned long int) (statfsbuf.f_type));
++ else
++ printf("Type: %s\n", human_fstype(statfsbuf.f_type));
++
++ format = (flags & OPT_TERSE
++ ? "%lu %lu %ld %ld %ld %ld %ld\n"
++ : "Block size: %-10lu Fundamental block size: %lu\n"
++ "Blocks: Total: %-10ld Free: %-10ld Available: %ld\n"
++ "Inodes: Total: %-10ld Free: %ld\n");
++ printf(format,
++ (unsigned long int) (statfsbuf.f_bsize),
++ statfsbuf.f_frsize ? statfsbuf.f_frsize : statfsbuf.f_bsize,
++ (intmax_t) (statfsbuf.f_blocks),
++ (intmax_t) (statfsbuf.f_bfree),
++ (intmax_t) (statfsbuf.f_bavail),
++ (intmax_t) (statfsbuf.f_files),
++ (intmax_t) (statfsbuf.f_ffree));
++#endif
++
++ return 1;
++}
++
++/* stat the file and print what we find */
++static int do_stat(char const *filename, char const *format)
++{
++ struct stat statbuf;
++
++ if ((flags & OPT_DEREFERNCE ? stat : lstat) (filename, &statbuf) != 0) {
++ bb_perror_msg("cannot stat '%s'", filename);
++ return 0;
++ }
++
++#ifdef CONFIG_FEATURE_STAT_FORMAT
++ if (format == NULL) {
++ if (flags & OPT_TERSE) {
++ format = "%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 =
++ " 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";
++ }
++ }
++ }
++ print_it(format, filename, print_stat, &statbuf);
++#else
++ if (flags & OPT_TERSE) {
++ printf("%s %lu %lu %lx %lu %lu %lx %lu %lu %lx %lx %lu %lu %lu %lu\n",
++ filename,
++ (uintmax_t) (statbuf.st_size),
++ (uintmax_t) statbuf.st_blocks,
++ (unsigned long int) statbuf.st_mode,
++ (unsigned long int) statbuf.st_uid,
++ (unsigned long int) statbuf.st_gid,
++ (uintmax_t) statbuf.st_dev,
++ (uintmax_t) statbuf.st_ino,
++ (unsigned long int) statbuf.st_nlink,
++ (unsigned long int) major(statbuf.st_rdev),
++ (unsigned long int) minor(statbuf.st_rdev),
++ (unsigned long int) statbuf.st_atime,
++ (unsigned long int) statbuf.st_mtime,
++ (unsigned long int) statbuf.st_ctime,
++ (unsigned long int) statbuf.st_blksize
++ );
++ } else {
++ char *linkname = NULL;
++
++ struct passwd *pw_ent;
++ struct group *gw_ent;
++ setgrent();
++ gw_ent = getgrgid(statbuf.st_gid);
++ setpwent();
++ pw_ent = getpwuid(statbuf.st_uid);
++
++ if (S_ISLNK(statbuf.st_mode))
++ linkname = xreadlink(filename);
++ if (linkname)
++ printf(" File: \"%s\" -> \"%s\"\n", filename, linkname);
++ else
++ printf(" File: \"%s\"\n", filename);
++
++ printf(" Size: %-10lu\tBlocks: %-10lu IO Block: %-6lu %s\n"
++ "Device: %lxh/%lud\tInode: %-10lu Links: %-5lu",
++ (uintmax_t) (statbuf.st_size),
++ (uintmax_t) statbuf.st_blocks,
++ (unsigned long int) statbuf.st_blksize,
++ file_type(&statbuf),
++ (uintmax_t) statbuf.st_dev,
++ (uintmax_t) statbuf.st_dev,
++ (uintmax_t) statbuf.st_ino,
++ (unsigned long int) statbuf.st_nlink);
++ if (S_ISBLK(statbuf.st_mode) || S_ISCHR(statbuf.st_mode))
++ printf(" Device type: %lx,%lx\n",
++ (unsigned long int) major(statbuf.st_rdev),
++ (unsigned long int) minor(statbuf.st_rdev));
++ else
++ putchar('\n');
++ printf("Access: (%04lo/%10.10s) Uid: (%5lu/%8s) Gid: (%5lu/%8s)\n"
++ "Access: %s\n" "Modify: %s\n" "Change: %s\n",
++ (unsigned long int) (statbuf.st_mode & (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO)),
++ bb_mode_string(statbuf.st_mode),
++ (unsigned long int) statbuf.st_uid,
++ (pw_ent != 0L) ? pw_ent->pw_name : "UNKNOWN",
++ (unsigned long int) statbuf.st_gid,
++ (gw_ent != 0L) ? gw_ent->gr_name : "UNKNOWN",
++ human_time(statbuf.st_atime),
++ human_time(statbuf.st_mtime),
++ human_time(statbuf.st_ctime));
++ }
++#endif
++ return 1;
++}
++
++int stat_main(int argc, char **argv)
++{
++ int i;
++ char *format = NULL;
++ int ok = 1;
++ int (*statfunc)(char const *, char const *) = do_stat;
++
++ flags = bb_getopt_ulflags(argc, argv, "ftL"
++#ifdef CONFIG_FEATURE_STAT_FORMAT
++ "c:", &format
++#endif
++ );
++
++ if (flags & 1) /* -f */
++ statfunc = do_statfs;
++ if (argc == optind) /* files */
++ bb_show_usage();
++
++ for (i = optind; i < argc; ++i)
++ ok &= statfunc(argv[i], format);
++
++ return (ok ? EXIT_SUCCESS : EXIT_FAILURE);
++}
+diff -Nur busybox-1.00/coreutils/sum.c busybox/coreutils/sum.c
+--- busybox-1.00/coreutils/sum.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/coreutils/sum.c 2005-06-04 08:20:13.000000000 +0200
+@@ -0,0 +1,181 @@
++/*
++ * 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 <andersen@codepoet.org>
++ * Copyright (C) 2005 by Mike Frysinger <vapier@gentoo.org>
++ *
++ * Written by Kayvan Aghaiepour and David MacKenzie
++ * Taken from coreutils and turned into a busybox applet by Mike Frysinger
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ */
++
++#include <stdio.h>
++#include <sys/types.h>
++#include <sys/stat.h>
++#include <fcntl.h>
++#include <unistd.h>
++#include <getopt.h>
++
++#include "libbb.h"
++
++/* 1 if any of the files read were the standard input */
++static int have_read_stdin;
++
++/* make a little more readable and avoid using strcmp for just 2 bytes */
++#define IS_STDIN(s) (s[0] == '-' && s[1] == '\0')
++
++/* Calculate and print the rotated checksum and the size in 1K blocks
++ of file FILE, or of the standard input if FILE is "-".
++ If PRINT_NAME is >1, print FILE next to the checksum and size.
++ The checksum varies depending on sizeof (int).
++ Return 1 if successful. */
++static int bsd_sum_file(const char *file, int print_name)
++{
++ register FILE *fp;
++ register int checksum = 0; /* The checksum mod 2^16. */
++ register uintmax_t total_bytes = 0; /* The number of bytes. */
++ register int ch; /* Each character read. */
++
++ if (IS_STDIN(file)) {
++ fp = stdin;
++ have_read_stdin = 1;
++ } else {
++ fp = bb_wfopen(file, "r");
++ if (fp == NULL)
++ return 0;
++ }
++
++ while ((ch = getc(fp)) != EOF) {
++ ++total_bytes;
++ checksum = (checksum >> 1) + ((checksum & 1) << 15);
++ checksum += ch;
++ checksum &= 0xffff; /* Keep it within bounds. */
++ }
++
++ if (ferror(fp)) {
++ bb_perror_msg(file);
++ bb_fclose_nonstdin(fp);
++ return 0;
++ }
++
++ if (bb_fclose_nonstdin(fp) == EOF) {
++ bb_perror_msg(file);
++ return 0;
++ }
++
++ printf("%05d %5s ", checksum,
++ make_human_readable_str(total_bytes, 1, 1024));
++ if (print_name > 1)
++ puts(file);
++ else
++ printf("\n");
++
++ return 1;
++}
++
++/* Calculate and print the checksum and the size in 512-byte blocks
++ of file FILE, or of the standard input if FILE is "-".
++ If PRINT_NAME is >0, print FILE next to the checksum and size.
++ Return 1 if successful. */
++static int sysv_sum_file(const char *file, int print_name)
++{
++ int fd;
++ unsigned char buf[8192];
++ uintmax_t total_bytes = 0;
++ int r;
++ int checksum;
++
++ /* The sum of all the input bytes, modulo (UINT_MAX + 1). */
++ unsigned int s = 0;
++
++ if (IS_STDIN(file)) {
++ fd = 0;
++ have_read_stdin = 1;
++ } else {
++ fd = open(file, O_RDONLY);
++ if (fd == -1) {
++ bb_perror_msg(file);
++ return 0;
++ }
++ }
++
++ while (1) {
++ size_t i;
++ size_t bytes_read = safe_read(fd, buf, sizeof(buf));
++
++ if (bytes_read == 0)
++ break;
++
++ if (bytes_read == -1) {
++ bb_perror_msg(file);
++ if (!IS_STDIN(file))
++ close(fd);
++ return 0;
++ }
++
++ for (i = 0; i < bytes_read; i++)
++ s += buf[i];
++ total_bytes += bytes_read;
++ }
++
++ if (!IS_STDIN(file) && close(fd) == -1) {
++ bb_perror_msg(file);
++ return 0;
++ }
++
++ r = (s & 0xffff) + ((s & 0xffffffff) >> 16);
++ checksum = (r & 0xffff) + (r >> 16);
++
++ printf("%d %s ", checksum,
++ make_human_readable_str(total_bytes, 1, 512));
++ if (print_name)
++ puts(file);
++ else
++ printf("\n");
++
++ return 1;
++}
++
++int sum_main(int argc, char **argv)
++{
++ int flags;
++ int ok;
++ int files_given;
++ int (*sum_func)(const char *, int) = bsd_sum_file;
++
++ /* give the bsd func priority over sysv func */
++ flags = bb_getopt_ulflags(argc, argv, "sr");
++ if (flags & 1)
++ sum_func = sysv_sum_file;
++ if (flags & 2)
++ sum_func = bsd_sum_file;
++
++ have_read_stdin = 0;
++ files_given = argc - optind;
++ if (files_given <= 0)
++ ok = sum_func("-", files_given);
++ else
++ for (ok = 1; optind < argc; optind++)
++ ok &= sum_func(argv[optind], files_given);
++
++ if (have_read_stdin && fclose(stdin) == EOF)
++ bb_perror_msg_and_die("-");
++
++ exit(ok ? EXIT_SUCCESS : EXIT_FAILURE);
++}
+diff -Nur busybox-1.00/coreutils/test.c busybox/coreutils/test.c
+--- busybox-1.00/coreutils/test.c 2004-08-11 04:45:47.000000000 +0200
++++ busybox/coreutils/test.c 2005-06-04 08:20:13.000000000 +0200
+@@ -51,7 +51,7 @@
+ 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"|
++ binary-operator ::= "="|"=="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
+ "-nt"|"-ot"|"-ef";
+ operand ::= <any legal UNIX file name>
+ */
+@@ -135,6 +135,7 @@
+ "-L", FILSYM, UNOP}, {
+ "-S", FILSOCK, UNOP}, {
+ "=", STREQ, BINOP}, {
++ "==", STREQ, BINOP}, {
+ "!=", STRNE, BINOP}, {
+ "<", STRLT, BINOP}, {
+ ">", STRGT, BINOP}, {
+@@ -191,6 +192,11 @@
+ bb_error_msg_and_die("missing ]");
+ argv[argc] = NULL;
+ }
++ if (strcmp(bb_applet_name, "[[") == 0) {
++ if (strcmp(argv[--argc], "]]"))
++ bb_error_msg_and_die("missing ]]");
++ argv[argc] = NULL;
++ }
+ /* Implement special cases from POSIX.2, section 4.62.4 */
+ switch (argc) {
+ case 1:
+@@ -304,7 +310,7 @@
+ return strlen(*t_wp) > 0;
+ }
+
+-static int binop()
++static int binop(void)
+ {
+ const char *opnd1, *opnd2;
+ struct t_op const *op;
+@@ -531,7 +537,7 @@
+ return (-1);
+ }
+
+-static void initialize_group_array()
++static void initialize_group_array(void)
+ {
+ ngroups = getgroups(0, NULL);
+ group_array = xrealloc(group_array, ngroups * sizeof(gid_t));
+diff -Nur busybox-1.00/coreutils/watch.c busybox/coreutils/watch.c
+--- busybox-1.00/coreutils/watch.c 2003-03-19 10:11:34.000000000 +0100
++++ busybox/coreutils/watch.c 2005-06-04 08:20:13.000000000 +0200
+@@ -100,11 +100,10 @@
+ //child
+ close(1);
+ dup(old_stdout);
+- if (execvp(*watched_argv, watched_argv)) {
+- bb_error_msg_and_die("Couldn't run command\n");
+- }
++ execvp(*watched_argv, watched_argv);
++ bb_perror_msg_and_die(*watched_argv);
+ } else {
+- bb_error_msg_and_die("Couldn't vfork\n");
++ bb_perror_msg_and_die("vfork");
+ }
+ }
+ }
+diff -Nur busybox-1.00/coreutils/who.c busybox/coreutils/who.c
+--- busybox-1.00/coreutils/who.c 2004-03-15 09:28:22.000000000 +0100
++++ busybox/coreutils/who.c 2005-06-04 08:20:13.000000000 +0200
+@@ -74,7 +74,7 @@
+ } else
+ printf("%-8s ", "?");
+
+- printf("%-12.12s %s\n", ctime(&(ut->ut_tv.tv_sec)) + 4, ut->ut_host);
++ printf("%-12.12s %s\n", ctime((time_t*)&(ut->ut_tv.tv_sec)) + 4, ut->ut_host);
+ }
+ }
+ endutent();
+diff -Nur busybox-1.00/debianutils/Config.in busybox/debianutils/Config.in
+--- busybox-1.00/debianutils/Config.in 2004-03-15 09:28:24.000000000 +0100
++++ busybox/debianutils/Config.in 2005-06-04 08:20:10.000000000 +0200
+@@ -24,6 +24,13 @@
+ This program reads a symbolic link and returns the name
+ of the file it points to
+
++config CONFIG_FEATURE_READLINK_FOLLOW
++ bool " Enable canonicalization by following all symlinks (-f)"
++ default n
++ depends on CONFIG_READLINK
++ help
++ Enable the readlink option (-f).
++
+ config CONFIG_RUN_PARTS
+ bool "run-parts"
+ default n
+diff -Nur busybox-1.00/debianutils/readlink.c busybox/debianutils/readlink.c
+--- busybox-1.00/debianutils/readlink.c 2003-03-19 10:11:41.000000000 +0100
++++ busybox/debianutils/readlink.c 2005-06-04 08:20:10.000000000 +0200
+@@ -23,18 +23,38 @@
+ #include <errno.h>
+ #include <unistd.h>
+ #include <stdlib.h>
++#include <getopt.h>
+ #include "busybox.h"
+
++#ifdef CONFIG_FEATURE_READLINK_FOLLOW
++# define READLINK_FOLLOW "f"
++# define READLINK_FLAG_f (1 << 0)
++#else
++# define READLINK_FOLLOW ""
++#endif
++
++static const char readlink_options[] = READLINK_FOLLOW;
++
+ int readlink_main(int argc, char **argv)
+ {
+ char *buf = NULL;
++ unsigned long opt = bb_getopt_ulflags(argc, argv, readlink_options);
++#ifdef CONFIG_FEATURE_READLINK_FOLLOW
++ RESERVE_CONFIG_BUFFER(resolved_path, PATH_MAX);
++#endif
+
+ /* no options, no getopt */
+
+- if (argc != 2)
++ if (optind + 1 != argc)
+ bb_show_usage();
+
+- buf = xreadlink(argv[1]);
++#ifdef CONFIG_FEATURE_READLINK_FOLLOW
++ if (opt & READLINK_FLAG_f) {
++ buf = realpath(argv[optind], resolved_path);
++ } else
++#endif
++ buf = xreadlink(argv[optind]);
++
+ if (!buf)
+ return EXIT_FAILURE;
+ puts(buf);
+diff -Nur busybox-1.00/debianutils/start_stop_daemon.c busybox/debianutils/start_stop_daemon.c
+--- busybox-1.00/debianutils/start_stop_daemon.c 2004-04-13 20:28:46.000000000 +0200
++++ busybox/debianutils/start_stop_daemon.c 2005-06-04 08:20:10.000000000 +0200
+@@ -238,7 +238,7 @@
+ &startas, &cmdname, &signame, &userspec, &execname, &pidfile);
+
+ /* Check one and only one context option was given */
+- if ((opt & 0x80000000UL) || (opt & (SSD_CTX_STOP | SSD_CTX_START)) == 0) {
++ if ((opt & BB_GETOPT_ERROR) || (opt & (SSD_CTX_STOP | SSD_CTX_START)) == 0) {
+ bb_show_usage();
+ }
+
+diff -Nur busybox-1.00/docs/autodocifier.pl busybox/docs/autodocifier.pl
+--- busybox-1.00/docs/autodocifier.pl 2004-04-06 17:26:25.000000000 +0200
++++ busybox/docs/autodocifier.pl 2005-06-04 08:20:06.000000000 +0200
+@@ -271,4 +271,4 @@
+
+ =cut
+
+-# $Id$
++# $Id$
+diff -Nur busybox-1.00/docs/busybox.net/FAQ.html busybox/docs/busybox.net/FAQ.html
+--- busybox-1.00/docs/busybox.net/FAQ.html 2004-10-13 11:42:10.000000000 +0200
++++ busybox/docs/busybox.net/FAQ.html 2005-06-04 08:20:06.000000000 +0200
+@@ -15,14 +15,15 @@
+ <li><a href="#bugs">I think I found a bug in BusyBox! What should I do?!</a>
+ <li><a href="#job_control">Why do I keep getting "sh: can't access tty; job control
+ turned off" errors? Why doesn't Control-C work within my shell?</a>
++<li><a href="#getting_started">How can I get started using BusyBox?</a>
+ <li><a href="#demanding">I demand that you to add <favorite feature> right now! How come
+ you don't answer all my questions on the mailing list instantly? I demand
+ that you help me with all of my problems <em>Right Now</em>!</a>
+-<li><a href="#getting_started">How can I get started using BusyBox?</a>
+ <li><a href="#helpme">I need help with BusyBox! What should I do?</a>
+ <li><a href="#contracts">I need you to add <favorite feature>! Are the BusyBox developers willing to
+ be paid in order to fix bugs or add in <favorite feature>? Are you willing to provide
+ support contracts?</a>
++<li><a href="#external">Where can I find other small utilities since busybox does not include the features I want?</a></li>
+ <li><a href="#support">I think you guys are great and I want to help support your work!</a>
+
+
+@@ -76,6 +77,7 @@
+ with the generous terms of the GPL BusyBox license</a> you can ship BusyBox
+ as part of the software on your device.
+
++ <br>
+ <a href="#support">Please consider sharing some of the money you make.</a>
+
+
+@@ -84,49 +86,33 @@
+ <h2><a name="bugs">I think I found a bug in BusyBox! What should I do?</h2>
+ <p>
+
+- If you find a problem with BusyBox, please submit a detailed bug report to
+- the BusyBox mailing list at <a href="mailto:busybox@mail.busybox.net">
+- busybox@mail.busybox.net</a>. Please do not send private email to Erik
+- (the maintainer of BusyBox) asking for private help unless you are planning
+- on paying for consulting services. When we answer questions on the BusyBox
+- mailing list, it helps everyone, while private answers help only you...
+-
+- <p>
+-
+- If you find bugs, please submit a detailed bug report to the BusyBox mailing
+- list at busybox@mail.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:
+-
+-<pre>
+- To: busybox@mail.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:
++<p>
+
+- $ date
+- Fri Oct 8 14:19:41 MDT 2004
++ If you simply need help with using or configuring BusyBox, please submit a
++ detailed description of your problem to the BusyBox mailing list at <a
++ href="mailto:busybox@mail.busybox.net"> busybox@mail.busybox.net</a>.
++ Please do not send private email to Erik (the maintainer of BusyBox) asking
++ for private help unless you are planning on paying for consulting services.
++ When we answer questions on the BusyBox mailing list, it helps everyone,
++ while private answers help only you...
+
+- But when I use BusyBox date I get this instead:
++ <p>
+
+- $ date
+- illegal instruction
++ The developers of BusyBox are busy people, and have only so much they can
++ keep in their brains at a time. As a result, bug reports sometimes get
++ lost when posted to the mailing list. To prevent your bug report from
++ getting lost, if you find a bug in BusyBox, please use the <a
++ href="http://bugs.busybox.net/">BusyBox Bug and Patch Tracking System</a>
++ to submit a detailed bug report.
+
+- I am using Debian unstable, kernel version 2.4.27 on a x86 system,
+- and the latest uClibc from CVS. Thanks for the wonderful program!
++ <p>
+
+- -Diligent
+-</pre>
++ The same also applies to patches... Regardless of whether your patch is a
++ bug fix or adds shiney new features, please post your patch to the <a
++ href="http://bugs.busybox.net/">BusyBox Bug and Patch Tracking System</a>
++ to make certain it is properly considered.
+
+- 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 proper detail may never be fixed... Thanks for understanding.
+
+ <hr />
+ <p>
+@@ -153,8 +139,8 @@
+ An easy method to build your own basic BusyBox based system, is to
+ follow these simple steps:
+ <ul>
+- <li> Point your web browser <a href="/cgi-bin/cvsweb/buildroot/">here</a>
+- <li> Click on "Download tarball"
++ <li> Point your web browser <a href="http://buildroot.uclibc.org/">here</a>
++ <li> Download a copy of buildroot
+ <li> Unpack the tarball on your Linux system somewhere
+ <li> run 'make' and configure things to taste.
+ <li> run 'unset CC'. Some Linux systems (i.e. Gentoo) set 'CC'
+@@ -220,19 +206,25 @@
+ href="http://codepoet-consulting.com/">CodePoet Consulting</a> to bid
+ on your project. If Erik is too busy to personally add your feature, there
+ are many other active BusyBox contributors who will almost certainly be able
+- to help you out. Erik can contact them privatly, and may even let you to
++ to help you out. Erik can contact them privately, and may even let you to
+ post your request for services on the mailing list.
+
+
+ <hr />
+ <p>
++<h2><a name="external">Where can I find other small utilities since busybox
++ does not include the features I want?</a></h2>
++<p>
++ We maintain such a <a href="tinyutils.html">list</a> on this site!
++
++
++<hr />
++<p>
+ <h2><a name="support">I think you guys are great and I want to help support your work!</a></h2>
+ <p>
+
+- Wow, that would be great! Erik personally pays for all the bandwidth, and
+- all servers used for busybox.net out of his own pocket. If you would like
+- to make a donation to help support BusyBox, and/or request features, you
+- can click here:
++ Wow, that would be great! If you would like to make a donation to help
++ support BusyBox, and/or request features, you can click here:
+
+ <!-- Begin PayPal Logo -->
+ <center>
+@@ -321,4 +313,3 @@
+ <br>
+
+ <!--#include file="footer.html" -->
+-
+diff -Nur busybox-1.00/docs/busybox.net/cvs_anon.html busybox/docs/busybox.net/cvs_anon.html
+--- busybox-1.00/docs/busybox.net/cvs_anon.html 2004-03-15 09:28:29.000000000 +0100
++++ busybox/docs/busybox.net/cvs_anon.html 1970-01-01 01:00:00.000000000 +0100
+@@ -1,57 +0,0 @@
+-<!--#include file="header.html" -->
+-
+-
+-<h3>Anonymous CVS</h3>
+-
+-We allow anonymous (read-only) CVS access to everyone. The first command you
+-need to run for anonymous CVS access is:
+-<pre>
+-cvs -d:pserver:anonymous@busybox.net:/var/cvs login</pre>
+-<p>
+-CVS will prompt you for a password. Just press the Enter key (there is no
+-password for anonymous access). This step only needs to be done once, the first
+-time you attempt to access CVS.
+-<p>
+-Once the login is complete, you can then check the list of available
+-CVS modules by running the following command (all on one line):
+-<pre>
+-cvs -z3 -d:pserver:anonymous@busybox.net:/var/cvs co -c </pre>
+-
+-<p>
+-If you wish, you can then check out a local copy of any of the
+-available modules. The following is an example of how to grab
+-a copy of busybox and tinylogin:
+-<pre>
+- cvs -z3 -d:pserver:anonymous@busybox.net:/var/cvs co -P busybox tinylogin</pre>
+-This will create a directory called <b>busybox</b> and a directory called
+-<b>tinylogin</b> in the current directory. These directories contain the
+-latest and greatest source code for busybox and tinylogin.
+-
+-<p>
+-If you are not already familiar with using CVS, I recommend you visit
+-this quick <a href="/cvs_howto.html">Introduction to CVS</a>.
+-
+-<p>
+-I usually create a ~/.cvsrc file with the following things in it, and I
+-recommend you should use the same:
+-<pre>
+- -z3
+- update -dP
+- rdiff -u
+- diff -ubBwpN
+- checkout -P</pre>
+-
+-<p>
+-Once you've checked out a copy of the source tree, you can update your
+-source tree at any time so it is in sync with the latest and greatest by
+-running the command:
+-<pre>
+-cvs update</pre>
+-
+-Because you've only been granted anonymous access to the tree, you won't be
+-able to commit any changes. Changes can be submitted for inclusion by posting
+-them to the appropriate mailing list. For those that are actively contributing
+-<a href="cvs_write.html">CVS write access</a> can be made available.
+-
+-<!--#include file="footer.html" -->
+-
+diff -Nur busybox-1.00/docs/busybox.net/cvs_howto.html busybox/docs/busybox.net/cvs_howto.html
+--- busybox-1.00/docs/busybox.net/cvs_howto.html 2004-03-15 09:28:29.000000000 +0100
++++ busybox/docs/busybox.net/cvs_howto.html 1970-01-01 01:00:00.000000000 +0100
+@@ -1,44 +0,0 @@
+-<!--#include file="header.html" -->
+-
+-
+-<h3>How to use CVS</h3>
+-
+-
+-If you want to know all the gory details, you will want to visit
+-<a href="http://www.cvshome.org/">the CVS main web page</a>.<p>
+-For the impatient, the following is probably about all you need to know:
+-<p>
+-
+-<dl>
+- <dt><pre>cvs checkout -c</pre>
+- <dd>Will list the modules available for checkout
+- <dt><pre>cvs checkout < module name ></pre>
+- <dd>Will checkout the named module
+- <dt><pre>cvs co < module name ></pre>
+- <dd>Same thing
+- <dt><pre>cvs update</pre>
+-
+- <dd>Updates your local archive so it is in sync with the repository
+- -- your local updates are left intact. Tries to merge upstream updates
+- into your local updates. You will see the following tags when it is
+- updating your local repository: C means conflict, U means update,
+- P means patched, and M means modified.
+- <dt><pre>cvs up</pre>
+- <dd>Same thing
+- <dt><pre>cvs update < file name ></pre>
+- <dd>Same thing but for just the named file(s)/directory(s).
+- <dt><pre>cvs commit</pre>
+- <dd>Will check in all your work.
+- <dt><pre>cvs add < file name ></pre>
+-
+- <dd>Adds the named file/directory into CVS
+- <dt><pre>cvs remove < file name ></pre>
+- <dd>Removes the named file/directory from the upstream repository.
+- <dt><pre>cvs rm < file name ></pre>
+- <dd>Same thing
+- <dt><pre>cvs log < file name ></pre>
+-</dl>
+-
+-
+-<!--#include file="footer.html" -->
+-
+diff -Nur busybox-1.00/docs/busybox.net/cvs_write.html busybox/docs/busybox.net/cvs_write.html
+--- busybox-1.00/docs/busybox.net/cvs_write.html 2004-09-08 22:13:05.000000000 +0200
++++ busybox/docs/busybox.net/cvs_write.html 1970-01-01 01:00:00.000000000 +0100
+@@ -1,32 +0,0 @@
+-<!--#include file="header.html" -->
+-
+-
+-<h3>CVS Read/Write Access</h3>
+-
+-If you want to be able to commit things to CVS, first contribute some
+-stuff to show you are serious. Then, very nicely ask
+-<a href="mailto:andersen@codepoet.org">Erik Andersen</a> if he will set you up with
+-an account. To access CVS, you will want to add the following to set up your environment:
+-<pre>
+-$ export CVS_RSH=/usr/bin/ssh
+-$ export CVSROOT='username@cvs.busybox.net:/var/cvs'</pre>
+-<br>
+-It goes without saying you must change <em>username</em> to your own
+-username...
+-<p>
+-
+-To obtain commit access, you will need to demonstrate you are
+-serious by submitting a few good patches first. Then, you will need to
+-select a user-name to use when committing stuff, and finally, you will
+-need to send me the username you have selected, an ssh key, and the email
+-address where you prefer email to be sent (I will forward any email sent
+-to you, but not store it).
+-
+-<p>
+-Note that if you would prefer to keep your communications with me
+-private, you can encrypt your email using my
+-<a href="http://www.codepoet.org/andersen/erik/gpg.asc">public key</a>.
+-
+-<!--#include file="footer.html" -->
+-
+-
+diff -Nur busybox-1.00/docs/busybox.net/developer.html busybox/docs/busybox.net/developer.html
+--- busybox-1.00/docs/busybox.net/developer.html 1970-01-01 01:00:00.000000000 +0100
++++ busybox/docs/busybox.net/developer.html 2005-06-04 08:20:06.000000000 +0200
+@@ -0,0 +1,58 @@
++<!--#include file="header.html" -->
++
++
++<h3>Subversion Read/Write Access</h3>
++
++If you want to be able to commit things to Subversion, first contribute some
++stuff to show you are serious. Then, very nicely ask <a
++href="mailto:andersen@codepoet.org">Erik Andersen</a> if he will set you up
++with an commit access to the Subversion repository. To access Subversion, you
++will want to add the following to set up your environment:
++
++<p>
++
++To obtain commit access, you will need to demonstrate you are serious by
++submitting a few good patches first. Then, you will need to select a username
++to use when committing stuff, and finally, you will need to send me the
++username you have selected, an ssh key, and the email address where you prefer
++email to be sent (I will forward any email sent to you, but not store it).
++
++<p>
++
++Note that if you would prefer to keep your communications with me
++private, you can encrypt your email using my
++<a href="http://www.codepoet.org/andersen/erik/gpg.asc">public key</a>.
++
++<p>
++
++Once you are setup with an account, you will need to use your account to
++checkout a copy of BusyBox from Subversion:
++
++<pre>
++svn list svn+ssh://username@svn.uclibc.org/svn/trunk/busybox</pre>
++<br>
++It goes without saying you must change <em>username</em> to your own
++username...
++<p>
++
++You can then enter the newly checked out BusyBox directory, make changes, check
++your changes, diff your changes, revert your changes, and and commit your
++changes usine commands such as:
++
++<pre>
++svn diff
++svn status
++svn revert
++svn commit</pre>
++
++<p>
++
++For additional detail on how to use Subversion, please visit the
++<a href="http://subversion.tigris.org/">the Subversion website</a>.
++You might also want to read online or buy a copy of <a
++href="http://svnbook.red-bean.com/">the Subversion Book</a>...
++
++
++<!--#include file="footer.html" -->
++
++
+diff -Nur busybox-1.00/docs/busybox.net/download.html busybox/docs/busybox.net/download.html
+--- busybox-1.00/docs/busybox.net/download.html 2004-03-15 09:28:29.000000000 +0100
++++ busybox/docs/busybox.net/download.html 2005-06-04 08:20:06.000000000 +0200
+@@ -9,27 +9,18 @@
+
+ <p>
+ You can also obtain <a href= "downloads/snapshots/">Daily Snapshots</a> of
+-the latest stable, and the latest development CVS source trees.
+-
+-<p>
+-BusyBox now has <b>two</b> CVS trees. The "busybox-stable" tree
+-contains the older 0.60.x stable series. The "busybox" tree contains
+-the latest 1.0.0-preX development version of busybox.<br>
++the latest development source tree for those wishing to follow BusyBox development,
++but cannot or do not wish to use Subversion (svn).
+
+ <ul>
+- <li> Click here to browse the <a href="/cgi-bin/cvsweb/busybox/">
+- CVS tree for the 1.0.0-preX development version of BusyBox</a>
+- </li>
+-
+- <li>Click here to browse the <a href="/cgi-bin/cvsweb/busybox.stable/">
+- CVS tree for the stable 0.60.x version of BusyBox</a>.
++ <li> Click here to <a href="/cgi-bin/viewcvs.cgi/trunk/busybox/">browse the source tree</a>.
+ </li>
+
+- <li>Anonymous <a href="cvs_anon.html">CVS access</a> is available.
++ <li>Anonymous <a href="subversion.html">Subversion access</a> is available.
+ </li>
+
+- <li>For those that are actively contributing there is
+- even <a href="cvs_write.html">CVS write access</a>.
++ <li>For those that are actively contributing obtaining
++ <a href="developer.html">Subversion read/write access</a> is also possible.
+ </li>
+
+ </ul>
+diff -Nur busybox-1.00/docs/busybox.net/footer.html busybox/docs/busybox.net/footer.html
+--- busybox-1.00/docs/busybox.net/footer.html 2004-03-15 09:28:29.000000000 +0100
++++ busybox/docs/busybox.net/footer.html 2005-06-04 08:20:06.000000000 +0200
+@@ -7,14 +7,34 @@
+
+ <hr />
+
+- <p>
+- <font face="arial, helvetica, sans-serif" size="-1">
+- <a HREF="/copyright.txt">Copyright © 1999-2003 Erik Andersen</a>
+- <br>
+- Mail all comments, insults, suggestions and bribes to
+- <br>
+- Erik Andersen <A HREF="mailto:andersen@codepoet.org">andersen@codepoet.org</A><BR>
+- </font>
++
++ <table width="100%">
++ <tr>
++ <td width="60%">
++ <font face="arial, helvetica, sans-serif" size="-1">
++ <a href="/copyright.txt">Copyright © 1999-2005 Erik Andersen</a>
++ <br>
++ Mail all comments, insults, suggestions and bribes to
++ <br>
++ Erik Andersen <a href="mailto:andersen@codepoet.org">andersen@codepoet.org</a><br>
++ </font>
++ </td>
++
++ <td>
++ <a href="http://www.vim.org/"><img border=0 width=88 height=31
++ src="images/written.in.vi.png"
++ alt="This site created with the vi editor"></a>
++ </td>
++
++ <td>
++ <a href="http://osuosl.org/"><img border=0 width=114 height=63
++ src="images/osuosl.png"
++ alt="This site is kindly hosted by OSL"></a>
++ </td>
++
++ </TR>
++ </table>
+
+ </body>
+ </html>
++
+diff -Nur busybox-1.00/docs/busybox.net/header.html busybox/docs/busybox.net/header.html
+--- busybox-1.00/docs/busybox.net/header.html 2004-10-08 12:50:08.000000000 +0200
++++ busybox/docs/busybox.net/header.html 2005-06-04 08:20:06.000000000 +0200
+@@ -48,9 +48,10 @@
+ <br><a href="/lists.html">Mailing Lists</a>
+ <br><a href="/news.html">Latest News</a>
+ <br><a href="/download.html">Download</a>
++ <br><a href="/cgi-bin/viewcvs.cgi/trunk/busybox/">Browse Source</a>
++ <br><a href="/subversion.html">Accessing Source</a>
++ <br><a href="http://bugs.busybox.net/">Bug Tracking</a>
+ <br><a href="/FAQ.html">FAQ</a>
+- <br><a href="/cvs_anon.html">Accessing CVS</a>
+- <br><a href="/cgi-bin/cvsweb/busybox/">Browse CVS</a>
+ <br><a href="/docs.html">Documentation</a>
+ <br><a href="/products.html">Products</a>
+ <br><a href="/shame.html">Hall of Shame</a>
+@@ -58,8 +59,11 @@
+
+ <p><b>Related Sites</b>
+ <br><a href="http://uclibc.org/">uClibc.org</a>
++ <br><a href="http://cxx.uclibc.org/">uClibc++</a>
+ <br><a href="http://udhcp.busybox.net/">udhcp</a>
+- <br><a href="http://tinylogin.busybox.net/">tinylogin</a>
++ <br><a href="http://buildroot.uclibc.org/">buildroot</a>
++ <br><a href="http://www.scratchbox.org/">Scratchbox</a>
++ <br><a href="http://openembedded.org/">OpenEmbedded</a>
+ <br><a href="http://www.ucdot.org/">uCdot</a>
+ <br><a href="http://www.linuxdevices.com">LinuxDevices</a>
+ <br><a href="http://slashdot.org/">Slashdot</a>
+Files busybox-1.00/docs/busybox.net/images/osuosl.png and busybox/docs/busybox.net/images/osuosl.png differ
+diff -Nur busybox-1.00/docs/busybox.net/lists.html busybox/docs/busybox.net/lists.html
+--- busybox-1.00/docs/busybox.net/lists.html 2004-06-19 08:26:30.000000000 +0200
++++ busybox/docs/busybox.net/lists.html 2005-06-04 08:20:06.000000000 +0200
+@@ -15,7 +15,8 @@
+ wishing to read the complete diff of each and every change to busybox -- not for the
+ faint of heart. Active developers can subscribe by visiting
+ <a href="http://codepoet.org/mailman/listinfo/busybox-cvs">this page</a>.
+-The CVS server is the only one permtted to post to this list.
++The Subversion server is the only one permtted to post to this list. And yes,
++this list name uses the word 'cvs' even though we don't use that anymore...
+
+ <p>
+
+diff -Nur busybox-1.00/docs/busybox.net/news.html busybox/docs/busybox.net/news.html
+--- busybox-1.00/docs/busybox.net/news.html 2004-10-13 11:42:10.000000000 +0200
++++ busybox/docs/busybox.net/news.html 2005-06-04 08:20:06.000000000 +0200
+@@ -3,6 +3,25 @@
+
+ <ul>
+
++ <li><b>13 January 2005 -- Bug and Patch Tracking</b><p>
++
++ Bug reports sometimes get lost when posted to the mailing list. The
++ developers of BusyBox are busy people, and have only so much they can keep
++ in their brains at a time. In my case, I'm lucky if I can remember my own
++ name, much less a bug report posted last week... To prevent your bug report
++ from getting lost, if you find a bug in BusyBox, please use the
++ <a href="http://bugs.busybox.net/">shiny new Bug and Patch Tracking System</a>
++ to post all the gory details.
++
++ <p>
++
++ The same applies to patches... Regardless of whether your patch
++ is a bug fix or adds spiffy new features, please post your patch
++ to the Bug and Patch Tracking System to make certain it is
++ properly considered.
++
++
++ <p>
+ <li><b>13 October 2004 -- BusyBox 1.00 released</b><p>
+
+ When you take a careful look at nearly every embedded Linux device or
+diff -Nur busybox-1.00/docs/busybox.net/oldnews.html busybox/docs/busybox.net/oldnews.html
+--- busybox-1.00/docs/busybox.net/oldnews.html 2004-10-13 11:42:10.000000000 +0200
++++ busybox/docs/busybox.net/oldnews.html 2005-06-04 08:20:06.000000000 +0200
+@@ -888,7 +888,7 @@
+ Also, some exciting infrastructure news! Busybox now has its own
+ <a href="lists/busybox/">mailing list</a>,
+ publically browsable
+- <a href="/cgi-bin/cvsweb/busybox/">CVS tree</a>,
++ <a href="/cgi-bin/viewcvs.cgi/trunk/busybox/">CVS tree</a>,
+ anonymous
+ <a href="cvs_anon.html">CVS access</a>, and
+ for those that are actively contributing there is even
+diff -Nur busybox-1.00/docs/busybox.net/products.html busybox/docs/busybox.net/products.html
+--- busybox-1.00/docs/busybox.net/products.html 2004-10-13 11:42:10.000000000 +0200
++++ busybox/docs/busybox.net/products.html 2005-06-04 08:20:06.000000000 +0200
+@@ -13,7 +13,7 @@
+ <ul>
+
+
+-<li><a href="/cgi-bin/cvsweb/buildroot/">buildroot</a><br>A configurable
++<li><a href="http://buildroot.uclibc.org/">buildroot</a><br>A configurable
+ means for building your own busybox/uClibc based system systems.
+
+ <li><a href="http://www.pengutronix.de/software/ptxdist_en.html">PTXdist</a><br>another
+diff -Nur busybox-1.00/docs/busybox.net/subversion.html busybox/docs/busybox.net/subversion.html
+--- busybox-1.00/docs/busybox.net/subversion.html 1970-01-01 01:00:00.000000000 +0100
++++ busybox/docs/busybox.net/subversion.html 2005-06-04 08:20:06.000000000 +0200
+@@ -0,0 +1,38 @@
++<!--#include file="header.html" -->
++
++
++<h3>Anonymous Subversion Access</h3>
++
++We allow anonymous (read-only) Subversion (svn) access to everyone. To
++grab a copy of the latest version of BusyBox using anonymous svn access is:
++
++<pre>
++svn co svn://busybox.net/trunk/busybox</pre>
++
++
++<p>
++
++If you are not already familiar with using Subversion, I recommend you visit <a
++href="http://subversion.tigris.org/">the Subversion website</a>. You might
++also want to read online or buy a copy of <a
++href="http://svnbook.red-bean.com/">the Subversion Book</a>. If you are
++already comfortable with using CVS, you may want to skip ahead to the <a
++href="http://svnbook.red-bean.com/en/1.1/apa.html">Subversion for CVS Users</a>
++part of the Subversion Book.
++
++<p>
++
++Once you've checked out a copy of the source tree, you can update your source
++tree at any time so it is in sync with the latest and greatest by entering your
++BusyBox directory and running the command:
++
++<pre>
++svn update</pre>
++
++Because you've only been granted anonymous access to the tree, you won't be
++able to commit any changes. Changes can be submitted for inclusion by posting
++them to the BusyBox mailing list. For those that are actively contributing
++<a href="developer.html">Subversion commit access</a> can be made available.
++
++<!--#include file="footer.html" -->
++
+diff -Nur busybox-1.00/docs/busybox.net/tinyutils.html busybox/docs/busybox.net/tinyutils.html
+--- busybox-1.00/docs/busybox.net/tinyutils.html 1970-01-01 01:00:00.000000000 +0100
++++ busybox/docs/busybox.net/tinyutils.html 2005-06-04 08:20:06.000000000 +0200
+@@ -0,0 +1,35 @@
++<!--#include file="header.html" -->
++
++
++<h3>External Tiny Utilities</h3>
++
++This is a list of tiny utilities whose functionality is not provided by
++busybox. If you have additional suggestions, please send an e-mail to our
++dev mailing list.
++
++<br><br>
++
++<table>
++<tr>
++ <th>Feature</th>
++ <th>Utilities</th>
++</tr>
++
++<tr>
++ <td>SSH</td>
++ <td><a href="http://matt.ucc.asn.au/dropbear/">Dropbear</a> has both a sshd and a ssh client.</td>
++</tr>
++
++<tr>
++ <td>SMTP</td>
++ <td><a href="ftp://ftp.debian.org/debian/pool/main/s/ssmtp/">ssmtp</a> is an extremely simple MTA.</td>
++</tr>
++
++<tr>
++ <td>DNS</td>
++ <td><a href="http://www.thekelleys.org.uk/dnsmasq/">dnsmasq</a> is a small forwarding DNS server meant for small environments.</td>
++</tr>
++</table>
++
++<!--#include file="footer.html" -->
++
+diff -Nur busybox-1.00/docs/busybox_footer.pod busybox/docs/busybox_footer.pod
+--- busybox-1.00/docs/busybox_footer.pod 2004-04-25 08:05:14.000000000 +0200
++++ busybox/docs/busybox_footer.pod 2005-06-04 08:20:06.000000000 +0200
+@@ -254,5 +254,5 @@
+
+ =cut
+
+-# $Id$
++# $Id$
+
+diff -Nur busybox-1.00/docs/new-applet-HOWTO.txt busybox/docs/new-applet-HOWTO.txt
+--- busybox-1.00/docs/new-applet-HOWTO.txt 2004-03-15 09:28:26.000000000 +0100
++++ busybox/docs/new-applet-HOWTO.txt 2005-06-04 08:20:06.000000000 +0200
+@@ -52,10 +52,10 @@
+ char mu;
+
+ if ((fd = open("/dev/random", O_RDONLY)) < 0)
+- perror_msg_and_die("/dev/random");
++ bb_perror_msg_and_die("/dev/random");
+
+ if ((n = safe_read(fd, &mu, 1)) < 1)
+- perror_msg_and_die("/dev/random");
++ bb_perror_msg_and_die("/dev/random");
+
+ return mu;
+ }
+@@ -137,11 +137,6 @@
+ /* all programs below here are alphabetically "greater than" 'mu' */
+
+
+-Finally, add a define for your applet to include/config.h
+-
+- #undef CONFIG_MU
+-
+-
+ Documentation
+ -------------
+
+diff -Nur busybox-1.00/e2fsprogs/Config.in busybox/e2fsprogs/Config.in
+--- busybox-1.00/e2fsprogs/Config.in 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/Config.in 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,20 @@
++#
++# For a description of the syntax of this configuration file,
++# see scripts/kbuild/config-language.txt.
++#
++
++menu "Linux Ext2 FS Progs"
++
++config CONFIG_CHATTR
++ bool "chattr"
++ default n
++ help
++ chattr changes the file attributes on a second extended file system.
++
++config CONFIG_LSATTR
++ bool "lsattr"
++ default n
++ help
++ lsattr lists the file attributes on a second extended file system.
++
++endmenu
+diff -Nur busybox-1.00/e2fsprogs/Makefile busybox/e2fsprogs/Makefile
+--- busybox-1.00/e2fsprogs/Makefile 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/Makefile 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,31 @@
++# Makefile for busybox
++#
++# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
++#
++# This program is free software; you can redistribute it and/or modify
++# it under the terms of the GNU General Public License as published by
++# the Free Software Foundation; either version 2 of the License, or
++# (at your option) any later version.
++#
++# This program is distributed in the hope that it will be useful,
++# but WITHOUT ANY WARRANTY; without even the implied warranty of
++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++# General Public License for more details.
++#
++# You should have received a copy of the GNU General Public License
++# along with this program; if not, write to the Free Software
++# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++#
++
++top_srcdir=..
++top_builddir=..
++srcdir=$(top_srcdir)/e2fsprogs
++E2FSPROGS_DIR:=./
++include $(top_builddir)/Rules.mak
++include $(top_builddir)/.config
++include Makefile.in
++all: $(libraries-y)
++-include $(top_builddir)/.depend
++
++clean:
++ rm -f *.o *.a $(AR_TARGET)
+diff -Nur busybox-1.00/e2fsprogs/Makefile.in busybox/e2fsprogs/Makefile.in
+--- busybox-1.00/e2fsprogs/Makefile.in 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/Makefile.in 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,44 @@
++# Makefile for busybox
++#
++# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
++#
++# This program is free software; you can redistribute it and/or modify
++# it under the terms of the GNU General Public License as published by
++# the Free Software Foundation; either version 2 of the License, or
++# (at your option) any later version.
++#
++# This program is distributed in the hope that it will be useful,
++# but WITHOUT ANY WARRANTY; without even the implied warranty of
++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++# General Public License for more details.
++#
++# You should have received a copy of the GNU General Public License
++# along with this program; if not, write to the Free Software
++# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++#
++
++E2FSPROGS_AR:=e2fsprogs.a
++ifndef $(E2FSPROGS_DIR)
++E2FSPROGS_DIR:=$(top_builddir)/e2fsprogs/
++endif
++srcdir=$(top_srcdir)/e2fsprogs
++
++CFLAGS += -I$(E2FSPROGS_DIR)
++
++E2P_SRC:=fgetsetflags.c fgetsetversion.c pf.c iod.c
++E2P_SRCS:=$(patsubst %,e2p/%, $(E2P_SRC))
++E2P_OBJS=$(patsubst %.c,%.o, $(E2P_SRCS))
++
++E2FSPROGS-:=
++E2FSPROGS-$(CONFIG_CHATTR) += chattr.o $(E2P_OBJS)
++E2FSPROGS-$(CONFIG_LSATTR) += lsattr.o $(E2P_OBJS)
++
++libraries-y+=$(E2FSPROGS_DIR)$(E2FSPROGS_AR)
++
++
++$(E2FSPROGS_DIR)$(E2FSPROGS_AR): $(patsubst %,$(E2FSPROGS_DIR)%, $(E2FSPROGS-y))
++ $(AR) -ro $@ $(patsubst %,$(E2FSPROGS_DIR)%, $(E2FSPROGS-y))
++
++$(E2FSPROGS_DIR)%.o: $(srcdir)/%.c
++ $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -DHAVE_ERRNO_H=1 -DHAVE_UNISTD_H=1 \
++ -DHAVE_EXT2_IOCTLS=1 -DHAVE_EXT2_IOCTLS=1 -c -o $@ $<
+diff -Nur busybox-1.00/e2fsprogs/README busybox/e2fsprogs/README
+--- busybox-1.00/e2fsprogs/README 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/README 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,3 @@
++This is a pretty straight rip from the e2fsprogs pkg.
++
++See README's in subdirs for specific info.
+diff -Nur busybox-1.00/e2fsprogs/base_device.c busybox/e2fsprogs/base_device.c
+--- busybox-1.00/e2fsprogs/base_device.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/base_device.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,147 @@
++/*
++ * 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 can not be determined, return NULL.
++ *
++ * The base_device() function returns an allocated string which must
++ * be freed.
++ *
++ * Written by Theodore Ts'o, <tytso@mit.edu>
++ *
++ * Copyright (C) 2000 Theodore Ts'o.
++ *
++ * %Begin-Header%
++ * This file may be redistributed under the terms of the GNU Public
++ * License.
++ * %End-Header%
++ */
++#include <stdio.h>
++#include <unistd.h>
++#include <stdlib.h>
++#include <ctype.h>
++#include <string.h>
++
++#include "busybox.h"
++
++#ifdef CONFIG_FEATURE_DEVFS
++/*
++ * Required for the uber-silly devfs /dev/ide/host1/bus2/target3/lun3
++ * pathames.
++ */
++static const char *devfs_hier[] = {
++ "host", "bus", "target", "lun", 0
++};
++#endif
++
++char *base_device(const char *device)
++{
++ char *str, *cp;
++#ifdef CONFIG_FEATURE_DEVFS
++ const char **hier, *disk;
++ int len;
++#endif
++
++ cp = str = bb_xstrdup(device);
++
++ /* Skip over /dev/; if it's not present, give up. */
++ if (strncmp(cp, "/dev/", 5) != 0)
++ goto errout;
++ cp += 5;
++
++#if 0 /* this is for old stuff no one uses anymore ? */
++ /* Skip over /dev/dsk/... */
++ if (strncmp(cp, "dsk/", 4) == 0)
++ cp += 4;
++#endif
++
++ /*
++ * 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;
++}
+diff -Nur busybox-1.00/e2fsprogs/blkid/blkid.h busybox/e2fsprogs/blkid/blkid.h
+--- busybox-1.00/e2fsprogs/blkid/blkid.h 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/blkid/blkid.h 2005-06-04 08:20:15.000000000 +0200
+@@ -0,0 +1,105 @@
++/*
++ * 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
++
++#include <sys/types.h>
++#include <linux/types.h>
++
++#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_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 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 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);
++
++/* version.c */
++extern int blkid_parse_version_string(const char *ver_string);
++extern int blkid_get_library_version(const char **ver_string,
++ const char **date_string);
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* _BLKID_BLKID_H */
+diff -Nur busybox-1.00/e2fsprogs/blkid/blkidP.h busybox/e2fsprogs/blkid/blkidP.h
+--- busybox-1.00/e2fsprogs/blkid/blkidP.h 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/blkid/blkidP.h 2005-06-04 08:20:15.000000000 +0200
+@@ -0,0 +1,239 @@
++/*
++ * 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
++
++#include <sys/types.h>
++#include <stdio.h>
++
++#include <blkid/blkid.h>
++
++#include <blkid/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 <stdio.h>
++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
++static inline void DEB_DUMP_TAG(int mask, blkid_tag tag)
++{
++ if (!(mask & blkid_debug_mask))
++ return;
++
++ if (!tag) {
++ printf(" tag: NULL\n");
++ return;
++ }
++
++ printf(" tag: %s=\"%s\"\n", tag->bit_name, tag->bit_val);
++}
++
++static inline void DEB_DUMP_DEV(int mask, blkid_dev dev)
++{
++ struct list_head *p;
++
++ if (!(mask & blkid_debug_mask))
++ return;
++
++ if (!dev) {
++ printf(" dev: NULL\n");
++ return;
++ }
++
++ printf(" dev: name = %s\n", dev->bid_name);
++ printf(" dev: DEVNO=\"0x%0Lx\"\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);
++ DEB_DUMP_TAG(mask, tag);
++ }
++ printf("\n");
++}
++
++static inline void DEB_DUMP_CACHE(int mask, blkid_cache cache)
++{
++ struct list_head *p;
++
++ if (!cache || !(mask & blkid_debug_mask)) {
++ printf("cache: NULL\n");
++ return;
++ }
++
++ printf("cache: time = %lu\n", cache->bic_time);
++ printf("cache: flags = 0x%08X\n", cache->bic_flags);
++
++ list_for_each(p, &cache->bic_devs) {
++ blkid_dev dev = list_entry(p, struct blkid_struct_dev, bid_devs);
++ DEB_DUMP_DEV(mask, dev);
++ }
++}
++#else
++#define DEB_DUMP_TAG(mask, tag) do {} while (0)
++#define DEB_DUMP_DEV(mask, dev) do {} while (0)
++#define DEB_DUMP_CACHE(mask, cache) do {} while (0)
++#endif
++
++/* lseek.c */
++extern blkid_loff_t blkid_llseek(int fd, blkid_loff_t offset, int whence);
++
++/* 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 /* _BLKID_BLKIDP_H */
+diff -Nur busybox-1.00/e2fsprogs/blkid/cache.c busybox/e2fsprogs/blkid/cache.c
+--- busybox-1.00/e2fsprogs/blkid/cache.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/blkid/cache.c 2005-06-04 08:20:15.000000000 +0200
+@@ -0,0 +1,126 @@
++/*
++ * 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 <stdlib.h>
++#include <string.h>
++#include <unistd.h>
++#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"));
++
++ if (!(cache = (blkid_cache) calloc(1, sizeof(struct blkid_struct_cache))))
++ return -BLKID_ERR_MEM;
++
++ 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"));
++
++ /* DEB_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);
++ }
++ if (cache->bic_filename)
++ 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, "/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 -Nur busybox-1.00/e2fsprogs/blkid/dev.c busybox/e2fsprogs/blkid/dev.c
+--- busybox-1.00/e2fsprogs/blkid/dev.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/blkid/dev.c 2005-06-04 08:20:15.000000000 +0200
+@@ -0,0 +1,118 @@
++/*
++ * 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 <stdlib.h>
++#include <string.h>
++
++#include "blkidP.h"
++
++blkid_dev blkid_new_dev(void)
++{
++ blkid_dev dev;
++
++ if (!(dev = (blkid_dev) calloc(1, sizeof(struct blkid_struct_dev))))
++ return NULL;
++
++ 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));
++ DEB_DUMP_DEV(DEBUG_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
++ */
++extern const char *blkid_dev_devname(blkid_dev dev)
++{
++ return dev->bid_name;
++}
++
++/*
++ * 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;
++};
++
++extern blkid_dev_iterate blkid_dev_iterate_begin(blkid_cache cache)
++{
++ blkid_dev_iterate iter;
++
++ iter = malloc(sizeof(struct blkid_struct_dev_iterate));
++ if (iter) {
++ 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;
++}
++
++extern void blkid_dev_iterate_end(blkid_dev_iterate iter)
++{
++ if (!iter || iter->magic != DEV_ITERATE_MAGIC)
++ return;
++ iter->magic = 0;
++ free(iter);
++}
++
+diff -Nur busybox-1.00/e2fsprogs/blkid/devname.c busybox/e2fsprogs/blkid/devname.c
+--- busybox-1.00/e2fsprogs/blkid/devname.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/blkid/devname.c 2005-06-04 08:20:15.000000000 +0200
+@@ -0,0 +1,376 @@
++/*
++ * 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 <stdio.h>
++#include <string.h>
++#if HAVE_UNISTD_H
++#include <unistd.h>
++#endif
++#include <stdlib.h>
++#include <string.h>
++#include <ctype.h>
++#if HAVE_SYS_TYPES_H
++#include <sys/types.h>
++#endif
++#if HAVE_SYS_STAT_H
++#include <sys/stat.h>
++#endif
++#if HAVE_ERRNO_H
++#include <errno.h>
++#endif
++#if HAVE_SYS_MKDEV_H
++#include <sys/mkdev.h>
++#endif
++#include <time.h>
++
++#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;
++ return;
++}
++
++#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 <dirent.h>
++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(lvm_device, "r")) == 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 (!strcmp(vg_name, ".") || !strcmp(vg_name, ".."))
++ continue;
++ vdirname = malloc(vg_len + strlen(vg_name) + 8);
++ if (!vdirname)
++ goto exit;
++ 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 (!strcmp(lv_name, ".") || !strcmp(lv_name, ".."))
++ continue;
++
++ lvm_device = malloc(vg_len + strlen(vg_name) +
++ strlen(lv_name) + 8);
++ if (!lvm_device) {
++ closedir(lv_list);
++ goto exit;
++ }
++ 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);
++ }
++exit:
++ 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(PROC_EVMS_VOLUMES, "r");
++ 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 = 0;
++ 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(0) - 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(PROC_PARTITIONS, "r");
++ 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(0);
++ 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, "/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 -Nur busybox-1.00/e2fsprogs/blkid/devno.c busybox/e2fsprogs/blkid/devno.c
+--- busybox-1.00/e2fsprogs/blkid/devno.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/blkid/devno.c 2005-06-04 08:20:15.000000000 +0200
+@@ -0,0 +1,233 @@
++/*
++ * 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 <stdio.h>
++#include <string.h>
++#if HAVE_UNISTD_H
++#include <unistd.h>
++#endif
++#include <stdlib.h>
++#include <string.h>
++#if HAVE_SYS_TYPES_H
++#include <sys/types.h>
++#endif
++#if HAVE_SYS_STAT_H
++#include <sys/stat.h>
++#endif
++#include <dirent.h>
++#if HAVE_ERRNO_H
++#include <errno.h>
++#endif
++#if HAVE_SYS_MKDEV_H
++#include <sys/mkdev.h>
++#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 = malloc(length + 1);
++ if (ret) {
++ 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 = malloc(sizeof(struct dir_list));
++ if (!dp)
++ return;
++ dp->name = blkid_strdup(name);
++ if (!dp->name) {
++ free(dp);
++ return;
++ }
++ 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%Lx at %s (%p)\n", devno,
++ path, *devname));
++ break;
++ }
++ }
++ closedir(dir);
++ return;
++}
++
++/* 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: couldn't find devno 0x%04lx\n",
++ (unsigned long) devno));
++ } else {
++ DBG(DEBUG_DEVNO,
++ printf("found devno 0x%04Lx 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 = "Couldn't 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);
++ if (devname)
++ free(devname);
++ return 0;
++}
++#endif
+diff -Nur busybox-1.00/e2fsprogs/blkid/getsize.c busybox/e2fsprogs/blkid/getsize.c
+--- busybox-1.00/e2fsprogs/blkid/getsize.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/blkid/getsize.c 2005-06-04 08:20:15.000000000 +0200
+@@ -0,0 +1,180 @@
++/*
++ * 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 <stdio.h>
++#if HAVE_UNISTD_H
++#include <unistd.h>
++#endif
++#if HAVE_ERRNO_H
++#include <errno.h>
++#endif
++#include <fcntl.h>
++#ifdef HAVE_SYS_IOCTL_H
++#include <sys/ioctl.h>
++#endif
++#ifdef HAVE_LINUX_FD_H
++#include <linux/fd.h>
++#endif
++#ifdef HAVE_SYS_DISKLABEL_H
++#include <sys/disklabel.h>
++#include <sys/stat.h>
++#endif
++#ifdef HAVE_SYS_DISK_H
++#ifdef HAVE_SYS_QUEUE_H
++#include <sys/queue.h> /* for LIST_HEAD */
++#endif
++#include <sys/disk.h>
++#endif
++#ifdef __linux__
++#include <sys/utsname.h>
++#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__
++ if ((uname(&ut) == 0) &&
++ ((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 %Ld 1k blocks.\n", argv[1], bytes >> 10);
++
++ return 0;
++}
++#endif
+diff -Nur busybox-1.00/e2fsprogs/blkid/list.h busybox/e2fsprogs/blkid/list.h
+--- busybox-1.00/e2fsprogs/blkid/list.h 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/blkid/list.h 2005-06-04 08:20:15.000000000 +0200
+@@ -0,0 +1,179 @@
++#if !defined(_BLKID_LIST_H) && !defined(LIST_HEAD)
++#define _BLKID_LIST_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#ifdef __GNUC__
++#define _INLINE_ static __inline__
++#else /* For Watcom C */
++#define _INLINE_ static inline
++#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)
++
++/*
++ * Insert a new entry between two known consecutive entries.
++ *
++ * This is only for internal list manipulation where we know
++ * the prev/next entries already!
++ */
++_INLINE_ 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.
++ */
++_INLINE_ 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.
++ */
++_INLINE_ 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!
++ */
++_INLINE_ 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.
++ */
++_INLINE_ 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.
++ */
++_INLINE_ 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.
++ */
++_INLINE_ 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.
++ */
++_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;
++ }
++}
++
++/**
++ * 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)
++
++#undef _INLINE_
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* _BLKID_LIST_H */
+diff -Nur busybox-1.00/e2fsprogs/blkid/llseek.c busybox/e2fsprogs/blkid/llseek.c
+--- busybox-1.00/e2fsprogs/blkid/llseek.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/blkid/llseek.c 2005-06-04 08:20:15.000000000 +0200
+@@ -0,0 +1,139 @@
++/*
++ * llseek.c -- stub calling the llseek system call
++ *
++ * Copyright (C) 1994, 1995, 1996, 1997 Theodore Ts'o.
++ *
++ * %Begin-Header%
++ * This file may be redistributed under the terms of the
++ * GNU Lesser General Public License.
++ * %End-Header%
++ */
++
++#if HAVE_SYS_TYPES_H
++#include <sys/types.h>
++#endif
++
++#if HAVE_ERRNO_H
++#include <errno.h>
++#endif
++#if HAVE_UNISTD_H
++#include <unistd.h>
++#endif
++#ifdef __MSDOS__
++#include <io.h>
++#endif
++
++#include "blkidP.h"
++
++#ifdef __linux__
++
++#if defined(HAVE_LSEEK64) && defined(HAVE_LSEEK64_PROTOTYPE)
++
++#define my_llseek lseek64
++
++#elif defined(HAVE_LLSEEK)
++#include <syscall.h>
++
++#ifndef HAVE_LLSEEK_PROTOTYPE
++extern long long llseek(int fd, long long offset, int origin);
++#endif
++
++#define my_llseek llseek
++
++#else /* ! HAVE_LLSEEK */
++
++#if defined(__alpha__) || defined(__ia64__)
++
++#define llseek lseek
++
++#else /* !__alpha__ && !__ia64__*/
++
++#include <linux/unistd.h>
++
++#ifndef __NR__llseek
++#define __NR__llseek 140
++#endif
++
++#ifndef __i386__
++static int _llseek(unsigned int, unsigned long, unsigned long,
++ blkid_loff_t *, unsigned int);
++
++static _syscall5(int, _llseek, unsigned int, fd, unsigned long, offset_high,
++ unsigned long, offset_low, blkid_loff_t *, result,
++ unsigned int, origin)
++#endif
++
++static blkid_loff_t my_llseek(int fd, blkid_loff_t offset, int origin)
++{
++ blkid_loff_t result;
++ int retval;
++
++#ifndef __i386__
++ retval = _llseek(fd, ((unsigned long long) offset) >> 32,
++ ((unsigned long long)offset) & 0xffffffff,
++ &result, origin);
++#else
++ retval = syscall(__NR__llseek, fd, ((unsigned long long) offset) >> 32,
++ ((unsigned long long)offset) & 0xffffffff,
++ &result, origin);
++#endif
++ return (retval == -1 ? (blkid_loff_t) retval : result);
++}
++
++#endif /* __alpha__ || __ia64__ */
++
++#endif /* HAVE_LLSEEK */
++
++blkid_loff_t blkid_llseek(int fd, blkid_loff_t offset, int whence)
++{
++ blkid_loff_t result;
++ static int do_compat = 0;
++
++ if ((sizeof(off_t) >= sizeof(blkid_loff_t)) ||
++ (offset < ((blkid_loff_t) 1 << ((sizeof(off_t)*8) -1))))
++ return lseek(fd, (off_t) offset, whence);
++
++ if (do_compat) {
++ errno = EOVERFLOW;
++ return -1;
++ }
++
++ result = my_llseek(fd, offset, whence);
++ if (result == -1 && errno == ENOSYS) {
++ /*
++ * Just in case this code runs on top of an old kernel
++ * which does not support the llseek system call
++ */
++ do_compat++;
++ errno = EOVERFLOW;
++ }
++ return result;
++}
++
++#else /* !linux */
++
++#ifndef EOVERFLOW
++#ifdef EXT2_ET_INVALID_ARGUMENT
++#define EOVERFLOW EXT2_ET_INVALID_ARGUMENT
++#else
++#define EOVERFLOW 112
++#endif
++#endif
++
++blkid_loff_t blkid_llseek(int fd, blkid_loff_t offset, int origin)
++{
++#if defined(HAVE_LSEEK64) && defined(HAVE_LSEEK64_PROTOTYPE)
++ return lseek64 (fd, offset, origin);
++#else
++ if ((sizeof(off_t) < sizeof(blkid_loff_t)) &&
++ (offset >= ((blkid_loff_t) 1 << ((sizeof(off_t)*8) - 1)))) {
++ errno = EOVERFLOW;
++ return -1;
++ }
++ return lseek(fd, (off_t) offset, origin);
++#endif
++}
++
++#endif /* linux */
++
++
+diff -Nur busybox-1.00/e2fsprogs/blkid/probe.c busybox/e2fsprogs/blkid/probe.c
+--- busybox-1.00/e2fsprogs/blkid/probe.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/blkid/probe.c 2005-06-04 08:20:15.000000000 +0200
+@@ -0,0 +1,704 @@
++/*
++ * 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 <stdio.h>
++#include <string.h>
++#include <stdlib.h>
++#include <unistd.h>
++#include <fcntl.h>
++#include <sys/types.h>
++#ifdef HAVE_SYS_STAT_H
++#include <sys/stat.h>
++#endif
++#ifdef HAVE_SYS_MKDEV_H
++#include <sys/mkdev.h>
++#endif
++#ifdef HAVE_ERRNO_H
++#include <errno.h>
++#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 = 0;
++
++ 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,
++ struct blkid_magic *id, 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,
++ struct blkid_magic *id, unsigned char *buf)
++{
++ struct ext2_super_block *es;
++// const char *sec_type = 0, *label = 0;
++
++ 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,
++ 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,
++ struct blkid_magic *id __BLKID_ATTR((unused)),
++ unsigned char *buf)
++{
++ struct vfat_super_block *vs;
++ char serno[10];
++ const char *label = 0;
++ 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,
++ 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 = 0;
++ 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,
++ struct blkid_magic *id __BLKID_ATTR((unused)),
++ unsigned char *buf)
++{
++ struct xfs_super_block *xs;
++ const char *label = 0;
++
++ 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,
++ struct blkid_magic *id, unsigned char *buf)
++{
++ struct reiserfs_super_block *rs = (struct reiserfs_super_block *) buf;
++ unsigned int blocksize;
++ const char *label = 0;
++
++ 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,
++ struct blkid_magic *id __BLKID_ATTR((unused)),
++ unsigned char *buf)
++{
++ struct jfs_super_block *js;
++ const char *label = 0;
++
++ 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,
++ struct blkid_magic *id __BLKID_ATTR((unused)),
++ unsigned char *buf)
++{
++ struct romfs_super_block *ros;
++ const char *label = 0;
++
++ 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_swap0(int fd __BLKID_ATTR((unused)),
++ blkid_cache cache __BLKID_ATTR((unused)),
++ blkid_dev dev,
++ 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,
++ struct blkid_magic *id __BLKID_ATTR((unused)),
++ unsigned char *buf __BLKID_ATTR((unused)))
++{
++ struct swap_id_block *sws;
++// const char *label = 0;
++
++ 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;
++ if (!(sws = (struct swap_id_block *)malloc(1024))) return 1;
++ 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", 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
++*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)),
++ struct blkid_magic *id __BLKID_ATTR((unused)),
++ unsigned char *buf __BLKID_ATTR((unused)))
++{
++ int j, bs;
++ struct iso_volume_descriptor isosb;
++ const char ** 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,
++ 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", ovl.label, ocfslabellen(ovl));
++ blkid_set_tag(dev, "MOUNT", 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,
++ 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", 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,
++ 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 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\034", 0 },
++ { "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)
++{
++ 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(0);
++ 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;
++
++ if (!(buf = (unsigned char *)malloc(1024)))
++ continue;
++
++ 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(0);
++ 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%04Lx, type %s\n",
++ dev->bid_name, st.st_rdev, type));
++ }
++
++ close(fd);
++
++ return dev;
++}
++
++int blkid_known_fstype(const char *fstype)
++{
++ 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, "/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 -Nur busybox-1.00/e2fsprogs/blkid/probe.h busybox/e2fsprogs/blkid/probe.h
+--- busybox-1.00/e2fsprogs/blkid/probe.h 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/blkid/probe.h 2005-06-04 08:20:15.000000000 +0200
+@@ -0,0 +1,359 @@
++/*
++ * 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
++
++#include <linux/types.h>
++
++struct blkid_magic;
++
++typedef int (*blkid_probe_t)(int fd, blkid_cache cache, blkid_dev dev,
++ 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 swap_id_block {
++/* unsigned char sws_boot[1024]; */
++ __u32 sws_version;
++ __u32 sws_lastpage;
++ __u32 sws_nrbad;
++ unsigned char sws_uuid[16];
++ unsigned 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];
++ unsigned char mount[128];
++ unsigned char mount_len[2];
++};
++
++struct ocfs_volume_label {
++ unsigned char disk_lock[48];
++ unsigned 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];
++ unsigned 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(val & 0xFFFFFFFFUL)) << 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(val & 0xFFFFFFFFUL)) << 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 -Nur busybox-1.00/e2fsprogs/blkid/read.c busybox/e2fsprogs/blkid/read.c
+--- busybox-1.00/e2fsprogs/blkid/read.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/blkid/read.c 2005-06-04 08:20:15.000000000 +0200
+@@ -0,0 +1,459 @@
++/*
++ * 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 <stdio.h>
++#include <ctype.h>
++#include <string.h>
++#include <time.h>
++#include <sys/types.h>
++#include <sys/stat.h>
++#include <fcntl.h>
++#include <unistd.h>
++#if HAVE_ERRNO_H
++#include <errno.h>
++#endif
++
++#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
++
++#if HAVE_STDLIB_H
++#include <stdlib.h>
++#endif
++
++/*
++ * File format:
++ *
++ * <device [<NAME="value"> ...]>device_name</device>
++ *
++ * The following tags are required for each entry:
++ * <ID="id"> unique (within this file) ID number of this device
++ * <TIME="time"> (ascii time_t) time this entry was last read from disk
++ * <TYPE="type"> (detected) type of filesystem/data for this partition
++ *
++ * The following tags may be present, depending on the device contents
++ * <LABEL="label"> (user supplied) label (volume name, etc)
++ * <UUID="uuid"> (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;
++}
++
++#if 0
++static char *parse_word(char **buf)
++{
++ char *word, *next;
++
++ word = *buf;
++ if (*word == '\0')
++ return NULL;
++
++ word = skip_over_blank(word);
++ next = skip_over_word(word);
++ if (*next) {
++ char *end = next - 1;
++ if (*end == '"' || *end == '\'')
++ *end = '\0';
++ *next++ = '\0';
++ }
++ *buf = next;
++
++ if (*word == '"' || *word == '\'')
++ word++;
++ return word;
++}
++#endif
++
++/*
++ * Start parsing a new line from the cache.
++ *
++ * line starts with "<device" return 1 -> continue parsing line
++ * line starts with "<foo", empty, or # return 0 -> 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, "<device", 7)) {
++ DBG(DEBUG_READ, printf("found device header: %8s\n", p));
++ p += 7;
++
++ *cp = p;
++ return 1;
++ }
++
++ if (*p == '<')
++ return 0;
++
++ return -BLKID_ERR_CACHE;
++}
++
++/* Consume the remaining XML on the line (cosmetic only) */
++static int parse_end(char **cp)
++{
++ *cp = skip_over_blank(*cp);
++
++ if (!strncmp(*cp, "</device>", 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:
++ * <device foo=bar>devname</device>
++ * <device>devname<foo>bar</foo></device>
++ */
++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 </device> 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 <NAME>value</NAME> 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 <device [TAG="value" ...]>/dev/foo</device>
++ *
++ * 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);
++ }
++
++ DEB_DUMP_DEV(DEBUG_READ, 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 = fdopen(fd, "r");
++ if (!file)
++ goto errout;
++
++ 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);
++ return;
++}
++
++#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 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 -Nur busybox-1.00/e2fsprogs/blkid/resolve.c busybox/e2fsprogs/blkid/resolve.c
+--- busybox-1.00/e2fsprogs/blkid/resolve.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/blkid/resolve.c 2005-06-04 08:20:15.000000000 +0200
+@@ -0,0 +1,140 @@
++/*
++ * 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 <stdio.h>
++#if HAVE_UNISTD_H
++#include <unistd.h>
++#endif
++#include <stdlib.h>
++#include <fcntl.h>
++#include <string.h>
++#include <sys/types.h>
++#include <sys/stat.h>
++#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 = 0, *v = 0;
++ 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:
++ if (t)
++ free(t);
++ if (v)
++ 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, "/dev/null") < 0) {
++ fprintf(stderr, "Couldn'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 : "<missing>");
++ } else {
++ value = blkid_get_devname(cache, argv[1], NULL);
++ printf("%s has tag %s\n", value ? value : "<none>", argv[1]);
++ }
++ blkid_put_cache(cache);
++ return value ? 0 : 1;
++}
++#endif
+diff -Nur busybox-1.00/e2fsprogs/blkid/save.c busybox/e2fsprogs/blkid/save.c
+--- busybox-1.00/e2fsprogs/blkid/save.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/blkid/save.c 2005-06-04 08:20:15.000000000 +0200
+@@ -0,0 +1,193 @@
++/*
++ * 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 <stdio.h>
++#include <string.h>
++#include <stdlib.h>
++#include <unistd.h>
++#include <sys/types.h>
++#ifdef HAVE_SYS_STAT_H
++#include <sys/stat.h>
++#endif
++#ifdef HAVE_SYS_MKDEV_H
++#include <sys/mkdev.h>
++#endif
++#ifdef HAVE_ERRNO_H
++#include <errno.h>
++#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,
++ "<device DEVNO=\"0x%04lx\" TIME=\"%lu\"",
++ (unsigned long) dev->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</device>\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 = malloc(strlen(filename) + 8);
++ if (tmp) {
++ sprintf(tmp, "%s-XXXXXX", filename);
++ fd = mkstemp(tmp);
++ if (fd >= 0) {
++ file = fdopen(fd, "w");
++ opened = tmp;
++ }
++ fchmod(fd, 0644);
++ }
++ }
++
++ if (!file) {
++ file = fopen(filename, "w");
++ 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 = malloc(strlen(filename) + 5);
++ if (backup) {
++ 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:
++ if (tmp)
++ 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, "/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 -Nur busybox-1.00/e2fsprogs/blkid/tag.c busybox/e2fsprogs/blkid/tag.c
+--- busybox-1.00/e2fsprogs/blkid/tag.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/blkid/tag.c 2005-06-04 08:20:15.000000000 +0200
+@@ -0,0 +1,340 @@
++/*
++ * 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 <stdlib.h>
++#include <string.h>
++#include <stdio.h>
++
++#include "blkidP.h"
++
++static blkid_tag blkid_new_tag(void)
++{
++ blkid_tag tag;
++
++ if (!(tag = (blkid_tag) calloc(1, sizeof(struct blkid_struct_tag))))
++ return NULL;
++
++ INIT_LIST_HEAD(&tag->bit_tags);
++ INIT_LIST_HEAD(&tag->bit_names);
++
++ return tag;
++}
++
++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)"));
++ DEB_DUMP_TAG(DEBUG_TAG, tag);
++
++ list_del(&tag->bit_tags); /* list of tags for this device */
++ list_del(&tag->bit_names); /* list of tags with this type */
++
++ if (tag->bit_name)
++ free(tag->bit_name);
++ if (tag->bit_val)
++ 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 = 0;
++
++ 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) {
++ if (t)
++ 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:
++ if (t)
++ blkid_free_tag(t);
++ else if (val)
++ free(val);
++ if (head)
++ 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;
++};
++
++extern blkid_tag_iterate blkid_tag_iterate_begin(blkid_dev dev)
++{
++ blkid_tag_iterate iter;
++
++ iter = malloc(sizeof(struct blkid_struct_tag_iterate));
++ if (iter) {
++ 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;
++}
++
++extern 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;
++}
+diff -Nur busybox-1.00/e2fsprogs/blkid/version.c busybox/e2fsprogs/blkid/version.c
+--- busybox-1.00/e2fsprogs/blkid/version.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/blkid/version.c 2005-06-04 08:20:15.000000000 +0200
+@@ -0,0 +1,49 @@
++/*
++ * version.c --- Return the version of the blkid library
++ *
++ * Copyright (C) 2004 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 <unistd.h>
++#endif
++#include <string.h>
++#include <stdio.h>
++#include <ctype.h>
++
++#include "blkid.h"
++#include "../../version.h"
++
++static const char *lib_version = E2FSPROGS_VERSION;
++static const char *lib_date = E2FSPROGS_DATE;
++
++int blkid_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 blkid_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 blkid_parse_version_string(lib_version);
++}
+diff -Nur busybox-1.00/e2fsprogs/chattr.c busybox/e2fsprogs/chattr.c
+--- busybox-1.00/e2fsprogs/chattr.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/chattr.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,225 @@
++/*
++ * chattr.c - Change file attributes on an ext2 file system
++ *
++ * Copyright (C) 1993, 1994 Remy Card <card@masi.ibp.fr>
++ * 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 <sys/types.h>
++#include <dirent.h>
++#include <fcntl.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <unistd.h>
++#include <string.h>
++#include <errno.h>
++#include <sys/param.h>
++#include <sys/stat.h>
++#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;
++
++#ifdef CONFIG_LFS
++# define LSTAT lstat64
++# define STRUCT_STAT struct stat64
++#else
++# define LSTAT lstat
++# define STRUCT_STAT struct stat
++#endif
++
++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] == '\0' || \
++ (de->d_name[1] == '.' && de->d_name[2] == '\0'))) {
++ 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)
++{
++ int i;
++ char *arg;
++
++ /* parse the args */
++ for (i = 1; i < argc; ++i) {
++ arg = argv[i];
++
++ /* take care of -R and -v <version> */
++ 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 -Nur busybox-1.00/e2fsprogs/e2fsbb.h busybox/e2fsprogs/e2fsbb.h
+--- busybox-1.00/e2fsprogs/e2fsbb.h 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/e2fsbb.h 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,40 @@
++/*
++ * 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.37"
++#define E2FSPROGS_DATE "21-Mar-2005"
++
++/* make sure com_err.h isnt included before us */
++#ifdef __COM_ERR_H__
++#error You should not have included com_err.h !
++#endif
++#define __COM_ERR_H__
++
++/* com_err crap */
++#define com_err(w, c, fmt, args...) bb_error_msg(fmt, ## args)
++typedef long errcode_t;
++#define ERRCODE_RANGE 8
++#define error_message(code) strerror((int) (code & ((1<<ERRCODE_RANGE)-1)))
++#define initialize_ext2_error_table(x)
++
++/* NLS crap */
++#define _(x) x
++#define N_(x) x
++#define P_(singular, plural, n) ((n) == 1 ? (singular) : (plural))
++
++/* misc crap */
++#define fatal_error(msg, err) bb_error_msg_and_die(msg)
++#define usage() bb_show_usage()
++#define perror(msg) bb_perror_msg(msg)
++
++#endif /* __E2FSBB_H__ */
+diff -Nur busybox-1.00/e2fsprogs/e2p/e2p.h busybox/e2fsprogs/e2p/e2p.h
+--- busybox-1.00/e2fsprogs/e2p/e2p.h 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/e2p/e2p.h 2005-06-04 08:20:15.000000000 +0200
+@@ -0,0 +1,59 @@
++#include <sys/types.h> /* Needed by dirent.h on netbsd */
++#include <stdio.h>
++#include <dirent.h>
++
++#include <ext2fs/ext2_fs.h>
++
++#define E2P_FEATURE_COMPAT 0
++#define E2P_FEATURE_INCOMPAT 1
++#define E2P_FEATURE_RO_INCOMPAT 2
++
++
++/* `options' for print_flags() */
++
++#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);
++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 -Nur busybox-1.00/e2fsprogs/e2p/feature.c busybox/e2fsprogs/e2p/feature.c
+--- busybox-1.00/e2fsprogs/e2p/feature.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/e2p/feature.c 2005-06-04 08:20:15.000000000 +0200
+@@ -0,0 +1,190 @@
++/*
++ * feature.c --- convert between features and strings
++ *
++ * Copyright (C) 1999 Theodore Ts'o <tytso@mit.edu>
++ *
++ * This file can be redistributed under the terms of the GNU Library General
++ * Public License
++ *
++ */
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include <ctype.h>
++#include <errno.h>
++
++#include "e2p.h"
++
++struct feature {
++ int compat;
++ unsigned int mask;
++ const char *string;
++};
++
++static 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)
++{
++ 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)
++{
++ 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 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 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 = malloc(strlen(str)+1);
++ if (!buf)
++ return 1;
++ strcpy(buf, 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 -Nur busybox-1.00/e2fsprogs/e2p/fgetsetflags.c busybox/e2fsprogs/e2p/fgetsetflags.c
+--- busybox-1.00/e2fsprogs/e2p/fgetsetflags.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/e2p/fgetsetflags.c 2005-06-04 08:20:15.000000000 +0200
+@@ -0,0 +1,69 @@
++/*
++ * 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 <card@masi.ibp.fr>
++ * 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
++ */
++
++#if HAVE_ERRNO_H
++#include <errno.h>
++#endif
++#if HAVE_UNISTD_H
++#include <unistd.h>
++#endif
++#include <sys/types.h>
++#include <sys/stat.h>
++#if HAVE_EXT2_IOCTLS
++#include <fcntl.h>
++#include <sys/ioctl.h>
++#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)
++{
++#if 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;
++#endif /* HAVE_EXT2_IOCTLS */
++notsupp:
++ errno = EOPNOTSUPP;
++ return -1;
++}
+diff -Nur busybox-1.00/e2fsprogs/e2p/fgetsetversion.c busybox/e2fsprogs/e2p/fgetsetversion.c
+--- busybox-1.00/e2fsprogs/e2p/fgetsetversion.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/e2p/fgetsetversion.c 2005-06-04 08:20:15.000000000 +0200
+@@ -0,0 +1,69 @@
++/*
++ * 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 <card@masi.ibp.fr>
++ * 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
++ */
++
++#if HAVE_ERRNO_H
++#include <errno.h>
++#endif
++#if HAVE_UNISTD_H
++#include <unistd.h>
++#endif
++#include <fcntl.h>
++#include <sys/ioctl.h>
++
++#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)
++{
++#if 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 -Nur busybox-1.00/e2fsprogs/e2p/hashstr.c busybox/e2fsprogs/e2p/hashstr.c
+--- busybox-1.00/e2fsprogs/e2p/hashstr.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/e2p/hashstr.c 2005-06-04 08:20:15.000000000 +0200
+@@ -0,0 +1,70 @@
++/*
++ * feature.c --- convert between features and strings
++ *
++ * Copyright (C) 1999 Theodore Ts'o <tytso@mit.edu>
++ *
++ * This file can be redistributed under the terms of the GNU Library General
++ * Public License
++ *
++ */
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include <ctype.h>
++#include <errno.h>
++
++#include "e2p.h"
++
++struct hash {
++ int num;
++ const char *string;
++};
++
++static 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)
++{
++ 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)
++{
++ 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 -Nur busybox-1.00/e2fsprogs/e2p/iod.c busybox/e2fsprogs/e2p/iod.c
+--- busybox-1.00/e2fsprogs/e2p/iod.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/e2p/iod.c 2005-06-04 08:20:15.000000000 +0200
+@@ -0,0 +1,51 @@
++/*
++ * iod.c - Iterate a function on each entry of a directory
++ *
++ * Copyright (C) 1993, 1994 Remy Card <card@masi.ibp.fr>
++ * 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 <unistd.h>
++#include <stdlib.h>
++#include <string.h>
++
++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 = (struct dirent *)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 -Nur busybox-1.00/e2fsprogs/e2p/ls.c busybox/e2fsprogs/e2p/ls.c
+--- busybox-1.00/e2fsprogs/e2p/ls.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/e2p/ls.c 2005-06-04 08:20:15.000000000 +0200
+@@ -0,0 +1,276 @@
++/*
++ * ls.c - List the contents of an ext2fs superblock
++ *
++ * Copyright (C) 1992, 1993, 1994 Remy Card <card@masi.ibp.fr>
++ * Laboratoire MASI, Institut Blaise Pascal
++ * Universite Pierre et Marie Curie (Paris VI)
++ *
++ * Copyright (C) 1995, 1996, 1997 Theodore Ts'o <tytso@mit.edu>
++ *
++ * This file can be redistributed under the terms of the GNU Library General
++ * Public License
++ */
++
++#include <stdio.h>
++#include <sys/types.h>
++#include <string.h>
++#include <grp.h>
++#include <pwd.h>
++#include <time.h>
++
++#include "e2p.h"
++
++static void print_user (unsigned short uid, FILE *f)
++{
++ struct passwd *pw;
++
++ fprintf(f, "%u ", uid);
++ pw = getpwuid (uid);
++ if (pw == NULL)
++ fprintf(f, "(user unknown)\n");
++ else
++ fprintf(f, "(user %s)\n", pw->pw_name);
++}
++
++static void print_group (unsigned short gid, FILE *f)
++{
++ struct group *gr;
++
++ fprintf(f, "%u ", gid);
++ gr = getgrgid (gid);
++ if (gr == NULL)
++ fprintf(f, "(group unknown)\n");
++ else
++ fprintf(f, "(group %s)\n", 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 "<none>";
++
++ 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, "<none>");
++ 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, "<not available>");
++ fprintf(f, "Last mounted on: %s\n", buf);
++ fprintf(f, "Filesystem UUID: %s\n", e2p_uuid2str(sb->s_uuid));
++ fprintf(f, "Filesystem magic number: 0x%04X\n", sb->s_magic);
++ fprintf(f, "Filesystem revision #: %d", 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, "\n");
++ fprintf(f, "Errors behavior: ");
++ print_fs_errors(f, sb->s_errors);
++ fprintf(f, "\n");
++ str = e2p_os2string(sb->s_creator_os);
++ fprintf(f, "Filesystem OS type: %s\n", str);
++ free(str);
++ fprintf(f, "Inode count: %u\n", sb->s_inodes_count);
++ fprintf(f, "Block count: %u\n", sb->s_blocks_count);
++ fprintf(f, "Reserved block count: %u\n", sb->s_r_blocks_count);
++ fprintf(f, "Free blocks: %u\n", sb->s_free_blocks_count);
++ fprintf(f, "Free inodes: %u\n", sb->s_free_inodes_count);
++ fprintf(f, "First block: %u\n", sb->s_first_data_block);
++ fprintf(f, "Block size: %u\n", EXT2_BLOCK_SIZE(sb));
++ fprintf(f, "Fragment size: %u\n", EXT2_FRAG_SIZE(sb));
++ 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", sb->s_blocks_per_group);
++ fprintf(f, "Fragments per group: %u\n", sb->s_frags_per_group);
++ fprintf(f, "Inodes per group: %u\n", sb->s_inodes_per_group);
++ fprintf(f, "Inode blocks per group: %u\n", 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", ctime(&tm));
++ fprintf(f, "Mount count: %u\n", sb->s_mnt_count);
++ fprintf(f, "Maximum mount count: %d\n", sb->s_max_mnt_count);
++ tm = sb->s_lastcheck;
++ fprintf(f, "Last checked: %s", ctime(&tm));
++ fprintf(f, "Check interval: %u (%s)\n", 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", sb->s_first_ino);
++ fprintf(f, "Inode size: %d\n", 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: ");
++ switch (sb->s_jnl_backup_type) {
++ case 1:
++ fprintf(f, "inode blocks\n");
++ break;
++ default:
++ fprintf(f, "type %u\n", sb->s_jnl_backup_type);
++ }
++ }
++}
++
++void list_super (struct ext2_super_block * s)
++{
++ list_super2(s, stdout);
++}
++
+diff -Nur busybox-1.00/e2fsprogs/e2p/mntopts.c busybox/e2fsprogs/e2p/mntopts.c
+--- busybox-1.00/e2fsprogs/e2p/mntopts.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/e2p/mntopts.c 2005-06-04 08:20:15.000000000 +0200
+@@ -0,0 +1,136 @@
++/*
++ * mountopts.c --- convert between default mount options and strings
++ *
++ * Copyright (C) 2002 Theodore Ts'o <tytso@mit.edu>
++ *
++ * This file can be redistributed under the terms of the GNU Library General
++ * Public License
++ *
++ */
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include <ctype.h>
++#include <errno.h>
++
++#include "e2p.h"
++
++struct mntopt {
++ unsigned int mask;
++ const char *string;
++};
++
++static 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)
++{
++ 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)
++{
++ 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 = malloc(strlen(str)+1);
++ if (!buf)
++ return 1;
++ strcpy(buf, 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 -Nur busybox-1.00/e2fsprogs/e2p/ostype.c busybox/e2fsprogs/e2p/ostype.c
+--- busybox-1.00/e2fsprogs/e2p/ostype.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/e2p/ostype.c 2005-06-04 08:20:15.000000000 +0200
+@@ -0,0 +1,73 @@
++/*
++ * getostype.c - Get the Filesystem OS type
++ *
++ * Copyright (C) 2004,2005 Theodore Ts'o <tytso@mit.edu>
++ *
++ * This file can be redistributed under the terms of the GNU Library General
++ * Public License
++ */
++
++#include "e2p.h"
++#include <string.h>
++
++const char *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 = malloc(strlen(os)+1);
++ strcpy(ret, os);
++ return ret;
++}
++
++/*
++ * Convert an os_type to a string
++ */
++int e2p_string2os(char *str)
++{
++ const char **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 -Nur busybox-1.00/e2fsprogs/e2p/parse_num.c busybox/e2fsprogs/e2p/parse_num.c
+--- busybox-1.00/e2fsprogs/e2p/parse_num.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/e2p/parse_num.c 2005-06-04 08:20:15.000000000 +0200
+@@ -0,0 +1,64 @@
++/*
++ * parse_num.c - Parse the number of blocks
++ *
++ * Copyright (C) 2004,2005 Theodore Ts'o <tytso@mit.edu>
++ *
++ * This file can be redistributed under the terms of the GNU Library General
++ * Public License
++ */
++
++#include "e2p.h"
++
++#include <stdlib.h>
++
++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 <unistd.h>
++#include <stdio.h>
++
++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 -Nur busybox-1.00/e2fsprogs/e2p/pe.c busybox/e2fsprogs/e2p/pe.c
+--- busybox-1.00/e2fsprogs/e2p/pe.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/e2p/pe.c 2005-06-04 08:20:15.000000000 +0200
+@@ -0,0 +1,37 @@
++/*
++ * pe.c - Print a second extended filesystem errors behavior
++ *
++ * Copyright (C) 1992, 1993, 1994 Remy Card <card@masi.ibp.fr>
++ * 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 <stdio.h>
++
++#include "e2p.h"
++
++void print_fs_errors (FILE * f, unsigned short errors)
++{
++ switch (errors)
++ {
++ case EXT2_ERRORS_CONTINUE:
++ fprintf (f, "Continue");
++ break;
++ case EXT2_ERRORS_RO:
++ fprintf (f, "Remount read-only");
++ break;
++ case EXT2_ERRORS_PANIC:
++ fprintf (f, "Panic");
++ break;
++ default:
++ fprintf (f, "Unknown (continue)");
++ }
++}
+diff -Nur busybox-1.00/e2fsprogs/e2p/pf.c busybox/e2fsprogs/e2p/pf.c
+--- busybox-1.00/e2fsprogs/e2p/pf.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/e2p/pf.c 2005-06-04 08:20:15.000000000 +0200
+@@ -0,0 +1,74 @@
++/*
++ * pf.c - Print file attributes on an ext2 file system
++ *
++ * Copyright (C) 1993, 1994 Remy Card <card@masi.ibp.fr>
++ * 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 <stdio.h>
++
++#include "e2p.h"
++
++struct flags_name {
++ unsigned long flag;
++ const char *short_name;
++ const char *long_name;
++};
++
++static 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);
++ 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 -Nur busybox-1.00/e2fsprogs/e2p/ps.c busybox/e2fsprogs/e2p/ps.c
+--- busybox-1.00/e2fsprogs/e2p/ps.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/e2p/ps.c 2005-06-04 08:20:15.000000000 +0200
+@@ -0,0 +1,29 @@
++/*
++ * ps.c - Print filesystem state
++ *
++ * Copyright (C) 1993, 1994 Remy Card <card@masi.ibp.fr>
++ * 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 <stdio.h>
++
++#include "e2p.h"
++
++void print_fs_state (FILE * f, unsigned short state)
++{
++ if (state & EXT2_VALID_FS)
++ fprintf (f, " clean");
++ else
++ fprintf (f, " not clean");
++ if (state & EXT2_ERROR_FS)
++ fprintf (f, " with errors");
++}
+diff -Nur busybox-1.00/e2fsprogs/e2p/uuid.c busybox/e2fsprogs/e2p/uuid.c
+--- busybox-1.00/e2fsprogs/e2p/uuid.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/e2p/uuid.c 2005-06-04 08:20:15.000000000 +0200
+@@ -0,0 +1,79 @@
++/*
++ * uuid.c -- utility routines for manipulating UUID's.
++ */
++
++#include <stdio.h>
++#include <string.h>
++#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 "<none>";
++ e2p_uuid_to_str(uu, buf);
++ return buf;
++}
++
+diff -Nur busybox-1.00/e2fsprogs/ext2fs/alloc.c busybox/e2fsprogs/ext2fs/alloc.c
+--- busybox-1.00/e2fsprogs/ext2fs/alloc.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/alloc.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,173 @@
++/*
++ * 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 <stdio.h>
++#if HAVE_UNISTD_H
++#include <unistd.h>
++#endif
++#include <time.h>
++#include <string.h>
++#if HAVE_SYS_STAT_H
++#include <sys/stat.h>
++#endif
++#if HAVE_SYS_TYPES_H
++#include <sys/types.h>
++#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 = 0;
++
++ 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 -Nur busybox-1.00/e2fsprogs/ext2fs/alloc_sb.c busybox/e2fsprogs/ext2fs/alloc_sb.c
+--- busybox-1.00/e2fsprogs/ext2fs/alloc_sb.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/alloc_sb.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,57 @@
++/*
++ * 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 <stdio.h>
++#include <string.h>
++#if HAVE_UNISTD_H
++#include <unistd.h>
++#endif
++#include <fcntl.h>
++#include <time.h>
++#if HAVE_SYS_STAT_H
++#include <sys/stat.h>
++#endif
++#if HAVE_SYS_TYPES_H
++#include <sys/types.h>
++#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 -Nur busybox-1.00/e2fsprogs/ext2fs/alloc_stats.c busybox/e2fsprogs/ext2fs/alloc_stats.c
+--- busybox-1.00/e2fsprogs/ext2fs/alloc_stats.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/alloc_stats.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,52 @@
++/*
++ * 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 <stdio.h>
++
++#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 -Nur busybox-1.00/e2fsprogs/ext2fs/alloc_tables.c busybox/e2fsprogs/ext2fs/alloc_tables.c
+--- busybox-1.00/e2fsprogs/ext2fs/alloc_tables.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/alloc_tables.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,117 @@
++/*
++ * 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 <stdio.h>
++#include <string.h>
++#if HAVE_UNISTD_H
++#include <unistd.h>
++#endif
++#include <fcntl.h>
++#include <time.h>
++#if HAVE_SYS_STAT_H
++#include <sys/stat.h>
++#endif
++#if HAVE_SYS_TYPES_H
++#include <sys/types.h>
++#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 -Nur busybox-1.00/e2fsprogs/ext2fs/badblocks.c busybox/e2fsprogs/ext2fs/badblocks.c
+--- busybox-1.00/e2fsprogs/ext2fs/badblocks.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/badblocks.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,327 @@
++/*
++ * 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 <stdio.h>
++#include <string.h>
++#if HAVE_UNISTD_H
++#include <unistd.h>
++#endif
++#include <fcntl.h>
++#include <time.h>
++#if HAVE_SYS_STAT_H
++#include <sys/stat.h>
++#endif
++#if HAVE_SYS_TYPES_H
++#include <sys/types.h>
++#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 -Nur busybox-1.00/e2fsprogs/ext2fs/bb_compat.c busybox/e2fsprogs/ext2fs/bb_compat.c
+--- busybox-1.00/e2fsprogs/ext2fs/bb_compat.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/bb_compat.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,63 @@
++/*
++ * 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 <stdio.h>
++#include <string.h>
++#if HAVE_UNISTD_H
++#include <unistd.h>
++#endif
++#include <fcntl.h>
++#include <time.h>
++#if HAVE_SYS_STAT_H
++#include <sys/stat.h>
++#endif
++#if HAVE_SYS_TYPES_H
++#include <sys/types.h>
++#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 -Nur busybox-1.00/e2fsprogs/ext2fs/bb_inode.c busybox/e2fsprogs/ext2fs/bb_inode.c
+--- busybox-1.00/e2fsprogs/ext2fs/bb_inode.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/bb_inode.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,267 @@
++/*
++ * 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 <stdio.h>
++#include <string.h>
++#if HAVE_UNISTD_H
++#include <unistd.h>
++#endif
++#include <fcntl.h>
++#include <time.h>
++#if HAVE_SYS_STAT_H
++#include <sys/stat.h>
++#endif
++#if HAVE_SYS_TYPES_H
++#include <sys/types.h>
++#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(0);
++ if (!inode.i_ctime)
++ inode.i_ctime = time(0);
++ 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 -Nur busybox-1.00/e2fsprogs/ext2fs/bitmaps.c busybox/e2fsprogs/ext2fs/bitmaps.c
+--- busybox-1.00/e2fsprogs/ext2fs/bitmaps.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/bitmaps.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,212 @@
++/*
++ * 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 <stdio.h>
++#include <string.h>
++#if HAVE_UNISTD_H
++#include <unistd.h>
++#endif
++#include <fcntl.h>
++#include <time.h>
++#if HAVE_SYS_STAT_H
++#include <sys/stat.h>
++#endif
++#if HAVE_SYS_TYPES_H
++#include <sys/types.h>
++#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);
++
++ return;
++}
++
++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 -Nur busybox-1.00/e2fsprogs/ext2fs/bitops.c busybox/e2fsprogs/ext2fs/bitops.c
+--- busybox-1.00/e2fsprogs/ext2fs/bitops.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/bitops.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,91 @@
++/*
++ * 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 <stdio.h>
++#if HAVE_SYS_TYPES_H
++#include <sys/types.h>
++#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(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(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(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)
++ com_err(0, errcode, "#%lu for %s", arg, description);
++ else
++ com_err(0, errcode, "#%lu", arg);
++#endif
++}
++
++void ext2fs_warn_bitmap2(ext2fs_generic_bitmap bitmap,
++ int code, unsigned long arg)
++{
++#ifndef OMIT_COM_ERR
++ if (bitmap->description)
++ com_err(0, bitmap->base_error_code+code,
++ "#%lu for %s", arg, bitmap->description);
++ else
++ com_err(0, bitmap->base_error_code + code, "#%lu", arg);
++#endif
++}
++
+diff -Nur busybox-1.00/e2fsprogs/ext2fs/bitops.h busybox/e2fsprogs/ext2fs/bitops.h
+--- busybox-1.00/e2fsprogs/ext2fs/bitops.h 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/bitops.h 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,617 @@
++/*
++ * 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 <asm/bitops.h>, Copyright 1992,
++ * Linus Torvalds.
++ */
++
++
++extern int ext2fs_set_bit(int nr,void * addr);
++extern int ext2fs_clear_bit(int nr, void * addr);
++extern int ext2fs_test_bit(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);
++/*
++ * The inline routines themselves...
++ *
++ * If NO_INLINE_FUNCS is defined, then we won't try to do inline
++ * functions at all; they will be included as normal functions in
++ * inline.c
++ */
++#ifdef NO_INLINE_FUNCS
++#if (defined(__GNUC__) && (defined(__i386__) || defined(__i486__) || \
++ defined(__i586__) || defined(__mc68000__) || \
++ defined(__sparc__)))
++ /* This prevents bitops.c from trying to include the C */
++ /* function version of these functions */
++#define _EXT2_HAVE_ASM_BITOPS_
++#endif
++#endif /* NO_INLINE_FUNCS */
++
++#if (defined(INCLUDE_INLINE_FUNCS) || !defined(NO_INLINE_FUNCS))
++#ifdef INCLUDE_INLINE_FUNCS
++#define _INLINE_ extern
++#else
++#ifdef __GNUC__
++#define _INLINE_ extern __inline__
++#else /* For Watcom C */
++#define _INLINE_ extern inline
++#endif
++#endif
++
++#if ((defined __GNUC__) && !defined(_EXT2_USE_C_VERSIONS_) && \
++ (defined(__i386__) || defined(__i486__) || defined(__i586__)))
++
++#define _EXT2_HAVE_ASM_BITOPS_
++#define _EXT2_HAVE_ASM_SWAB_
++#define _EXT2_HAVE_ASM_FINDBIT_
++
++/*
++ * These are done by inline assembly for speed reasons.....
++ *
++ * All bitoperations return 0 if the bit was cleared before the
++ * operation and != 0 if it was not. Bit 0 is the LSB of addr; bit 32
++ * is the LSB of (addr+1).
++ */
++
++/*
++ * Some hacks to defeat gcc over-optimizations..
++ */
++struct __dummy_h { unsigned long a[100]; };
++#define EXT2FS_ADDR (*(struct __dummy_h *) addr)
++#define EXT2FS_CONST_ADDR (*(const struct __dummy_h *) addr)
++
++_INLINE_ int ext2fs_set_bit(int nr, void * addr)
++{
++ int oldbit;
++
++ __asm__ __volatile__("btsl %2,%1\n\tsbbl %0,%0"
++ :"=r" (oldbit),"=m" (EXT2FS_ADDR)
++ :"r" (nr));
++ return oldbit;
++}
++
++_INLINE_ int ext2fs_clear_bit(int nr, void * addr)
++{
++ int oldbit;
++
++ __asm__ __volatile__("btrl %2,%1\n\tsbbl %0,%0"
++ :"=r" (oldbit),"=m" (EXT2FS_ADDR)
++ :"r" (nr));
++ return oldbit;
++}
++
++_INLINE_ int ext2fs_test_bit(int nr, const void * addr)
++{
++ int oldbit;
++
++ __asm__ __volatile__("btl %2,%1\n\tsbbl %0,%0"
++ :"=r" (oldbit)
++ :"m" (EXT2FS_CONST_ADDR),"r" (nr));
++ return oldbit;
++}
++
++#if 0
++_INLINE_ int ext2fs_find_first_bit_set(void * addr, unsigned size)
++{
++ int d0, d1, d2;
++ int res;
++
++ if (!size)
++ return 0;
++ /* This looks at memory. Mark it volatile to tell gcc not to move it around */
++ __asm__ __volatile__(
++ "cld\n\t"
++ "xorl %%eax,%%eax\n\t"
++ "xorl %%edx,%%edx\n\t"
++ "repe; scasl\n\t"
++ "je 1f\n\t"
++ "movl -4(%%edi),%%eax\n\t"
++ "subl $4,%%edi\n\t"
++ "bsfl %%eax,%%edx\n"
++ "1:\tsubl %%esi,%%edi\n\t"
++ "shll $3,%%edi\n\t"
++ "addl %%edi,%%edx"
++ :"=d" (res), "=&c" (d0), "=&D" (d1), "=&a" (d2)
++ :"1" ((size + 31) >> 5), "2" (addr), "S" (addr));
++ return res;
++}
++
++_INLINE_ int ext2fs_find_next_bit_set (void * addr, int size, int offset)
++{
++ unsigned long * p = ((unsigned long *) addr) + (offset >> 5);
++ int set = 0, bit = offset & 31, res;
++
++ if (bit) {
++ /*
++ * Look for zero in first byte
++ */
++ __asm__("bsfl %1,%0\n\t"
++ "jne 1f\n\t"
++ "movl $32, %0\n"
++ "1:"
++ : "=r" (set)
++ : "r" (*p >> bit));
++ if (set < (32 - bit))
++ return set + offset;
++ set = 32 - bit;
++ p++;
++ }
++ /*
++ * No bit found yet, search remaining full bytes for a bit
++ */
++ res = ext2fs_find_first_bit_set(p, size - 32 * (p - (unsigned long *) addr));
++ return (offset + set + res);
++}
++#endif
++
++#ifdef EXT2FS_ENABLE_SWAPFS
++_INLINE_ __u32 ext2fs_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 ext2fs_swab16(__u16 val)
++{
++ __asm__("xchgb %b0,%h0" /* swap bytes */ \
++ : "=q" (val) \
++ : "0" (val)); \
++ return val;
++}
++#endif
++
++#undef EXT2FS_ADDR
++
++#endif /* i386 */
++
++#ifdef __mc68000__
++
++#define _EXT2_HAVE_ASM_BITOPS_
++
++_INLINE_ int ext2fs_set_bit(int nr,void * addr)
++{
++ char retval;
++
++ __asm__ __volatile__ ("bfset %2@{%1:#1}; sne %0"
++ : "=d" (retval) : "d" (nr^7), "a" (addr));
++
++ return retval;
++}
++
++_INLINE_ int ext2fs_clear_bit(int nr, void * addr)
++{
++ char retval;
++
++ __asm__ __volatile__ ("bfclr %2@{%1:#1}; sne %0"
++ : "=d" (retval) : "d" (nr^7), "a" (addr));
++
++ return retval;
++}
++
++_INLINE_ int ext2fs_test_bit(int nr, const void * addr)
++{
++ char retval;
++
++ __asm__ __volatile__ ("bftst %2@{%1:#1}; sne %0"
++ : "=d" (retval) : "d" (nr^7), "a" (addr));
++
++ return retval;
++}
++
++#endif /* __mc68000__ */
++
++
++#if !defined(_EXT2_HAVE_ASM_SWAB_) && defined(EXT2FS_ENABLE_SWAPFS)
++
++_INLINE_ __u16 ext2fs_swab16(__u16 val)
++{
++ return (val >> 8) | (val << 8);
++}
++
++_INLINE_ __u32 ext2fs_swab32(__u32 val)
++{
++ return ((val>>24) | ((val>>8)&0xFF00) |
++ ((val<<8)&0xFF0000) | (val<<24));
++}
++
++#endif /* !_EXT2_HAVE_ASM_SWAB */
++
++#if !defined(_EXT2_HAVE_ASM_FINDBIT_)
++_INLINE_ int ext2fs_find_first_bit_set(void * addr, unsigned size)
++{
++ char *cp = (unsigned char *) addr;
++ int res = 0, d0;
++
++ if (!size)
++ return 0;
++
++ while ((size > res) && (*cp == 0)) {
++ cp++;
++ res += 8;
++ }
++ d0 = ffs(*cp);
++ if (d0 == 0)
++ return size;
++
++ return res + d0 - 1;
++}
++
++_INLINE_ int ext2fs_find_next_bit_set (void * addr, int size, int offset)
++{
++ unsigned char * p;
++ int set = 0, bit = offset & 7, res = 0, d0;
++
++ res = offset >> 3;
++ p = ((unsigned char *) addr) + res;
++
++ if (bit) {
++ set = ffs(*p & ~((1 << bit) - 1));
++ if (set)
++ return (offset & ~7) + set - 1;
++ p++;
++ res += 8;
++ }
++ while ((size > res) && (*p == 0)) {
++ p++;
++ res += 8;
++ }
++ d0 = ffs(*p);
++ if (d0 == 0)
++ return size;
++
++ return (res + d0 - 1);
++}
++#endif
++
++_INLINE_ int ext2fs_test_generic_bitmap(ext2fs_generic_bitmap bitmap,
++ blk_t bitno);
++
++_INLINE_ 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);
++}
++
++_INLINE_ int ext2fs_mark_block_bitmap(ext2fs_block_bitmap bitmap,
++ blk_t block)
++{
++ return ext2fs_mark_generic_bitmap((ext2fs_generic_bitmap)
++ bitmap,
++ block);
++}
++
++_INLINE_ int ext2fs_unmark_block_bitmap(ext2fs_block_bitmap bitmap,
++ blk_t block)
++{
++ return ext2fs_unmark_generic_bitmap((ext2fs_generic_bitmap) bitmap,
++ block);
++}
++
++_INLINE_ int ext2fs_test_block_bitmap(ext2fs_block_bitmap bitmap,
++ blk_t block)
++{
++ return ext2fs_test_generic_bitmap((ext2fs_generic_bitmap) bitmap,
++ block);
++}
++
++_INLINE_ int ext2fs_mark_inode_bitmap(ext2fs_inode_bitmap bitmap,
++ ext2_ino_t inode)
++{
++ return ext2fs_mark_generic_bitmap((ext2fs_generic_bitmap) bitmap,
++ inode);
++}
++
++_INLINE_ int ext2fs_unmark_inode_bitmap(ext2fs_inode_bitmap bitmap,
++ ext2_ino_t inode)
++{
++ return ext2fs_unmark_generic_bitmap((ext2fs_generic_bitmap) bitmap,
++ inode);
++}
++
++_INLINE_ int ext2fs_test_inode_bitmap(ext2fs_inode_bitmap bitmap,
++ ext2_ino_t inode)
++{
++ return ext2fs_test_generic_bitmap((ext2fs_generic_bitmap) bitmap,
++ inode);
++}
++
++_INLINE_ void ext2fs_fast_mark_block_bitmap(ext2fs_block_bitmap bitmap,
++ blk_t block)
++{
++#ifdef EXT2FS_DEBUG_FAST_OPS
++ if ((block < bitmap->start) || (block > bitmap->end)) {
++ ext2fs_warn_bitmap(EXT2_ET_BAD_BLOCK_MARK, block,
++ bitmap->description);
++ return;
++ }
++#endif
++ ext2fs_set_bit(block - bitmap->start, bitmap->bitmap);
++}
++
++_INLINE_ void ext2fs_fast_unmark_block_bitmap(ext2fs_block_bitmap bitmap,
++ blk_t block)
++{
++#ifdef EXT2FS_DEBUG_FAST_OPS
++ if ((block < bitmap->start) || (block > bitmap->end)) {
++ ext2fs_warn_bitmap(EXT2_ET_BAD_BLOCK_UNMARK,
++ block, bitmap->description);
++ return;
++ }
++#endif
++ ext2fs_clear_bit(block - bitmap->start, bitmap->bitmap);
++}
++
++_INLINE_ int ext2fs_fast_test_block_bitmap(ext2fs_block_bitmap bitmap,
++ blk_t block)
++{
++#ifdef EXT2FS_DEBUG_FAST_OPS
++ if ((block < bitmap->start) || (block > bitmap->end)) {
++ ext2fs_warn_bitmap(EXT2_ET_BAD_BLOCK_TEST,
++ block, bitmap->description);
++ return 0;
++ }
++#endif
++ return ext2fs_test_bit(block - bitmap->start, bitmap->bitmap);
++}
++
++_INLINE_ void ext2fs_fast_mark_inode_bitmap(ext2fs_inode_bitmap bitmap,
++ ext2_ino_t inode)
++{
++#ifdef EXT2FS_DEBUG_FAST_OPS
++ if ((inode < bitmap->start) || (inode > bitmap->end)) {
++ ext2fs_warn_bitmap(EXT2_ET_BAD_INODE_MARK,
++ inode, bitmap->description);
++ return;
++ }
++#endif
++ ext2fs_set_bit(inode - bitmap->start, bitmap->bitmap);
++}
++
++_INLINE_ void ext2fs_fast_unmark_inode_bitmap(ext2fs_inode_bitmap bitmap,
++ ext2_ino_t inode)
++{
++#ifdef EXT2FS_DEBUG_FAST_OPS
++ if ((inode < bitmap->start) || (inode > bitmap->end)) {
++ ext2fs_warn_bitmap(EXT2_ET_BAD_INODE_UNMARK,
++ inode, bitmap->description);
++ return;
++ }
++#endif
++ ext2fs_clear_bit(inode - bitmap->start, bitmap->bitmap);
++}
++
++_INLINE_ int ext2fs_fast_test_inode_bitmap(ext2fs_inode_bitmap bitmap,
++ ext2_ino_t inode)
++{
++#ifdef EXT2FS_DEBUG_FAST_OPS
++ if ((inode < bitmap->start) || (inode > bitmap->end)) {
++ ext2fs_warn_bitmap(EXT2_ET_BAD_INODE_TEST,
++ inode, bitmap->description);
++ return 0;
++ }
++#endif
++ return ext2fs_test_bit(inode - bitmap->start, bitmap->bitmap);
++}
++
++_INLINE_ blk_t ext2fs_get_block_bitmap_start(ext2fs_block_bitmap bitmap)
++{
++ return bitmap->start;
++}
++
++_INLINE_ ext2_ino_t ext2fs_get_inode_bitmap_start(ext2fs_inode_bitmap bitmap)
++{
++ return bitmap->start;
++}
++
++_INLINE_ blk_t ext2fs_get_block_bitmap_end(ext2fs_block_bitmap bitmap)
++{
++ return bitmap->end;
++}
++
++_INLINE_ ext2_ino_t ext2fs_get_inode_bitmap_end(ext2fs_inode_bitmap bitmap)
++{
++ return bitmap->end;
++}
++
++_INLINE_ 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;
++}
++
++_INLINE_ int ext2fs_fast_test_block_bitmap_range(ext2fs_block_bitmap bitmap,
++ blk_t block, int num)
++{
++ int i;
++
++#ifdef EXT2FS_DEBUG_FAST_OPS
++ if ((block < bitmap->start) || (block+num-1 > bitmap->end)) {
++ ext2fs_warn_bitmap(EXT2_ET_BAD_BLOCK_TEST,
++ block, bitmap->description);
++ return 0;
++ }
++#endif
++ for (i=0; i < num; i++) {
++ if (ext2fs_fast_test_block_bitmap(bitmap, block+i))
++ return 0;
++ }
++ return 1;
++}
++
++_INLINE_ 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);
++}
++
++_INLINE_ void ext2fs_fast_mark_block_bitmap_range(ext2fs_block_bitmap bitmap,
++ blk_t block, int num)
++{
++ int i;
++
++#ifdef EXT2FS_DEBUG_FAST_OPS
++ if ((block < bitmap->start) || (block+num-1 > bitmap->end)) {
++ ext2fs_warn_bitmap(EXT2_ET_BAD_BLOCK_MARK, block,
++ bitmap->description);
++ return;
++ }
++#endif
++ for (i=0; i < num; i++)
++ ext2fs_set_bit(block + i - bitmap->start, bitmap->bitmap);
++}
++
++_INLINE_ 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);
++}
++
++_INLINE_ void ext2fs_fast_unmark_block_bitmap_range(ext2fs_block_bitmap bitmap,
++ blk_t block, int num)
++{
++ int i;
++
++#ifdef EXT2FS_DEBUG_FAST_OPS
++ if ((block < bitmap->start) || (block+num-1 > bitmap->end)) {
++ ext2fs_warn_bitmap(EXT2_ET_BAD_BLOCK_UNMARK, block,
++ bitmap->description);
++ return;
++ }
++#endif
++ for (i=0; i < num; i++)
++ ext2fs_clear_bit(block + i - bitmap->start, bitmap->bitmap);
++}
++#undef _INLINE_
++#endif
++
+diff -Nur busybox-1.00/e2fsprogs/ext2fs/block.c busybox/e2fsprogs/ext2fs/block.c
+--- busybox-1.00/e2fsprogs/ext2fs/block.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/block.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,437 @@
++/*
++ * 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 <stdio.h>
++#include <string.h>
++#if HAVE_UNISTD_H
++#include <unistd.h>
++#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 -Nur busybox-1.00/e2fsprogs/ext2fs/bmap.c busybox/e2fsprogs/ext2fs/bmap.c
+--- busybox-1.00/e2fsprogs/ext2fs/bmap.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/bmap.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,270 @@
++/*
++ * 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 <stdio.h>
++#include <string.h>
++#if HAVE_UNISTD_H
++#include <unistd.h>
++#endif
++
++#include "ext2_fs.h"
++#include "ext2fs.h"
++
++#if defined(__GNUC__) && !defined(NO_INLINE_FUNCS)
++#define _BMAP_INLINE_ __inline__
++#else
++#define _BMAP_INLINE_
++#endif
++
++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 _BMAP_INLINE_ 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;
++#ifdef EXT2FS_ENABLE_SWAPFS
++ 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];
++
++#ifdef EXT2FS_ENABLE_SWAPFS
++ 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;
++
++#ifdef EXT2FS_ENABLE_SWAPFS
++ 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 _BMAP_INLINE_ 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 _BMAP_INLINE_ 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 = 0;
++ 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;
++#ifdef EXT2FS_ENABLE_SWAPFS
++ 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:
++ if (buf)
++ 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 -Nur busybox-1.00/e2fsprogs/ext2fs/bmove.c busybox/e2fsprogs/ext2fs/bmove.c
+--- busybox-1.00/e2fsprogs/ext2fs/bmove.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/bmove.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,160 @@
++/*
++ * 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 <stdio.h>
++#include <string.h>
++#if HAVE_UNISTD_H
++#include <unistd.h>
++#endif
++#if HAVE_SYS_TYPES_H
++#include <sys/types.h>
++#endif
++#if HAVE_SYS_TIME_H
++#include <sys/time.h>
++#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) {
++ if (fs->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 -Nur busybox-1.00/e2fsprogs/ext2fs/brel.h busybox/e2fsprogs/ext2fs/brel.h
+--- busybox-1.00/e2fsprogs/ext2fs/brel.h 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/brel.h 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,86 @@
++/*
++ * 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 -Nur busybox-1.00/e2fsprogs/ext2fs/brel_ma.c busybox/e2fsprogs/ext2fs/brel_ma.c
+--- busybox-1.00/e2fsprogs/ext2fs/brel_ma.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/brel_ma.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,197 @@
++/*
++ * 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 <fcntl.h>
++#include <stdio.h>
++#include <string.h>
++#if HAVE_UNISTD_H
++#include <unistd.h>
++#endif
++#if HAVE_ERRNO_H
++#include <errno.h>
++#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) {
++ if (ma->entries)
++ ext2fs_free_mem(&ma->entries);
++ ext2fs_free_mem(&ma);
++ }
++ if (brel->name)
++ ext2fs_free_mem(&brel->name);
++ ext2fs_free_mem(&brel);
++ return 0;
++}
+diff -Nur busybox-1.00/e2fsprogs/ext2fs/check_desc.c busybox/e2fsprogs/ext2fs/check_desc.c
+--- busybox-1.00/e2fsprogs/ext2fs/check_desc.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/check_desc.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,68 @@
++/*
++ * 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 <stdio.h>
++#include <string.h>
++#if HAVE_UNISTD_H
++#include <unistd.h>
++#endif
++#include <fcntl.h>
++#include <time.h>
++#if HAVE_SYS_STAT_H
++#include <sys/stat.h>
++#endif
++#if HAVE_SYS_TYPES_H
++#include <sys/types.h>
++#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 -Nur busybox-1.00/e2fsprogs/ext2fs/closefs.c busybox/e2fsprogs/ext2fs/closefs.c
+--- busybox-1.00/e2fsprogs/ext2fs/closefs.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/closefs.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,381 @@
++/*
++ * 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 <stdio.h>
++#if HAVE_UNISTD_H
++#include <unistd.h>
++#endif
++#include <time.h>
++#include <string.h>
++
++#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);
++#if 0
++ printf("Writing %d bytes starting at %d\n",
++ size, write_idx*2);
++#endif
++ 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;
++#ifdef EXT2FS_ENABLE_SWAPFS
++ 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,j;
++ blk_t group_block;
++ errcode_t retval;
++ unsigned long fs_state;
++ struct ext2_super_block *super_shadow = 0;
++ struct ext2_group_desc *group_shadow = 0;
++ struct ext2_group_desc *s, *t;
++ char *group_ptr;
++ int old_desc_blocks;
++
++ 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;
++#ifdef EXT2FS_ENABLE_SWAPFS
++ 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;
++#ifdef EXT2FS_ENABLE_SWAPFS
++ 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;
++#ifdef EXT2FS_ENABLE_SWAPFS
++ 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 -Nur busybox-1.00/e2fsprogs/ext2fs/cmp_bitmaps.c busybox/e2fsprogs/ext2fs/cmp_bitmaps.c
+--- busybox-1.00/e2fsprogs/ext2fs/cmp_bitmaps.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/cmp_bitmaps.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,72 @@
++/*
++ * 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 <stdio.h>
++#include <string.h>
++#if HAVE_UNISTD_H
++#include <unistd.h>
++#endif
++#include <fcntl.h>
++#include <time.h>
++#if HAVE_SYS_STAT_H
++#include <sys/stat.h>
++#endif
++#if HAVE_SYS_TYPES_H
++#include <sys/types.h>
++#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 -Nur busybox-1.00/e2fsprogs/ext2fs/dblist.c busybox/e2fsprogs/ext2fs/dblist.c
+--- busybox-1.00/e2fsprogs/ext2fs/dblist.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/dblist.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,260 @@
++/*
++ * 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 <stdio.h>
++#if HAVE_UNISTD_H
++#include <unistd.h>
++#endif
++#include <string.h>
++#include <time.h>
++
++#include "ext2_fs.h"
++#include "ext2fsP.h"
++
++static EXT2_QSORT_TYPE 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:
++ if (dblist)
++ 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,
++ EXT2_QSORT_TYPE (*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 EXT2_QSORT_TYPE 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 -Nur busybox-1.00/e2fsprogs/ext2fs/dblist_dir.c busybox/e2fsprogs/ext2fs/dblist_dir.c
+--- busybox-1.00/e2fsprogs/ext2fs/dblist_dir.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/dblist_dir.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,75 @@
++/*
++ * 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 <stdio.h>
++#if HAVE_UNISTD_H
++#include <unistd.h>
++#endif
++#include <string.h>
++#include <time.h>
++
++#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 -Nur busybox-1.00/e2fsprogs/ext2fs/dir_iterate.c busybox/e2fsprogs/ext2fs/dir_iterate.c
+--- busybox-1.00/e2fsprogs/ext2fs/dir_iterate.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/dir_iterate.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,219 @@
++/*
++ * 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 <stdio.h>
++#include <string.h>
++#if HAVE_UNISTD_H
++#include <unistd.h>
++#endif
++#if HAVE_ERRNO_H
++#include <errno.h>
++#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 -Nur busybox-1.00/e2fsprogs/ext2fs/dirblock.c busybox/e2fsprogs/ext2fs/dirblock.c
+--- busybox-1.00/e2fsprogs/ext2fs/dirblock.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/dirblock.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,130 @@
++/*
++ * 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 <stdio.h>
++#if HAVE_UNISTD_H
++#include <unistd.h>
++#endif
++#include <string.h>
++#include <time.h>
++
++#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, do_swap;
++
++
++ retval = io_channel_read_blk(fs->io, block, 1, buf);
++ if (retval)
++ return retval;
++#ifdef EXT2FS_ENABLE_SWAPFS
++ 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;
++#ifdef EXT2FS_ENABLE_SWAPFS
++ 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)))
++{
++#ifdef EXT2FS_ENABLE_SWAPFS
++ int do_swap = 0;
++ errcode_t retval;
++ char *p, *end;
++ char *buf = 0;
++ 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 -Nur busybox-1.00/e2fsprogs/ext2fs/dirhash.c busybox/e2fsprogs/ext2fs/dirhash.c
+--- busybox-1.00/e2fsprogs/ext2fs/dirhash.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/dirhash.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,233 @@
++/*
++ * 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 <stdio.h>
++#include <string.h>
++
++#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 <jeremy@zip.com.au> 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 -Nur busybox-1.00/e2fsprogs/ext2fs/dosio.c busybox/e2fsprogs/ext2fs/dosio.c
+--- busybox-1.00/e2fsprogs/ext2fs/dosio.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/dosio.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,456 @@
++/*
++ * dosio.c -- Disk I/O module for the ext2fs/DOS library.
++ *
++ * Copyright (c) 1997 by Theodore Ts'o.
++ *
++ * Copyright (c) 1997 Mark Habersack
++ * This file may be distributed under the terms of the GNU Public License.
++ *
++ */
++
++#include <stdio.h>
++#include <bios.h>
++#include <string.h>
++#include <ctype.h>
++#include <io.h>
++#ifdef HAVE_ERRNO_H
++#include <errno.h>
++#endif
++
++#include <ext2fs/ext2_types.h>
++#include "utils.h"
++#include "dosio.h"
++#include "et/com_err.h"
++#include "ext2_err.h"
++#include "ext2fs/io.h"
++
++/*
++ * Some helper macros
++ */
++#define LINUX_EXT2FS 0x83
++#define LINUX_SWAP 0x82
++#define WRITE_ERR(_msg_) write(2, _msg_, strlen(_msg_))
++#define WRITE_ERR_S(_msg_) write(2, _msg_, sizeof(_msg_))
++
++/*
++ * Exported variables
++ */
++unsigned long _dio_error;
++unsigned long _dio_hw_error;
++
++/*
++ * Array of all opened partitions
++ */
++static PARTITION **partitions = NULL;
++static unsigned short npart = 0; /* Number of mapped partitions */
++static PARTITION *active = NULL;
++
++/*
++ * I/O Manager routine prototypes
++ */
++static errcode_t dos_open(const char *dev, int flags, io_channel *channel);
++static errcode_t dos_close(io_channel channel);
++static errcode_t dos_set_blksize(io_channel channel, int blksize);
++static errcode_t dos_read_blk(io_channel channel, unsigned long block,
++ int count, void *buf);
++static errcode_t dos_write_blk(io_channel channel, unsigned long block,
++ int count, const void *buf);
++static errcode_t dos_flush(io_channel channel);
++
++static struct struct_io_manager struct_dos_manager = {
++ EXT2_ET_MAGIC_IO_MANAGER,
++ "DOS I/O Manager",
++ dos_open,
++ dos_close,
++ dos_set_blksize,
++ dos_read_blk,
++ dos_write_blk,
++ dos_flush
++};
++io_manager dos_io_manager = &struct_dos_manager;
++
++/*
++ * Macro taken from unix_io.c
++ */
++/*
++ * For checking structure magic numbers...
++ */
++
++#define EXT2_CHECK_MAGIC(struct, code) \
++ if ((struct)->magic != (code)) return (code)
++
++/*
++ * Calculates a CHS address of a sector from its LBA
++ * offset for the given partition.
++ */
++static void lba2chs(unsigned long lba_addr, CHS *chs, PARTITION *part)
++{
++ unsigned long abss;
++
++ chs->offset = lba_addr & 0x000001FF;
++ abss = (lba_addr >> 9) + part->start;
++ chs->cyl = abss / (part->sects * part->heads);
++ chs->head = (abss / part->sects) % part->heads;
++ chs->sector = (abss % part->sects) + 1;
++}
++
++#ifdef __TURBOC__
++#pragma argsused
++#endif
++/*
++ * Scans the passed partition table looking for *pno partition
++ * that has LINUX_EXT2FS type.
++ *
++ * TODO:
++ * For partition numbers >5 Linux uses DOS extended partitions -
++ * dive into them an return an appropriate entry. Also dive into
++ * extended partitions when scanning for a first Linux/ext2fs.
++ */
++static PTABLE_ENTRY *scan_partition_table(PTABLE_ENTRY *pentry,
++ unsigned short phys,
++ unsigned char *pno)
++{
++ unsigned i;
++
++ if(*pno != 0xFF && *pno >= 5)
++ return NULL; /* We don't support extended partitions for now */
++
++ if(*pno != 0xFF)
++ {
++ if(pentry[*pno].type == LINUX_EXT2FS)
++ return &pentry[*pno];
++ else
++ {
++ if(!pentry[*pno].type)
++ *pno = 0xFE;
++ else if(pentry[*pno].type == LINUX_SWAP)
++ *pno = 0xFD;
++ return NULL;
++ }
++ }
++
++ for(i = 0; i < 4; i++)
++ if(pentry[i].type == LINUX_EXT2FS)
++ {
++ *pno = i;
++ return &pentry[i];
++ }
++
++ return NULL;
++}
++
++/*
++ * Allocate libext2fs structures associated with I/O manager
++ */
++static io_channel alloc_io_channel(PARTITION *part)
++{
++ io_channel ioch;
++
++ ioch = (io_channel)malloc(sizeof(struct struct_io_channel));
++ if (!ioch)
++ return NULL;
++ memset(ioch, 0, sizeof(struct struct_io_channel));
++ ioch->magic = EXT2_ET_MAGIC_IO_CHANNEL;
++ ioch->manager = dos_io_manager;
++ ioch->name = (char *)malloc(strlen(part->dev)+1);
++ if (!ioch->name) {
++ free(ioch);
++ return NULL;
++ }
++ strcpy(ioch->name, part->dev);
++ ioch->private_data = part;
++ ioch->block_size = 1024; /* The smallest ext2fs block size */
++ ioch->read_error = 0;
++ ioch->write_error = 0;
++
++ return ioch;
++}
++
++#ifdef __TURBOC__
++#pragma argsused
++#endif
++/*
++ * Open the 'name' partition, initialize all information structures
++ * we need to keep and create libext2fs I/O manager.
++ */
++static errcode_t dos_open(const char *dev, int flags, io_channel *channel)
++{
++ unsigned char *tmp, sec[512];
++ PARTITION *part;
++ PTABLE_ENTRY *pent;
++ PARTITION **newparts;
++
++ if(!dev)
++ {
++ _dio_error = ERR_BADDEV;
++ return EXT2_ET_BAD_DEVICE_NAME;
++ }
++
++ /*
++ * First check whether the dev name is OK
++ */
++ tmp = (unsigned char*)strrchr(dev, '/');
++ if(!tmp)
++ {
++ _dio_error = ERR_BADDEV;
++ return EXT2_ET_BAD_DEVICE_NAME;
++ }
++ *tmp = 0;
++ if(strcmp(dev, "/dev"))
++ {
++ _dio_error = ERR_BADDEV;
++ return EXT2_ET_BAD_DEVICE_NAME;
++ }
++ *tmp++ = '/';
++
++ /*
++ * Check whether the partition data is already in cache
++ */
++
++ part = (PARTITION*)malloc(sizeof(PARTITION));
++ if (!part)
++ return ENOMEM;
++ {
++ int i = 0;
++
++ for(;i < npart; i++)
++ if(!strcmp(partitions[i]->dev, dev))
++ {
++ /* Found it! Make it the active one */
++ active = partitions[i];
++ *channel = alloc_io_channel(active);
++ if (!*channel)
++ return ENOMEM;
++ return 0;
++ }
++ }
++
++ /*
++ * Drive number & optionally partn number
++ */
++ switch(tmp[0])
++ {
++ case 'h':
++ case 's':
++ part->phys = 0x80;
++ part->phys += toupper(tmp[2]) - 'A';
++ /*
++ * Do we have the partition number?
++ */
++ if(tmp[3])
++ part->pno = isdigit((int)tmp[3]) ? tmp[3] - '0' - 1: 0;
++ else
++ part->pno = 0xFF;
++ break;
++
++ case 'f':
++ if(tmp[2])
++ part->phys = isdigit((int)tmp[2]) ? tmp[2] - '0' : 0;
++ else
++ part->phys = 0x00; /* We'll assume /dev/fd0 */
++ break;
++
++ default:
++ _dio_error = ERR_BADDEV;
++ return ENODEV;
++ }
++
++ if(part->phys < 0x80)
++ {
++ /* We don't support floppies for now */
++ _dio_error = ERR_NOTSUPP;
++ return EINVAL;
++ }
++
++ part->dev = strdup(dev);
++
++ /*
++ * Get drive's geometry
++ */
++ _dio_hw_error = biosdisk(DISK_GET_GEOMETRY,
++ part->phys,
++ 0, /* head */
++ 0, /* cylinder */
++ 1, /* sector */
++ 1, /* just one sector */
++ sec);
++
++ if(!HW_OK())
++ {
++ _dio_error = ERR_HARDWARE;
++ if (part)
++ free(part);
++ return EFAULT;
++ }
++
++ /*
++ * Calculate the geometry
++ */
++ part->cyls = (unsigned short)(((sec[0] >> 6) << 8) + sec[1] + 1);
++ part->heads = sec[3] + 1;
++ part->sects = sec[0] & 0x3F;
++
++ /*
++ * Now that we know all we need, let's look for the partition
++ */
++ _dio_hw_error = biosdisk(DISK_READ, part->phys, 0, 0, 1, 1, sec);
++
++ if(!HW_OK())
++ {
++ _dio_error = ERR_HARDWARE;
++ if (part)
++ free(part);
++ return EFAULT;
++ }
++
++ pent = (PTABLE_ENTRY*)&sec[0x1BE];
++ pent = scan_partition_table(pent, part->phys, &part->pno);
++
++ if(!pent)
++ {
++ _dio_error = part->pno == 0xFE ? ERR_EMPTYPART :
++ part->pno == 0xFD ? ERR_LINUXSWAP : ERR_NOTEXT2FS;
++ if (part)
++ free(part);
++ return ENODEV;
++ }
++
++ /*
++ * Calculate the remaining figures
++ */
++ {
++ unsigned long fsec, fhead, fcyl;
++
++ fsec = (unsigned long)(pent->start_sec & 0x3F);
++ fhead = (unsigned long)pent->start_head;
++ fcyl = ((pent->start_sec >> 6) << 8) + pent->start_cyl;
++ part->start = fsec + fhead * part->sects + fcyl *
++ (part->heads * part->sects) - 1;
++ part->len = pent->size;
++ }
++
++ /*
++ * Add the partition to the table
++ */
++ newparts = (PARTITION**)realloc(partitions, sizeof(PARTITION) * npart);
++ if (!newparts) {
++ free(part);
++ return ENOMEM;
++ }
++ partitions = newparts;
++ partitions[npart++] = active = part;
++
++ /*
++ * Now alloc all libe2fs structures
++ */
++ *channel = alloc_io_channel(active);
++ if (!*channel)
++ return ENOMEM;
++
++ return 0;
++}
++
++static errcode_t dos_close(io_channel channel)
++{
++ if (channel->name)
++ free(channel->name);
++ if (channel)
++ free(channel);
++
++ return 0;
++}
++
++static errcode_t dos_set_blksize(io_channel channel, int blksize)
++{
++ channel->block_size = blksize;
++
++ return 0;
++}
++
++static errcode_t dos_read_blk(io_channel channel, unsigned long block,
++ int count, void *buf)
++{
++ PARTITION *part;
++ size_t size;
++ ext2_loff_t loc;
++ CHS chs;
++
++ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
++ part = (PARTITION*)channel->private_data;
++
++ size = (size_t)((count < 0) ? -count : count * channel->block_size);
++ loc = (ext2_loff_t) block * channel->block_size;
++
++ lba2chs(loc, &chs, part);
++ /*
++ * Potential bug here:
++ * If DJGPP is used then reads of >18 sectors will fail!
++ * Have to rewrite biosdisk.
++ */
++ _dio_hw_error = biosdisk(DISK_READ,
++ part->phys,
++ chs.head,
++ chs.cyl,
++ chs.sector,
++ size < 512 ? 1 : size/512,
++ buf);
++
++ if(!HW_OK())
++ {
++ _dio_error = ERR_HARDWARE;
++ return EFAULT;
++ }
++
++ return 0;
++}
++
++static errcode_t dos_write_blk(io_channel channel, unsigned long block,
++ int count, const void *buf)
++{
++ PARTITION *part;
++ size_t size;
++ ext2_loff_t loc;
++ CHS chs;
++
++ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
++ part = (PARTITION*)channel->private_data;
++
++ if(count == 1)
++ size = (size_t)channel->block_size;
++ else
++ {
++ if (count < 0)
++ size = (size_t)-count;
++ else
++ size = (size_t)(count * channel->block_size);
++ }
++
++ loc = (ext2_loff_t)block * channel->block_size;
++ lba2chs(loc, &chs, part);
++ _dio_hw_error = biosdisk(DISK_WRITE,
++ part->phys,
++ chs.head,
++ chs.cyl,
++ chs.sector,
++ size < 512 ? 1 : size/512,
++ (void*)buf);
++
++ if(!HW_OK())
++ {
++ _dio_error = ERR_HARDWARE;
++ return EFAULT;
++ }
++
++ return 0;
++}
++
++#ifdef __TURBOC__
++#pragma argsused
++#endif
++static errcode_t dos_flush(io_channel channel)
++{
++ /*
++ * No buffers, no flush...
++ */
++ return 0;
++}
+diff -Nur busybox-1.00/e2fsprogs/ext2fs/dosio.h busybox/e2fsprogs/ext2fs/dosio.h
+--- busybox-1.00/e2fsprogs/ext2fs/dosio.h 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/dosio.h 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,153 @@
++/*
++ * v1.0
++ *
++ * Disk I/O include file for the ext2fs/DOS library.
++ *
++ * Copyright (c) 1997 Mark Habersack
++ * This file may be distributed under the terms of the GNU Public License.
++ *
++ */
++#ifndef __diskio_h
++#define __diskio_h
++#ifdef __TURBOC__
++#ifndef __LARGE__
++# error "ext2fs/DOS library requires LARGE model!"
++#endif
++#endif
++
++#ifdef __TURBOC__
++#include "msdos.h"
++#endif
++
++/*
++ * A helper structure used in LBA => CHS conversion
++ */
++typedef struct
++{
++ unsigned short cyl; /* Cylinder (or track) */
++ unsigned short head;
++ unsigned short sector;
++ unsigned short offset; /* Offset of byte within the sector */
++} CHS;
++
++/*
++ * All partition data we need is here
++ */
++typedef struct
++{
++ char *dev; /* _Linux_ device name (like "/dev/hda1") */
++ unsigned char phys; /* Physical DOS drive number */
++ unsigned long start; /* LBA address of partition start */
++ unsigned long len; /* length of partition in sectors */
++ unsigned char pno; /* Partition number (read from *dev) */
++
++ /* This partition's drive geometry */
++ unsigned short cyls;
++ unsigned short heads;
++ unsigned short sects;
++} PARTITION;
++
++/*
++ * PC partition table entry format
++ */
++#ifdef __DJGPP__
++#pragma pack(1)
++#endif
++typedef struct
++{
++ unsigned char active;
++ unsigned char start_head;
++ unsigned char start_sec;
++ unsigned char start_cyl;
++ unsigned char type;
++ unsigned char end_head;
++ unsigned char end_sec;
++ unsigned char end_cyl;
++ unsigned long first_sec_rel;
++ unsigned long size;
++} PTABLE_ENTRY;
++#ifdef __DJGPP__
++#pragma pack()
++#endif
++
++/*
++ * INT 0x13 operation codes
++ */
++#define DISK_READ 0x02
++#define DISK_WRITE 0x03
++#define DISK_GET_GEOMETRY 0x08
++#define DISK_READY 0x10
++
++/*
++ * Errors to put in _dio_error
++ */
++#define ERR_BADDEV 0x00000001L
++#define ERR_HARDWARE 0x00000002L
++#define ERR_NOTSUPP 0x00000003L
++#define ERR_NOTEXT2FS 0x00000004L
++#define ERR_EMPTYPART 0x00000005L
++#define ERR_LINUXSWAP 0x00000006L
++
++/*
++ * Functions in diskio.c
++ */
++
++/*
++ * Variable contains last module's error
++ */
++extern unsigned long _dio_error;
++
++/*
++ * This one contains last hardware error (if _dio_error == ERR_HARDWARE)
++ */
++extern unsigned long _dio_hw_error;
++
++/*
++ * Macros to check for disk hardware errors
++ */
++#define HW_OK() ((unsigned char)_dio_hw_error == 0x00)
++#define HW_BAD_CMD() ((unsigned char)_dio_hw_error == 0x01)
++#define HW_NO_ADDR_MARK() ((unsigned char)_dio_hw_error == 0x02)
++#define HW_WRITE_PROT() ((unsigned char)_dio_hw_error == 0x03)
++#define HW_NO_SECTOR() ((unsigned char)_dio_hw_error == 0x04)
++#define HW_RESET_FAIL() ((unsigned char)_dio_hw_error == 0x05)
++#define HW_DISK_CHANGED() ((unsigned char)_dio_hw_error == 0x06)
++#define HW_DRIVE_FAIL() ((unsigned char)_dio_hw_error == 0x07)
++#define HW_DMA_OVERRUN() ((unsigned char)_dio_hw_error == 0x08)
++#define HW_DMA_BOUNDARY() ((unsigned char)_dio_hw_error == 0x09)
++#define HW_BAD_SECTOR() ((unsigned char)_dio_hw_error == 0x0A)
++#define HW_BAD_TRACK() ((unsigned char)_dio_hw_error == 0x0B)
++#define HW_UNSUPP_TRACK() ((unsigned char)_dio_hw_error == 0x0C)
++#define HW_BAD_CRC_ECC() ((unsigned char)_dio_hw_error == 0x10)
++#define HW_CRC_ECC_CORR() ((unsigned char)_dio_hw_error == 0x11)
++#define HW_CONTR_FAIL() ((unsigned char)_dio_hw_error == 0x20)
++#define HW_SEEK_FAIL() ((unsigned char)_dio_hw_error == 0x40)
++#define HW_ATTACH_FAIL() ((unsigned char)_dio_hw_error == 0x80)
++#define HW_DRIVE_NREADY() ((unsigned char)_dio_hw_error == 0xAA)
++#define HW_UNDEF_ERROR() ((unsigned char)_dio_hw_error == 0xBB)
++#define HW_WRITE_FAULT() ((unsigned char)_dio_hw_error == 0xCC)
++#define HW_STATUS_ERROR() ((unsigned char)_dio_hw_error == 0xE0)
++#define HW_SENSE_FAIL() ((unsigned char)_dio_hw_error == 0xFF)
++
++
++/*
++ * Open the specified partition.
++ * String 'dev' must have a format:
++ *
++ * /dev/{sd|hd|fd}[X]
++ *
++ * where,
++ *
++ * only one of the option in curly braces can be used and X is an optional
++ * partition number for the given device. If X is not specified, function
++ * scans the drive's partition table in search for the first Linux ext2fs
++ * partition (signature 0x83). Along the way it dives into every extended
++ * partition encountered.
++ * Scan ends if either (a) there are no more used partition entries, or
++ * (b) there is no Xth partition.
++ *
++ * Routine returns 0 on success and !=0 otherwise.
++ */
++int open_partition(char *dev);
++
++#endif /* __diskio_h */
+diff -Nur busybox-1.00/e2fsprogs/ext2fs/dupfs.c busybox/e2fsprogs/ext2fs/dupfs.c
+--- busybox-1.00/e2fsprogs/ext2fs/dupfs.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/dupfs.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,96 @@
++/*
++ * 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 <stdio.h>
++#if HAVE_UNISTD_H
++#include <unistd.h>
++#endif
++#include <time.h>
++#include <string.h>
++
++#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 -Nur busybox-1.00/e2fsprogs/ext2fs/e2image.h busybox/e2fsprogs/ext2fs/e2image.h
+--- busybox-1.00/e2fsprogs/ext2fs/e2image.h 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/e2image.h 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,51 @@
++/*
++ * 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 -Nur busybox-1.00/e2fsprogs/ext2fs/expanddir.c busybox/e2fsprogs/ext2fs/expanddir.c
+--- busybox-1.00/e2fsprogs/ext2fs/expanddir.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/expanddir.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,126 @@
++/*
++ * 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 <stdio.h>
++#include <string.h>
++#if HAVE_UNISTD_H
++#include <unistd.h>
++#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 -Nur busybox-1.00/e2fsprogs/ext2fs/ext2_err.h busybox/e2fsprogs/ext2fs/ext2_err.h
+--- busybox-1.00/e2fsprogs/ext2fs/ext2_err.h 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/ext2_err.h 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,117 @@
++/*
++ * ext2_err.h:
++ * This file is automatically generated; please do not edit it.
++ */
++
++#include <et/com_err.h>
++
++#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 -Nur busybox-1.00/e2fsprogs/ext2fs/ext2_ext_attr.h busybox/e2fsprogs/ext2fs/ext2_ext_attr.h
+--- busybox-1.00/e2fsprogs/ext2fs/ext2_ext_attr.h 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/ext2_ext_attr.h 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,69 @@
++/*
++ File: linux/ext2_ext_attr.h
++
++ On-disk format of extended attributes for the ext2 filesystem.
++
++ (C) 2000 Andreas Gruenbacher, <a.gruenbacher@computer.org>
++*/
++
++/* 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 */
++#if 0
++ char e_name[0]; /* attribute name */
++#endif
++};
++
++#define EXT2_EXT_ATTR_PAD_BITS 2
++#define EXT2_EXT_ATTR_PAD (1<<EXT2_EXT_ATTR_PAD_BITS)
++#define EXT2_EXT_ATTR_ROUND (EXT2_EXT_ATTR_PAD-1)
++#define EXT2_EXT_ATTR_LEN(name_len) \
++ (((name_len) + EXT2_EXT_ATTR_ROUND + \
++ sizeof(struct ext2_ext_attr_entry)) & ~EXT2_EXT_ATTR_ROUND)
++#define EXT2_EXT_ATTR_NEXT(entry) \
++ ( (struct ext2_ext_attr_entry *)( \
++ (char *)(entry) + EXT2_EXT_ATTR_LEN((entry)->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)
++
++#ifdef __KERNEL__
++# ifdef CONFIG_EXT2_FS_EXT_ATTR
++extern int ext2_get_ext_attr(struct inode *, const char *, char *, size_t, int);
++extern int ext2_set_ext_attr(struct inode *, const char *, char *, size_t, int);
++extern void ext2_ext_attr_free_inode(struct inode *inode);
++extern void ext2_ext_attr_put_super(struct super_block *sb);
++extern int ext2_ext_attr_init(void);
++extern void ext2_ext_attr_done(void);
++# else
++# define ext2_get_ext_attr NULL
++# define ext2_set_ext_attr NULL
++# endif
++#endif /* __KERNEL__ */
++
+diff -Nur busybox-1.00/e2fsprogs/ext2fs/ext2_fs.h busybox/e2fsprogs/ext2fs/ext2_fs.h
+--- busybox-1.00/e2fsprogs/ext2fs/ext2_fs.h 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/ext2_fs.h 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,644 @@
++/*
++ * 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
++
++#include <ext2fs/ext2_types.h> /* Changed from linux/types.h */
++
++/*
++ * The second extended filesystem constants/structures
++ */
++
++/*
++ * Define EXT2FS_DEBUG to produce debug messages
++ */
++#undef EXT2FS_DEBUG
++
++/*
++ * Define EXT2_PREALLOCATE to preallocate data blocks for expanding files
++ */
++#define EXT2_PREALLOCATE
++#define EXT2_DEFAULT_PREALLOC_BLOCKS 8
++
++/*
++ * The second extended file system version
++ */
++#define EXT2FS_DATE "95/08/09"
++#define EXT2FS_VERSION "0.5b"
++
++/*
++ * 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
++
++#ifdef __KERNEL__
++#define EXT2_SB(sb) (&((sb)->u.ext2_sb))
++#else
++/* 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)
++#endif
++
++/*
++ * 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)
++#ifdef __KERNEL__
++#define EXT2_BLOCK_SIZE(s) ((s)->s_blocksize)
++#define EXT2_BLOCK_SIZE_BITS(s) ((s)->s_blocksize_bits)
++#define EXT2_ADDR_PER_BLOCK_BITS(s) (EXT2_SB(s)->addr_per_block_bits)
++#define EXT2_INODE_SIZE(s) (EXT2_SB(s)->s_inode_size)
++#define EXT2_FIRST_INO(s) (EXT2_SB(s)->s_first_ino)
++#else
++#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)
++#endif
++#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
++#ifdef __KERNEL__
++# define EXT2_FRAG_SIZE(s) (EXT2_SB(s)->s_frag_size)
++# define EXT2_FRAGS_PER_BLOCK(s) (EXT2_SB(s)->s_frags_per_block)
++#else
++# 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))
++#endif
++
++/*
++ * 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))
++#ifdef __KERNEL__
++#define EXT2_DESC_PER_BLOCK(s) (EXT2_SB(s)->s_desc_per_block)
++#define EXT2_DESC_PER_BLOCK_BITS(s) (EXT2_SB(s)->s_desc_per_block_bits)
++#else
++#define EXT2_DESC_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof (struct ext2_group_desc))
++#endif
++
++/*
++ * 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
++
++#if defined(__KERNEL__) || defined(__linux__)
++#define i_reserved1 osd1.linux1.l_i_reserved1
++#define i_frag osd2.linux2.l_i_frag
++#define i_fsize osd2.linux2.l_i_fsize
++#define i_uid_low i_uid
++#define i_gid_low i_gid
++#define i_uid_high osd2.linux2.l_i_uid_high
++#define i_gid_high osd2.linux2.l_i_gid_high
++#define i_reserved2 osd2.linux2.l_i_reserved2
++
++#else
++#if defined(__GNU__)
++
++#define i_translator osd1.hurd1.h_i_translator
++#define i_frag osd2.hurd2.h_i_frag;
++#define i_fsize osd2.hurd2.h_i_fsize;
++#define i_uid_high osd2.hurd2.h_i_uid_high
++#define i_gid_high osd2.hurd2.h_i_gid_high
++#define i_author osd2.hurd2.h_i_author
++
++#else
++#if defined(__masix__)
++
++#define i_reserved1 osd1.masix1.m_i_reserved1
++#define i_frag osd2.masix2.m_i_frag
++#define i_fsize osd2.masix2.m_i_fsize
++#define i_reserved2 osd2.masix2.m_i_reserved2
++
++#endif /* __masix__ */
++#endif /* __GNU__ */
++#endif /* defined(__KERNEL__) || defined(__linux__) */
++
++/*
++ * 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 /* _LINUX_EXT2_FS_H */
+diff -Nur busybox-1.00/e2fsprogs/ext2fs/ext2_io.h busybox/e2fsprogs/ext2fs/ext2_io.h
+--- busybox-1.00/e2fsprogs/ext2fs/ext2_io.h 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/ext2_io.h 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,108 @@
++/*
++ * 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
++
++/*
++ * 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);
++
++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 /* _EXT2FS_EXT2_IO_H */
++
+diff -Nur busybox-1.00/e2fsprogs/ext2fs/ext2_types.h busybox/e2fsprogs/ext2fs/ext2_types.h
+--- busybox-1.00/e2fsprogs/ext2fs/ext2_types.h 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/ext2_types.h 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1 @@
++#include <linux/types.h>
+diff -Nur busybox-1.00/e2fsprogs/ext2fs/ext2fs.h busybox/e2fsprogs/ext2fs/ext2fs.h
+--- busybox-1.00/e2fsprogs/ext2fs/ext2fs.h 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/ext2fs.h 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,1137 @@
++/*
++ * 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
++
++#ifdef __GNUC__
++#define EXT2FS_ATTR(x) __attribute__(x)
++#else
++#define EXT2FS_ATTR(x)
++#endif
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++/*
++ * Non-GNU C compilers won't necessarily understand inline
++ */
++#if (!defined(__GNUC__) && !defined(__WATCOMC__))
++#define NO_INLINE_FUNCS
++#endif
++
++/*
++ * Build in support for byte-swapping filesystems if we the feature
++ * has been configured or if we're being built on a CPU architecture
++ * with a non-native byte order.
++ */
++#if defined(ENABLE_SWAPFS) || defined(WORDS_BIGENDIAN)
++#define EXT2FS_ENABLE_SWAPFS
++#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 <sys/types.h>
++#endif
++
++#include <stdio.h>
++#include <stdlib.h>
++
++#if EXT2_FLAT_INCLUDES
++#include "e2_types.h"
++#include "ext2_fs.h"
++#else
++#include <ext2fs/ext2_types.h>
++#include <ext2fs/ext2_fs.h>
++#endif /* EXT2_FLAT_INCLUDES */
++
++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;
++
++#if EXT2_FLAT_INCLUDES
++#include "com_err.h"
++#include "ext2_io.h"
++#include "ext2_err.h"
++#else
++#include <et/com_err.h>
++#include <ext2fs/ext2_io.h>
++#include <ext2fs/ext2_err.h>
++#endif
++
++/*
++ * Portability help for Microsoft Visual C++
++ */
++#ifdef _MSC_VER
++#define EXT2_QSORT_TYPE int __cdecl
++#else
++#define EXT2_QSORT_TYPE int
++#endif
++
++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;
++
++#ifdef EXT2_DYNAMIC_REV
++#define EXT2_FIRST_INODE(s) EXT2_FIRST_INO(s)
++#else
++#define EXT2_FIRST_INODE(s) EXT2_FIRST_INO
++#define EXT2_INODE_SIZE(s) sizeof(struct ext2_inode)
++#endif
++
++/*
++ * 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;
++};
++
++#if EXT2_FLAT_INCLUDES
++#include "e2_bitops.h"
++#else
++#include <ext2fs/bitops.h>
++#endif
++
++/*
++ * 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
++
++/*
++ * 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,
++ EXT2_QSORT_TYPE (*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,
++ ext2_inode_scan scan,
++ 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);
++
++/*
++ * The actual inlined functions definitions themselves...
++ *
++ * If NO_INLINE_FUNCS is defined, then we won't try to do inline
++ * functions at all!
++ */
++#if (defined(INCLUDE_INLINE_FUNCS) || !defined(NO_INLINE_FUNCS))
++#ifdef INCLUDE_INLINE_FUNCS
++#define _INLINE_ extern
++#else
++#ifdef __GNUC__
++#define _INLINE_ extern __inline__
++#else /* For Watcom C */
++#define _INLINE_ extern inline
++#endif
++#endif
++
++#ifndef EXT2_CUSTOM_MEMORY_ROUTINES
++/*
++ * Allocate memory
++ */
++_INLINE_ 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
++ */
++_INLINE_ errcode_t ext2fs_free_mem(void *ptr)
++{
++ void **pp = (void **)ptr;
++
++ free(*pp);
++ *pp = 0;
++ return 0;
++}
++
++/*
++ * Resize memory
++ */
++_INLINE_ errcode_t ext2fs_resize_mem(unsigned long EXT2FS_ATTR((unused)) old_size,
++ unsigned long size, void *ptr)
++{
++ void *p;
++ void **pp = (void **)ptr;
++
++ p = realloc(*pp, size);
++ if (!p)
++ return EXT2_ET_NO_MEMORY;
++ *pp = p;
++ return 0;
++}
++#endif /* Custom memory routines */
++
++/*
++ * Mark a filesystem superblock as dirty
++ */
++_INLINE_ void ext2fs_mark_super_dirty(ext2_filsys fs)
++{
++ fs->flags |= EXT2_FLAG_DIRTY | EXT2_FLAG_CHANGED;
++}
++
++/*
++ * Mark a filesystem as changed
++ */
++_INLINE_ void ext2fs_mark_changed(ext2_filsys fs)
++{
++ fs->flags |= EXT2_FLAG_CHANGED;
++}
++
++/*
++ * Check to see if a filesystem has changed
++ */
++_INLINE_ int ext2fs_test_changed(ext2_filsys fs)
++{
++ return (fs->flags & EXT2_FLAG_CHANGED);
++}
++
++/*
++ * Mark a filesystem as valid
++ */
++_INLINE_ void ext2fs_mark_valid(ext2_filsys fs)
++{
++ fs->flags |= EXT2_FLAG_VALID;
++}
++
++/*
++ * Mark a filesystem as NOT valid
++ */
++_INLINE_ void ext2fs_unmark_valid(ext2_filsys fs)
++{
++ fs->flags &= ~EXT2_FLAG_VALID;
++}
++
++/*
++ * Check to see if a filesystem is valid
++ */
++_INLINE_ int ext2fs_test_valid(ext2_filsys fs)
++{
++ return (fs->flags & EXT2_FLAG_VALID);
++}
++
++/*
++ * Mark the inode bitmap as dirty
++ */
++_INLINE_ void ext2fs_mark_ib_dirty(ext2_filsys fs)
++{
++ fs->flags |= EXT2_FLAG_IB_DIRTY | EXT2_FLAG_CHANGED;
++}
++
++/*
++ * Mark the block bitmap as dirty
++ */
++_INLINE_ 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
++ */
++_INLINE_ 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
++ */
++_INLINE_ int ext2fs_test_bb_dirty(ext2_filsys fs)
++{
++ return (fs->flags & EXT2_FLAG_BB_DIRTY);
++}
++
++/*
++ * Return the group # of a block
++ */
++_INLINE_ 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
++ */
++_INLINE_ int ext2fs_group_of_ino(ext2_filsys fs, ext2_ino_t ino)
++{
++ return (ino - 1) / fs->super->s_inodes_per_group;
++}
++
++_INLINE_ 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);
++}
++#undef _INLINE_
++#endif
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* _EXT2FS_EXT2FS_H */
+diff -Nur busybox-1.00/e2fsprogs/ext2fs/ext2fsP.h busybox/e2fsprogs/ext2fs/ext2fsP.h
+--- busybox-1.00/e2fsprogs/ext2fs/ext2fsP.h 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/ext2fsP.h 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,88 @@
++/*
++ * 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 -Nur busybox-1.00/e2fsprogs/ext2fs/ext_attr.c busybox/e2fsprogs/ext2fs/ext_attr.c
+--- busybox-1.00/e2fsprogs/ext2fs/ext_attr.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/ext_attr.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,105 @@
++/*
++ * ext_attr.c --- extended attribute blocks
++ *
++ * Copyright (C) 2001 Andreas Gruenbacher, <a.gruenbacher@computer.org>
++ *
++ * Copyright (C) 2002 Theodore Ts'o.
++ *
++ * %Begin-Header%
++ * This file may be redistributed under the terms of the GNU Public
++ * License.
++ * %End-Header%
++ */
++
++#include <stdio.h>
++#if HAVE_UNISTD_H
++#include <unistd.h>
++#endif
++#include <string.h>
++#include <time.h>
++
++#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;
++#ifdef EXT2FS_ENABLE_SWAPFS
++ 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;
++
++#ifdef EXT2FS_ENABLE_SWAPFS
++ if ((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
++#endif
++ 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 = 0;
++
++ 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 -Nur busybox-1.00/e2fsprogs/ext2fs/fileio.c busybox/e2fsprogs/ext2fs/fileio.c
+--- busybox-1.00/e2fsprogs/ext2fs/fileio.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/fileio.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,378 @@
++/*
++ * 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 <stdio.h>
++#include <string.h>
++#if HAVE_UNISTD_H
++#include <unistd.h>
++#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:
++ if (file->buf)
++ 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);
++
++ if (file->buf)
++ 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 -Nur busybox-1.00/e2fsprogs/ext2fs/finddev.c busybox/e2fsprogs/ext2fs/finddev.c
+--- busybox-1.00/e2fsprogs/ext2fs/finddev.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/finddev.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,208 @@
++/*
++ * 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 <stdio.h>
++#include <string.h>
++#if HAVE_UNISTD_H
++#include <unistd.h>
++#endif
++#include <stdlib.h>
++#include <string.h>
++#if HAVE_SYS_TYPES_H
++#include <sys/types.h>
++#endif
++#if HAVE_SYS_STAT_H
++#include <sys/stat.h>
++#endif
++#include <dirent.h>
++#if HAVE_ERRNO_H
++#include <errno.h>
++#endif
++#if HAVE_SYS_MKDEV_H
++#include <sys/mkdev.h>
++#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 = malloc(sizeof(struct dir_list));
++ if (!dp)
++ return;
++ dp->name = malloc(strlen(name)+1);
++ if (!dp->name) {
++ free(dp);
++ return;
++ }
++ 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 = malloc(strlen(path)+1);
++ if (!cp) {
++ closedir(dir);
++ return ENOMEM;
++ }
++ 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 = 0, *new_list = 0;
++ struct dir_list *current;
++ char *ret_path = 0;
++
++ /*
++ * 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 = "Couldn't 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("Couldn't find device.\n");
++ }
++ return 0;
++}
++
++#endif
+diff -Nur busybox-1.00/e2fsprogs/ext2fs/flushb.c busybox/e2fsprogs/ext2fs/flushb.c
+--- busybox-1.00/e2fsprogs/ext2fs/flushb.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/flushb.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,82 @@
++/*
++ * 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 <stdio.h>
++#if HAVE_ERRNO_H
++#include <errno.h>
++#endif
++#if HAVE_UNISTD_H
++#include <unistd.h>
++#endif
++#if HAVE_SYS_IOCTL_H
++#include <sys/ioctl.h>
++#endif
++#if HAVE_SYS_MOUNT_H
++#include <sys/param.h>
++#include <sys/mount.h> /* 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 -Nur busybox-1.00/e2fsprogs/ext2fs/freefs.c busybox/e2fsprogs/ext2fs/freefs.c
+--- busybox-1.00/e2fsprogs/ext2fs/freefs.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/freefs.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,147 @@
++/*
++ * 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 <stdio.h>
++#if HAVE_UNISTD_H
++#include <unistd.h>
++#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);
++ }
++ if (fs->device_name)
++ ext2fs_free_mem(&fs->device_name);
++ if (fs->super)
++ ext2fs_free_mem(&fs->super);
++ if (fs->orig_super)
++ ext2fs_free_mem(&fs->orig_super);
++ if (fs->group_desc)
++ ext2fs_free_mem(&fs->group_desc);
++ if (fs->block_map)
++ ext2fs_free_block_bitmap(fs->block_map);
++ if (fs->inode_map)
++ ext2fs_free_inode_bitmap(fs->inode_map);
++
++ if (fs->badblocks)
++ ext2fs_badblocks_list_free(fs->badblocks);
++ fs->badblocks = 0;
++
++ if (fs->dblist)
++ 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;
++ if (bitmap->description) {
++ ext2fs_free_mem(&bitmap->description);
++ bitmap->description = 0;
++ }
++ if (bitmap->bitmap) {
++ ext2fs_free_mem(&bitmap->bitmap);
++ bitmap->bitmap = 0;
++ }
++ 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;
++ if (icache->buffer)
++ ext2fs_free_mem(&icache->buffer);
++ if (icache->cache)
++ 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->magic != EXT2_ET_MAGIC_BADBLOCKS_LIST)
++ return;
++
++ if (bb->list)
++ ext2fs_free_mem(&bb->list);
++ bb->list = 0;
++ 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;
++
++ if (dblist->list)
++ ext2fs_free_mem(&dblist->list);
++ dblist->list = 0;
++ if (dblist->fs && dblist->fs->dblist == dblist)
++ dblist->fs->dblist = 0;
++ dblist->magic = 0;
++ ext2fs_free_mem(&dblist);
++}
++
+diff -Nur busybox-1.00/e2fsprogs/ext2fs/gen_bitmap.c busybox/e2fsprogs/ext2fs/gen_bitmap.c
+--- busybox-1.00/e2fsprogs/ext2fs/gen_bitmap.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/gen_bitmap.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,48 @@
++/*
++ * 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 <stdio.h>
++#include <string.h>
++#if HAVE_UNISTD_H
++#include <unistd.h>
++#endif
++#include <fcntl.h>
++#include <time.h>
++#if HAVE_SYS_STAT_H
++#include <sys/stat.h>
++#endif
++#if HAVE_SYS_TYPES_H
++#include <sys/types.h>
++#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 -Nur busybox-1.00/e2fsprogs/ext2fs/get_pathname.c busybox/e2fsprogs/ext2fs/get_pathname.c
+--- busybox-1.00/e2fsprogs/ext2fs/get_pathname.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/get_pathname.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,157 @@
++/*
++ * 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 <name>. <dir> is the containing
++ * directory inode, and <ino> is the inode number itself. If
++ * <ino> is zero, then ext2fs_get_pathname will return pathname
++ * of the the directory <dir>.
++ *
++ */
++
++#include <stdio.h>
++#include <string.h>
++#if HAVE_UNISTD_H
++#include <unistd.h>
++#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:
++ if (gp.name)
++ 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 -Nur busybox-1.00/e2fsprogs/ext2fs/getsectsize.c busybox/e2fsprogs/ext2fs/getsectsize.c
+--- busybox-1.00/e2fsprogs/ext2fs/getsectsize.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/getsectsize.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,57 @@
++/*
++ * 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 <stdio.h>
++#if HAVE_UNISTD_H
++#include <unistd.h>
++#endif
++#if HAVE_ERRNO_H
++#include <errno.h>
++#endif
++#include <fcntl.h>
++#ifdef HAVE_LINUX_FD_H
++#include <sys/ioctl.h>
++#include <linux/fd.h>
++#endif
++
++#if defined(__linux__) && defined(_IO) && !defined(BLKGETSIZE)
++#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 -Nur busybox-1.00/e2fsprogs/ext2fs/getsize.c busybox/e2fsprogs/ext2fs/getsize.c
+--- busybox-1.00/e2fsprogs/ext2fs/getsize.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/getsize.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,290 @@
++/*
++ * 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 <stdio.h>
++#if HAVE_UNISTD_H
++#include <unistd.h>
++#endif
++#if HAVE_ERRNO_H
++#include <errno.h>
++#endif
++#include <fcntl.h>
++#ifdef HAVE_SYS_IOCTL_H
++#include <sys/ioctl.h>
++#endif
++#ifdef HAVE_LINUX_FD_H
++#include <linux/fd.h>
++#endif
++#ifdef HAVE_SYS_DISKLABEL_H
++#include <sys/disklabel.h>
++#endif
++#ifdef HAVE_SYS_DISK_H
++#ifdef HAVE_SYS_QUEUE_H
++#include <sys/queue.h> /* for LIST_HEAD */
++#endif
++#include <sys/disk.h>
++#endif
++#ifdef __linux__
++#include <sys/utsname.h>
++#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 "windows.h"
++#include "winioctl.h"
++
++#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__
++ if ((uname(&ut) == 0) &&
++ ((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 -Nur busybox-1.00/e2fsprogs/ext2fs/icount.c busybox/e2fsprogs/ext2fs/icount.c
+--- busybox-1.00/e2fsprogs/ext2fs/icount.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/icount.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,483 @@
++/*
++ * 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 <unistd.h>
++#endif
++#include <string.h>
++#include <stdio.h>
++
++#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;
++ if (icount->list)
++ ext2fs_free_mem(&icount->list);
++ if (icount->single)
++ ext2fs_free_inode_bitmap(icount->single);
++ if (icount->multiple)
++ 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));
++#if 0
++ printf("Icount allocated %d entries, %d bytes.\n",
++ icount->size, bytes);
++#endif
++ 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;
++#if 0
++ printf("Reallocating icount %d entries...\n", new_size);
++#endif
++ 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++];
++#if 0
++ printf("Non-cursor get_icount_el: %u\n", ino);
++#endif
++ low = 0;
++ high = (int) icount->count-1;
++ while (low <= high) {
++#if 0
++ mid = (low+high)/2;
++#else
++ 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)));
++ }
++#endif
++ 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 -Nur busybox-1.00/e2fsprogs/ext2fs/imager.c busybox/e2fsprogs/ext2fs/imager.c
+--- busybox-1.00/e2fsprogs/ext2fs/imager.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/imager.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,387 @@
++/*
++ * 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 <stdio.h>
++#include <string.h>
++#if HAVE_UNISTD_H
++#include <unistd.h>
++#endif
++#if HAVE_ERRNO_H
++#include <errno.h>
++#endif
++#include <fcntl.h>
++#include <time.h>
++#if HAVE_SYS_STAT_H
++#include <sys/stat.h>
++#endif
++#if HAVE_SYS_TYPES_H
++#include <sys/types.h>
++#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 = malloc(fs->blocksize * BUF_BLOCKS);
++ if (!buf)
++ return ENOMEM;
++
++ 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 = malloc(fs->blocksize * BUF_BLOCKS);
++ if (!buf)
++ return ENOMEM;
++
++ 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 = malloc(fs->blocksize);
++ if (!buf)
++ return ENOMEM;
++
++ /*
++ * 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 = malloc(size);
++ if (!buf)
++ return ENOMEM;
++
++ /*
++ * 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 = 0;
++ 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 = malloc(size);
++ if (!buf)
++ return ENOMEM;
++
++ 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:
++ if (buf)
++ free(buf);
++ return (retval);
++}
+diff -Nur busybox-1.00/e2fsprogs/ext2fs/ind_block.c busybox/e2fsprogs/ext2fs/ind_block.c
+--- busybox-1.00/e2fsprogs/ext2fs/ind_block.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/ind_block.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,66 @@
++/*
++ * 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 <stdio.h>
++#include <string.h>
++#if HAVE_UNISTD_H
++#include <unistd.h>
++#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;
++ blk_t *block_nr;
++ int i;
++ int limit = fs->blocksize >> 2;
++
++ 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;
++ }
++#ifdef EXT2FS_ENABLE_SWAPFS
++ 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)
++{
++ blk_t *block_nr;
++ int i;
++ int limit = fs->blocksize >> 2;
++
++ if (fs->flags & EXT2_FLAG_IMAGE_FILE)
++ return 0;
++
++#ifdef EXT2FS_ENABLE_SWAPFS
++ 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 -Nur busybox-1.00/e2fsprogs/ext2fs/initialize.c busybox/e2fsprogs/ext2fs/initialize.c
+--- busybox-1.00/e2fsprogs/ext2fs/initialize.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/initialize.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,387 @@
++/*
++ * 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 <stdio.h>
++#include <string.h>
++#if HAVE_UNISTD_H
++#include <unistd.h>
++#endif
++#include <fcntl.h>
++#include <time.h>
++#if HAVE_SYS_STAT_H
++#include <sys/stat.h>
++#endif
++#if HAVE_SYS_TYPES_H
++#include <sys/types.h>
++#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 -Nur busybox-1.00/e2fsprogs/ext2fs/inline.c busybox/e2fsprogs/ext2fs/inline.c
+--- busybox-1.00/e2fsprogs/ext2fs/inline.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/inline.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,32 @@
++/*
++ * 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 <stdio.h>
++#include <string.h>
++#if HAVE_UNISTD_H
++#include <unistd.h>
++#endif
++#include <fcntl.h>
++#include <time.h>
++#if HAVE_SYS_STAT_H
++#include <sys/stat.h>
++#endif
++#if HAVE_SYS_TYPES_H
++#include <sys/types.h>
++#endif
++
++#include "ext2_fs.h"
++#define INCLUDE_INLINE_FUNCS
++#include "ext2fs.h"
++
+diff -Nur busybox-1.00/e2fsprogs/ext2fs/inode.c busybox/e2fsprogs/ext2fs/inode.c
+--- busybox-1.00/e2fsprogs/ext2fs/inode.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/inode.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,794 @@
++/*
++ * 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 <stdio.h>
++#include <string.h>
++#if HAVE_UNISTD_H
++#include <unistd.h>
++#endif
++#if HAVE_ERRNO_H
++#include <errno.h>
++#endif
++#if HAVE_SYS_STAT_H
++#include <sys/stat.h>
++#endif
++#if HAVE_SYS_TYPES_H
++#include <sys/types.h>
++#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,
++ ext2_inode_scan scan,
++ 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 && fs->badblocks) {
++ 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);
++ return;
++}
++
++void ext2fs_set_inode_callback(ext2_inode_scan scan,
++ errcode_t (*done_group)(ext2_filsys fs,
++ ext2_inode_scan scan,
++ 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;
++}
++
++#if 0
++/*
++ * Returns 1 if the entire inode_buffer has a non-zero size and
++ * contains all zeros. (Not just deleted inodes, since that means
++ * that part of the inode table was used at one point; we want all
++ * zeros, which means that the inode table is pristine.)
++ */
++static inline int is_empty_scan(ext2_inode_scan scan)
++{
++ int i;
++
++ if (scan->bytes_left == 0)
++ return 0;
++
++ for (i=0; i < scan->bytes_left; i++)
++ if (scan->ptr[i])
++ return 0;
++ return 1;
++}
++#endif
++
++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, 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;
++
++#ifdef EXT2FS_ENABLE_SWAPFS
++ 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 {
++#ifdef EXT2FS_ENABLE_SWAPFS
++ 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++;
++ }
++
++#ifdef EXT2FS_ENABLE_SWAPFS
++ 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 = malloc(length);
++ if (!w_inode)
++ return ENOMEM;
++ } else
++ w_inode = &temp_inode;
++ memset(w_inode, 0, length);
++
++#ifdef EXT2FS_ENABLE_SWAPFS
++ 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 = malloc(size);
++ if (!buf)
++ return ENOMEM;
++
++ 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 -Nur busybox-1.00/e2fsprogs/ext2fs/inode_io.c busybox/e2fsprogs/ext2fs/inode_io.c
+--- busybox-1.00/e2fsprogs/ext2fs/inode_io.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/inode_io.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,270 @@
++/*
++ * 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 <stdio.h>
++#include <string.h>
++#if HAVE_UNISTD_H
++#include <unistd.h>
++#endif
++#if HAVE_ERRNO_H
++#include <errno.h>
++#endif
++#include <time.h>
++
++#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 -Nur busybox-1.00/e2fsprogs/ext2fs/io_manager.c busybox/e2fsprogs/ext2fs/io_manager.c
+--- busybox-1.00/e2fsprogs/ext2fs/io_manager.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/io_manager.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,69 @@
++/*
++ * io_manager.c --- the I/O manager abstraction
++ */
++
++#include <stdio.h>
++#include <string.h>
++#if HAVE_UNISTD_H
++#include <unistd.h>
++#endif
++#include <fcntl.h>
++#include <time.h>
++#if HAVE_SYS_STAT_H
++#include <sys/stat.h>
++#endif
++#if HAVE_SYS_TYPES_H
++#include <sys/types.h>
++#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 -Nur busybox-1.00/e2fsprogs/ext2fs/irel.h busybox/e2fsprogs/ext2fs/irel.h
+--- busybox-1.00/e2fsprogs/ext2fs/irel.h 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/irel.h 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,114 @@
++/*
++ * 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 -Nur busybox-1.00/e2fsprogs/ext2fs/irel_ma.c busybox/e2fsprogs/ext2fs/irel_ma.c
+--- busybox-1.00/e2fsprogs/ext2fs/irel_ma.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/irel_ma.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,372 @@
++/*
++ * 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 <fcntl.h>
++#include <stdio.h>
++#include <string.h>
++#if HAVE_UNISTD_H
++#include <unistd.h>
++#endif
++#if HAVE_ERRNO_H
++#include <errno.h>
++#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];
++ if (ma->ref_entries[(unsigned) new].refs)
++ 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;
++ if (ma->ref_entries[(unsigned) old].refs)
++ 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) {
++ if (ma->orig_map)
++ ext2fs_free_mem(&ma->orig_map);
++ if (ma->entries)
++ ext2fs_free_mem(&ma->entries);
++ if (ma->ref_entries) {
++ for (ino = 0; ino <= ma->max_inode; ino++) {
++ if (ma->ref_entries[(unsigned) ino].refs)
++ ext2fs_free_mem(&ma->ref_entries[(unsigned) ino].refs);
++ }
++ ext2fs_free_mem(&ma->ref_entries);
++ }
++ ext2fs_free_mem(&ma);
++ }
++ if (irel->name)
++ ext2fs_free_mem(&irel->name);
++ ext2fs_free_mem(&irel);
++ return 0;
++}
+diff -Nur busybox-1.00/e2fsprogs/ext2fs/ismounted.c busybox/e2fsprogs/ext2fs/ismounted.c
+--- busybox-1.00/e2fsprogs/ext2fs/ismounted.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/ismounted.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,358 @@
++/*
++ * 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 <stdio.h>
++#if HAVE_UNISTD_H
++#include <unistd.h>
++#endif
++#if HAVE_ERRNO_H
++#include <errno.h>
++#endif
++#include <fcntl.h>
++#ifdef HAVE_LINUX_FD_H
++#include <linux/fd.h>
++#endif
++#ifdef HAVE_MNTENT_H
++#include <mntent.h>
++#endif
++#ifdef HAVE_GETMNTINFO
++#include <paths.h>
++#include <sys/param.h>
++#include <sys/mount.h>
++#endif /* HAVE_GETMNTINFO */
++#include <string.h>
++#include <sys/stat.h>
++
++#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 (!strcmp(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("/proc/swaps", "r")))
++ 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, "<swap>", 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_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 -Nur busybox-1.00/e2fsprogs/ext2fs/jfs_compat.h busybox/e2fsprogs/ext2fs/jfs_compat.h
+--- busybox-1.00/e2fsprogs/ext2fs/jfs_compat.h 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/jfs_compat.h 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,67 @@
++
++#ifndef _JFS_COMPAT_H
++#define _JFS_COMPAT_H
++
++#include "kernel-list.h"
++#include <errno.h>
++#ifdef HAVE_NETINET_IN_H
++#include <netinet/in.h>
++#endif
++
++#define printk printf
++#define KERN_ERR ""
++#define KERN_DEBUG ""
++
++#define READ 0
++#define WRITE 1
++
++#define cpu_to_be32(n) htonl(n)
++#define be32_to_cpu(n) ntohl(n)
++
++typedef unsigned int tid_t;
++typedef struct journal_s journal_t;
++
++struct buffer_head;
++struct inode;
++
++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;
++};
++
++#define J_ASSERT(assert) \
++ do { if (!(assert)) { \
++ printf ("Assertion failure in %s() at %s line %d: " \
++ "\"%s\"\n", \
++ __FUNCTION__, __FILE__, __LINE__, # assert); \
++ fatal_error(e2fsck_global_ctx, 0); \
++ } } while (0)
++
++#define is_journal_abort(x) 0
++
++#define BUFFER_TRACE(bh, info) do {} while (0)
++
++/* Need this so we can compile with configure --enable-gcc-wall */
++#ifdef NO_INLINE_FUNCS
++#define inline
++#endif
++
++#endif /* _JFS_COMPAT_H */
+diff -Nur busybox-1.00/e2fsprogs/ext2fs/jfs_dat.h busybox/e2fsprogs/ext2fs/jfs_dat.h
+--- busybox-1.00/e2fsprogs/ext2fs/jfs_dat.h 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/jfs_dat.h 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,64 @@
++/*
++ * 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 -Nur busybox-1.00/e2fsprogs/ext2fs/jfs_user.h busybox/e2fsprogs/ext2fs/jfs_user.h
+--- busybox-1.00/e2fsprogs/ext2fs/jfs_user.h 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/jfs_user.h 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,8 @@
++#ifndef _JFS_USER_H
++#define _JFS_USER_H
++
++typedef unsigned short kdev_t;
++
++#include "kernel-jbd.h"
++
++#endif /* _JFS_USER_H */
+diff -Nur busybox-1.00/e2fsprogs/ext2fs/kernel-jbd.h busybox/e2fsprogs/ext2fs/kernel-jbd.h
+--- busybox-1.00/e2fsprogs/ext2fs/kernel-jbd.h 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/kernel-jbd.h 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,910 @@
++/*
++ * linux/include/linux/jbd.h
++ *
++ * Written by Stephen C. Tweedie <sct@redhat.com>
++ *
++ * 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
++
++#if defined(CONFIG_JBD) || defined(CONFIG_JBD_MODULE) || !defined(__KERNEL__)
++
++/* Allow this file to be included directly into e2fsprogs */
++#ifndef __KERNEL__
++#include "jfs_compat.h"
++#define JFS_DEBUG
++#define jfs_debug jbd_debug
++#else
++
++#include <linux/journal-head.h>
++#include <linux/stddef.h>
++#include <asm/semaphore.h>
++#endif
++
++#ifndef __GNUC__
++#define __FUNCTION__ ""
++#endif
++
++#define journal_oom_retry 1
++
++#ifdef __STDC__
++#ifdef __CONFIG_JBD_DEBUG__E2FS
++/*
++ * Define JBD_EXPENSIVE_CHECKING to enable more expensive internal
++ * consistency checks. By default we don't do this unless
++ * __CONFIG_JBD_DEBUG__E2FS is on.
++ */
++#define JBD_EXPENSIVE_CHECKING
++extern int journal_enable_debug;
++
++#define jbd_debug(n, f, a...) \
++ do { \
++ if ((n) <= journal_enable_debug) { \
++ printk (KERN_DEBUG "(%s, %d): %s: ", \
++ __FILE__, __LINE__, __FUNCTION__); \
++ printk (f, ## a); \
++ } \
++ } while (0)
++#else
++#ifdef __GNUC__
++#define jbd_debug(f, a...) /**/
++#else
++#define jbd_debug(f, ...) /**/
++#endif
++#endif
++#else
++#define jbd_debug(x) /* AIX doesn't do STDC */
++#endif
++
++extern void * __jbd_kmalloc (char *where, size_t size, int flags, int retry);
++#define jbd_kmalloc(size, flags) \
++ __jbd_kmalloc(__FUNCTION__, (size), (flags), journal_oom_retry)
++#define jbd_rep_kmalloc(size, flags) \
++ __jbd_kmalloc(__FUNCTION__, (size), (flags), 1)
++
++#define JFS_MIN_JOURNAL_BLOCKS 1024
++
++#ifdef __KERNEL__
++typedef struct handle_s handle_t; /* Atomic operation type */
++typedef struct journal_s journal_t; /* Journal control structure */
++#endif
++
++/*
++ * Internal structures used by the logging mechanism:
++ */
++
++#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_V1 3
++#define JFS_SUPERBLOCK_V2 4
++#define JFS_REVOKE_BLOCK 5
++
++/*
++ * 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;
++
++/*
++ * 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 */
++
++
++/*
++ * 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;
++
++#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
++
++#ifdef __KERNEL__
++
++#include <linux/fs.h>
++#include <linux/sched.h>
++
++#define JBD_ASSERTIONS
++#ifdef JBD_ASSERTIONS
++#define J_ASSERT(assert) \
++do { \
++ if (!(assert)) { \
++ printk (KERN_EMERG \
++ "Assertion failure in %s() at %s:%d: \"%s\"\n", \
++ __FUNCTION__, __FILE__, __LINE__, # assert); \
++ BUG(); \
++ } \
++} while (0)
++
++#if defined(CONFIG_BUFFER_DEBUG)
++void buffer_assertion_failure(struct buffer_head *bh);
++#define J_ASSERT_BH(bh, expr) \
++ do { \
++ if (!(expr)) \
++ buffer_assertion_failure(bh); \
++ J_ASSERT(expr); \
++ } while (0)
++#define J_ASSERT_JH(jh, expr) J_ASSERT_BH(jh2bh(jh), expr)
++#else
++#define J_ASSERT_BH(bh, expr) J_ASSERT(expr)
++#define J_ASSERT_JH(jh, expr) J_ASSERT(expr)
++#endif
++
++#else
++#define J_ASSERT(assert)
++#endif /* JBD_ASSERTIONS */
++
++enum jbd_state_bits {
++ BH_JWrite
++ = BH_PrivateStart, /* 1 if being written to log (@@@ DEBUGGING) */
++ BH_Freed, /* 1 if buffer has been freed (truncated) */
++ BH_Revoked, /* 1 if buffer has been revoked from the log */
++ BH_RevokeValid, /* 1 if buffer revoked flag is valid */
++ BH_JBDDirty, /* 1 if buffer is dirty but journaled */
++};
++
++/* Return true if the buffer is one which JBD is managing */
++static inline int buffer_jbd(struct buffer_head *bh)
++{
++ return __buffer_state(bh, JBD);
++}
++
++static inline struct buffer_head *jh2bh(struct journal_head *jh)
++{
++ return jh->b_bh;
++}
++
++static inline struct journal_head *bh2jh(struct buffer_head *bh)
++{
++ return bh->b_private;
++}
++
++struct jbd_revoke_table_s;
++
++/* The handle_t type represents a single atomic update being performed
++ * by some process. All filesystem modifications made by the process go
++ * through this handle. Recursive operations (such as quota operations)
++ * are gathered into a single update.
++ *
++ * The buffer credits field is used to account for journaled buffers
++ * being modified by the running process. To ensure that there is
++ * enough log space for all outstanding operations, we need to limit the
++ * number of outstanding buffers possible at any time. When the
++ * operation completes, any buffer credits not used are credited back to
++ * the transaction, so that at all times we know how many buffers the
++ * outstanding updates on a transaction might possibly touch. */
++
++struct handle_s
++{
++ /* Which compound transaction is this update a part of? */
++ transaction_t * h_transaction;
++
++ /* Number of remaining buffers we are allowed to dirty: */
++ int h_buffer_credits;
++
++ /* Reference count on this handle */
++ int h_ref;
++
++ /* Field for caller's use to track errors through large fs
++ operations */
++ int h_err;
++
++ /* Flags */
++ unsigned int h_sync: 1; /* sync-on-close */
++ unsigned int h_jdata: 1; /* force data journaling */
++ unsigned int h_aborted: 1; /* fatal error on handle */
++};
++
++
++/* The transaction_t type is the guts of the journaling mechanism. It
++ * tracks a compound transaction through its various states:
++ *
++ * RUNNING: accepting new updates
++ * LOCKED: Updates still running but we don't accept new ones
++ * RUNDOWN: Updates are tidying up but have finished requesting
++ * new buffers to modify (state not used for now)
++ * FLUSH: All updates complete, but we are still writing to disk
++ * COMMIT: All data on disk, writing commit record
++ * FINISHED: We still have to keep the transaction for checkpointing.
++ *
++ * The transaction keeps track of all of the buffers modified by a
++ * running transaction, and all of the buffers committed but not yet
++ * flushed to home for finished transactions.
++ */
++
++struct transaction_s
++{
++ /* Pointer to the journal for this transaction. */
++ journal_t * t_journal;
++
++ /* Sequence number for this transaction */
++ tid_t t_tid;
++
++ /* Transaction's current state */
++ enum {
++ T_RUNNING,
++ T_LOCKED,
++ T_RUNDOWN,
++ T_FLUSH,
++ T_COMMIT,
++ T_FINISHED
++ } t_state;
++
++ /* Where in the log does this transaction's commit start? */
++ unsigned long t_log_start;
++
++ /* Doubly-linked circular list of all inodes owned by this
++ transaction */ /* AKPM: unused */
++ struct inode * t_ilist;
++
++ /* Number of buffers on the t_buffers list */
++ int t_nr_buffers;
++
++ /* Doubly-linked circular list of all buffers reserved but not
++ yet modified by this transaction */
++ struct journal_head * t_reserved_list;
++
++ /* Doubly-linked circular list of all metadata buffers owned by this
++ transaction */
++ struct journal_head * t_buffers;
++
++ /*
++ * Doubly-linked circular list of all data buffers still to be
++ * flushed before this transaction can be committed.
++ * Protected by journal_datalist_lock.
++ */
++ struct journal_head * t_sync_datalist;
++
++ /*
++ * Doubly-linked circular list of all writepage data buffers
++ * still to be written before this transaction can be committed.
++ * Protected by journal_datalist_lock.
++ */
++ struct journal_head * t_async_datalist;
++
++ /* Doubly-linked circular list of all forget buffers (superceded
++ buffers which we can un-checkpoint once this transaction
++ commits) */
++ struct journal_head * t_forget;
++
++ /*
++ * Doubly-linked circular list of all buffers still to be
++ * flushed before this transaction can be checkpointed.
++ */
++ /* Protected by journal_datalist_lock */
++ struct journal_head * t_checkpoint_list;
++
++ /* Doubly-linked circular list of temporary buffers currently
++ undergoing IO in the log */
++ struct journal_head * t_iobuf_list;
++
++ /* Doubly-linked circular list of metadata buffers being
++ shadowed by log IO. The IO buffers on the iobuf list and the
++ shadow buffers on this list match each other one for one at
++ all times. */
++ struct journal_head * t_shadow_list;
++
++ /* Doubly-linked circular list of control buffers being written
++ to the log. */
++ struct journal_head * t_log_list;
++
++ /* Number of outstanding updates running on this transaction */
++ int t_updates;
++
++ /* Number of buffers reserved for use by all handles in this
++ * transaction handle but not yet modified. */
++ int t_outstanding_credits;
++
++ /*
++ * Forward and backward links for the circular list of all
++ * transactions awaiting checkpoint.
++ */
++ /* Protected by journal_datalist_lock */
++ transaction_t *t_cpnext, *t_cpprev;
++
++ /* When will the transaction expire (become due for commit), in
++ * jiffies ? */
++ unsigned long t_expires;
++
++ /* How many handles used this transaction? */
++ int t_handle_count;
++};
++
++
++/* The journal_t maintains all of the journaling state information for a
++ * single filesystem. It is linked to from the fs superblock structure.
++ *
++ * We use the journal_t to keep track of all outstanding transaction
++ * activity on the filesystem, and to manage the state of the log
++ * writing process. */
++
++struct journal_s
++{
++ /* General journaling state flags */
++ unsigned long j_flags;
++
++ /* Is there an outstanding uncleared error on the journal (from
++ * a prior abort)? */
++ int j_errno;
++
++ /* The superblock buffer */
++ struct buffer_head * j_sb_buffer;
++ journal_superblock_t * j_superblock;
++
++ /* Version of the superblock format */
++ int j_format_version;
++
++ /* Number of processes waiting to create a barrier lock */
++ int j_barrier_count;
++
++ /* The barrier lock itself */
++ struct semaphore j_barrier;
++
++ /* Transactions: The current running transaction... */
++ transaction_t * j_running_transaction;
++
++ /* ... the transaction we are pushing to disk ... */
++ transaction_t * j_committing_transaction;
++
++ /* ... and a linked circular list of all transactions waiting
++ * for checkpointing. */
++ /* Protected by journal_datalist_lock */
++ transaction_t * j_checkpoint_transactions;
++
++ /* Wait queue for waiting for a locked transaction to start
++ committing, or for a barrier lock to be released */
++ wait_queue_head_t j_wait_transaction_locked;
++
++ /* Wait queue for waiting for checkpointing to complete */
++ wait_queue_head_t j_wait_logspace;
++
++ /* Wait queue for waiting for commit to complete */
++ wait_queue_head_t j_wait_done_commit;
++
++ /* Wait queue to trigger checkpointing */
++ wait_queue_head_t j_wait_checkpoint;
++
++ /* Wait queue to trigger commit */
++ wait_queue_head_t j_wait_commit;
++
++ /* Wait queue to wait for updates to complete */
++ wait_queue_head_t j_wait_updates;
++
++ /* Semaphore for locking against concurrent checkpoints */
++ struct semaphore j_checkpoint_sem;
++
++ /* The main journal lock, used by lock_journal() */
++ struct semaphore j_sem;
++
++ /* Journal head: identifies the first unused block in the journal. */
++ unsigned long j_head;
++
++ /* Journal tail: identifies the oldest still-used block in the
++ * journal. */
++ unsigned long j_tail;
++
++ /* Journal free: how many free blocks are there in the journal? */
++ unsigned long j_free;
++
++ /* Journal start and end: the block numbers of the first usable
++ * block and one beyond the last usable block in the journal. */
++ unsigned long j_first, j_last;
++
++ /* Device, blocksize and starting block offset for the location
++ * where we store the journal. */
++ kdev_t j_dev;
++ int j_blocksize;
++ unsigned int j_blk_offset;
++
++ /* Device which holds the client fs. For internal journal this
++ * will be equal to j_dev. */
++ kdev_t j_fs_dev;
++
++ /* Total maximum capacity of the journal region on disk. */
++ unsigned int j_maxlen;
++
++ /* Optional inode where we store the journal. If present, all
++ * journal block numbers are mapped into this inode via
++ * bmap(). */
++ struct inode * j_inode;
++
++ /* Sequence number of the oldest transaction in the log */
++ tid_t j_tail_sequence;
++ /* Sequence number of the next transaction to grant */
++ tid_t j_transaction_sequence;
++ /* Sequence number of the most recently committed transaction */
++ tid_t j_commit_sequence;
++ /* Sequence number of the most recent transaction wanting commit */
++ tid_t j_commit_request;
++
++ /* Journal uuid: identifies the object (filesystem, LVM volume
++ * etc) backed by this journal. This will eventually be
++ * replaced by an array of uuids, allowing us to index multiple
++ * devices within a single journal and to perform atomic updates
++ * across them. */
++
++ __u8 j_uuid[16];
++
++ /* Pointer to the current commit thread for this journal */
++ struct task_struct * j_task;
++
++ /* Maximum number of metadata buffers to allow in a single
++ * compound commit transaction */
++ int j_max_transaction_buffers;
++
++ /* What is the maximum transaction lifetime before we begin a
++ * commit? */
++ unsigned long j_commit_interval;
++
++ /* The timer used to wakeup the commit thread: */
++ struct timer_list * j_commit_timer;
++ int j_commit_timer_active;
++
++ /* Link all journals together - system-wide */
++ struct list_head j_all_journals;
++
++ /* The revoke table: maintains the list of revoked blocks in the
++ current transaction. */
++ struct jbd_revoke_table_s *j_revoke;
++};
++
++/*
++ * Journal flag definitions
++ */
++#define JFS_UNMOUNT 0x001 /* Journal thread is being destroyed */
++#define JFS_ABORT 0x002 /* Journaling has been aborted for errors. */
++#define JFS_ACK_ERR 0x004 /* The errno in the sb has been acked */
++#define JFS_FLUSHED 0x008 /* The journal superblock has been flushed */
++#define JFS_LOADED 0x010 /* The journal superblock has been loaded */
++
++/*
++ * Function declarations for the journaling transaction and buffer
++ * management
++ */
++
++/* Filing buffers */
++extern void __journal_unfile_buffer(struct journal_head *);
++extern void journal_unfile_buffer(struct journal_head *);
++extern void __journal_refile_buffer(struct journal_head *);
++extern void journal_refile_buffer(struct journal_head *);
++extern void __journal_file_buffer(struct journal_head *, transaction_t *, int);
++extern void __journal_free_buffer(struct journal_head *bh);
++extern void journal_file_buffer(struct journal_head *, transaction_t *, int);
++extern void __journal_clean_data_list(transaction_t *transaction);
++
++/* Log buffer allocation */
++extern struct journal_head * journal_get_descriptor_buffer(journal_t *);
++extern unsigned long journal_next_log_block(journal_t *);
++
++/* Commit management */
++extern void journal_commit_transaction(journal_t *);
++
++/* Checkpoint list management */
++int __journal_clean_checkpoint_list(journal_t *journal);
++extern void journal_remove_checkpoint(struct journal_head *);
++extern void __journal_remove_checkpoint(struct journal_head *);
++extern void journal_insert_checkpoint(struct journal_head *, transaction_t *);
++extern void __journal_insert_checkpoint(struct journal_head *,transaction_t *);
++
++/* Buffer IO */
++extern int
++journal_write_metadata_buffer(transaction_t *transaction,
++ struct journal_head *jh_in,
++ struct journal_head **jh_out,
++ int blocknr);
++
++/* Transaction locking */
++extern void __wait_on_journal (journal_t *);
++
++/*
++ * Journal locking.
++ *
++ * We need to lock the journal during transaction state changes so that
++ * nobody ever tries to take a handle on the running transaction while
++ * we are in the middle of moving it to the commit phase.
++ *
++ * Note that the locking is completely interrupt unsafe. We never touch
++ * journal structures from interrupts.
++ *
++ * In 2.2, the BKL was required for lock_journal. This is no longer
++ * the case.
++ */
++
++static inline void lock_journal(journal_t *journal)
++{
++ down(&journal->j_sem);
++}
++
++/* This returns zero if we acquired the semaphore */
++static inline int try_lock_journal(journal_t * journal)
++{
++ return down_trylock(&journal->j_sem);
++}
++
++static inline void unlock_journal(journal_t * journal)
++{
++ up(&journal->j_sem);
++}
++
++
++static inline handle_t *journal_current_handle(void)
++{
++ return current->journal_info;
++}
++
++/* The journaling code user interface:
++ *
++ * Create and destroy handles
++ * Register buffer modifications against the current transaction.
++ */
++
++extern handle_t *journal_start(journal_t *, int nblocks);
++extern handle_t *journal_try_start(journal_t *, int nblocks);
++extern int journal_restart (handle_t *, int nblocks);
++extern int journal_extend (handle_t *, int nblocks);
++extern int journal_get_write_access (handle_t *, struct buffer_head *);
++extern int journal_get_create_access (handle_t *, struct buffer_head *);
++extern int journal_get_undo_access (handle_t *, struct buffer_head *);
++extern int journal_dirty_data (handle_t *,
++ struct buffer_head *, int async);
++extern int journal_dirty_metadata (handle_t *, struct buffer_head *);
++extern void journal_release_buffer (handle_t *, struct buffer_head *);
++extern void journal_forget (handle_t *, struct buffer_head *);
++extern void journal_sync_buffer (struct buffer_head *);
++extern int journal_flushpage(journal_t *, struct page *, unsigned long);
++extern int journal_try_to_free_buffers(journal_t *, struct page *, int);
++extern int journal_stop(handle_t *);
++extern int journal_flush (journal_t *);
++
++extern void journal_lock_updates (journal_t *);
++extern void journal_unlock_updates (journal_t *);
++
++extern journal_t * journal_init_dev(kdev_t dev, kdev_t fs_dev,
++ int start, int len, int bsize);
++extern journal_t * journal_init_inode (struct inode *);
++extern int journal_update_format (journal_t *);
++extern int journal_check_used_features
++ (journal_t *, unsigned long, unsigned long, unsigned long);
++extern int journal_check_available_features
++ (journal_t *, unsigned long, unsigned long, unsigned long);
++extern int journal_set_features
++ (journal_t *, unsigned long, unsigned long, unsigned long);
++extern int journal_create (journal_t *);
++extern int journal_load (journal_t *journal);
++extern void journal_destroy (journal_t *);
++extern int journal_recover (journal_t *journal);
++extern int journal_wipe (journal_t *, int);
++extern int journal_skip_recovery (journal_t *);
++extern void journal_update_superblock (journal_t *, int);
++extern void __journal_abort (journal_t *);
++extern void journal_abort (journal_t *, int);
++extern int journal_errno (journal_t *);
++extern void journal_ack_err (journal_t *);
++extern int journal_clear_err (journal_t *);
++extern unsigned long journal_bmap(journal_t *journal, unsigned long blocknr);
++extern int journal_force_commit(journal_t *journal);
++
++/*
++ * journal_head management
++ */
++extern struct journal_head
++ *journal_add_journal_head(struct buffer_head *bh);
++extern void journal_remove_journal_head(struct buffer_head *bh);
++extern void __journal_remove_journal_head(struct buffer_head *bh);
++extern void journal_unlock_journal_head(struct journal_head *jh);
++
++/* Primary revoke support */
++#define JOURNAL_REVOKE_DEFAULT_HASH 256
++extern int journal_init_revoke(journal_t *, int);
++extern void journal_destroy_revoke_caches(void);
++extern int journal_init_revoke_caches(void);
++
++extern void journal_destroy_revoke(journal_t *);
++extern int journal_revoke (handle_t *,
++ unsigned long, struct buffer_head *);
++extern int journal_cancel_revoke(handle_t *, struct journal_head *);
++extern void journal_write_revoke_records(journal_t *, transaction_t *);
++
++/* 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);
++
++/* The log thread user interface:
++ *
++ * Request space in the current transaction, and force transaction commit
++ * transitions on demand.
++ */
++
++extern int log_space_left (journal_t *); /* Called with journal locked */
++extern tid_t log_start_commit (journal_t *, transaction_t *);
++extern void log_wait_commit (journal_t *, tid_t);
++extern int log_do_checkpoint (journal_t *, int);
++
++extern void log_wait_for_space(journal_t *, int nblocks);
++extern void __journal_drop_transaction(journal_t *, transaction_t *);
++extern int cleanup_journal_tail(journal_t *);
++
++/* Reduce journal memory usage by flushing */
++extern void shrink_journal_memory(void);
++
++/* Debugging code only: */
++
++#define jbd_ENOSYS() \
++do { \
++ printk (KERN_ERR "JBD unimplemented function " __FUNCTION__); \
++ current->state = TASK_UNINTERRUPTIBLE; \
++ schedule(); \
++} while (1)
++
++/*
++ * is_journal_abort
++ *
++ * Simple test wrapper function to test the JFS_ABORT state flag. This
++ * bit, when set, indicates that we have had a fatal error somewhere,
++ * either inside the journaling layer or indicated to us by the client
++ * (eg. ext3), and that we and should not commit any further
++ * transactions.
++ */
++
++static inline int is_journal_aborted(journal_t *journal)
++{
++ return journal->j_flags & JFS_ABORT;
++}
++
++static inline int is_handle_aborted(handle_t *handle)
++{
++ if (handle->h_aborted)
++ return 1;
++ return is_journal_aborted(handle->h_transaction->t_journal);
++}
++
++static inline void journal_abort_handle(handle_t *handle)
++{
++ handle->h_aborted = 1;
++}
++
++/* Not all architectures define BUG() */
++#ifndef BUG
++#define BUG() do { \
++ printk("kernel BUG at %s:%d!\n", __FILE__, __LINE__); \
++ * ((char *) 0) = 0; \
++ } while (0)
++#endif /* BUG */
++
++#else
++
++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 /* __KERNEL__ */
++
++/* Comparison functions for transaction IDs: perform comparisons using
++ * modulo arithmetic so that they work over sequence number wraps. */
++
++static inline int tid_gt(tid_t x, tid_t y)
++{
++ int difference = (x - y);
++ return (difference > 0);
++}
++
++static inline int tid_geq(tid_t x, tid_t y)
++{
++ int difference = (x - y);
++ return (difference >= 0);
++}
++
++extern int journal_blocks_per_page(struct inode *inode);
++
++/*
++ * 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
++
++extern int jbd_blocks_per_page(struct inode *inode);
++
++#ifdef __KERNEL__
++
++extern spinlock_t jh_splice_lock;
++/*
++ * Once `expr1' has been found true, take jh_splice_lock
++ * and then reevaluate everything.
++ */
++#define SPLICE_LOCK(expr1, expr2) \
++ ({ \
++ int ret = (expr1); \
++ if (ret) { \
++ spin_lock(&jh_splice_lock); \
++ ret = (expr1) && (expr2); \
++ spin_unlock(&jh_splice_lock); \
++ } \
++ ret; \
++ })
++
++/*
++ * A number of buffer state predicates. They test for
++ * buffer_jbd() because they are used in core kernel code.
++ *
++ * These will be racy on SMP unless we're *sure* that the
++ * buffer won't be detached from the journalling system
++ * in parallel.
++ */
++
++/* Return true if the buffer is on journal list `list' */
++static inline int buffer_jlist_eq(struct buffer_head *bh, int list)
++{
++ return SPLICE_LOCK(buffer_jbd(bh), bh2jh(bh)->b_jlist == list);
++}
++
++/* Return true if this bufer is dirty wrt the journal */
++static inline int buffer_jdirty(struct buffer_head *bh)
++{
++ return buffer_jbd(bh) && __buffer_state(bh, JBDDirty);
++}
++
++/* Return true if it's a data buffer which journalling is managing */
++static inline int buffer_jbd_data(struct buffer_head *bh)
++{
++ return SPLICE_LOCK(buffer_jbd(bh),
++ bh2jh(bh)->b_jlist == BJ_SyncData ||
++ bh2jh(bh)->b_jlist == BJ_AsyncData);
++}
++
++#ifdef CONFIG_SMP
++#define assert_spin_locked(lock) J_ASSERT(spin_is_locked(lock))
++#else
++#define assert_spin_locked(lock) do {} while(0)
++#endif
++
++#define buffer_trace_init(bh) do {} while (0)
++#define print_buffer_fields(bh) do {} while (0)
++#define print_buffer_trace(bh) do {} while (0)
++#define BUFFER_TRACE(bh, info) do {} while (0)
++#define BUFFER_TRACE2(bh, bh2, info) do {} while (0)
++#define JBUFFER_TRACE(jh, info) do {} while (0)
++
++#endif /* __KERNEL__ */
++
++#endif /* CONFIG_JBD || CONFIG_JBD_MODULE || !__KERNEL__ */
++
++/*
++ * Compatibility no-ops which allow the kernel to compile without CONFIG_JBD
++ * go here.
++ */
++
++#if defined(__KERNEL__) && !(defined(CONFIG_JBD) || defined(CONFIG_JBD_MODULE))
++
++#define J_ASSERT(expr) do {} while (0)
++#define J_ASSERT_BH(bh, expr) do {} while (0)
++#define buffer_jbd(bh) 0
++#define buffer_jlist_eq(bh, val) 0
++#define journal_buffer_journal_lru(bh) 0
++
++#endif /* defined(__KERNEL__) && !defined(CONFIG_JBD) */
++#endif /* _LINUX_JBD_H */
+diff -Nur busybox-1.00/e2fsprogs/ext2fs/kernel-list.h busybox/e2fsprogs/ext2fs/kernel-list.h
+--- busybox-1.00/e2fsprogs/ext2fs/kernel-list.h 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/kernel-list.h 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,112 @@
++#ifndef _LINUX_LIST_H
++#define _LINUX_LIST_H
++
++/*
++ * 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 -Nur busybox-1.00/e2fsprogs/ext2fs/link.c busybox/e2fsprogs/ext2fs/link.c
+--- busybox-1.00/e2fsprogs/ext2fs/link.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/link.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,134 @@
++/*
++ * 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 <stdio.h>
++#include <string.h>
++#if HAVE_UNISTD_H
++#include <unistd.h>
++#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 -Nur busybox-1.00/e2fsprogs/ext2fs/llseek.c busybox/e2fsprogs/ext2fs/llseek.c
+--- busybox-1.00/e2fsprogs/ext2fs/llseek.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/llseek.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,135 @@
++/*
++ * llseek.c -- stub calling the llseek system call
++ *
++ * Copyright (C) 1994, 1995, 1996, 1997 Theodore Ts'o.
++ *
++ * %Begin-Header%
++ * This file may be redistributed under the terms of the GNU Public
++ * License.
++ * %End-Header%
++ */
++
++#if HAVE_SYS_TYPES_H
++#include <sys/types.h>
++#endif
++
++#if HAVE_ERRNO_H
++#include <errno.h>
++#endif
++#if HAVE_UNISTD_H
++#include <unistd.h>
++#endif
++#ifdef __MSDOS__
++#include <io.h>
++#endif
++#include "et/com_err.h"
++#include "ext2fs/ext2_io.h"
++
++#ifdef __linux__
++
++#if defined(HAVE_LSEEK64) && defined(HAVE_LSEEK64_PROTOTYPE)
++
++#define my_llseek lseek64
++
++#else
++#if defined(HAVE_LLSEEK)
++#include <syscall.h>
++
++#ifndef HAVE_LLSEEK_PROTOTYPE
++extern long long llseek (int fd, long long offset, int origin);
++#endif
++
++#define my_llseek llseek
++
++#else /* ! HAVE_LLSEEK */
++
++#if defined(__alpha__) || defined (__ia64__)
++
++#define llseek lseek
++
++#else /* !__alpha__ && !__ia64__*/
++
++#include <linux/unistd.h>
++
++#ifndef __NR__llseek
++#define __NR__llseek 140
++#endif
++
++#ifndef __i386__
++static int _llseek (unsigned int, unsigned long,
++ unsigned long, ext2_loff_t *, unsigned int);
++
++static _syscall5(int,_llseek,unsigned int,fd,unsigned long,offset_high,
++ unsigned long, offset_low,ext2_loff_t *,result,
++ unsigned int, origin)
++#endif
++
++static ext2_loff_t my_llseek (int fd, ext2_loff_t offset, int origin)
++{
++ ext2_loff_t result;
++ int retval;
++
++#ifndef __i386__
++ retval = _llseek(fd, ((unsigned long long) offset) >> 32,
++#else
++ retval = syscall(__NR__llseek, fd, (unsigned long long) (offset >> 32),
++#endif
++ ((unsigned long long) offset) & 0xffffffff,
++ &result, origin);
++ return (retval == -1 ? (ext2_loff_t) retval : result);
++}
++
++#endif /* __alpha__ || __ia64__ */
++
++#endif /* HAVE_LLSEEK */
++#endif /* defined(HAVE_LSEEK64) && defined(HAVE_LSEEK64_PROTOTYPE) */
++
++ext2_loff_t ext2fs_llseek (int fd, ext2_loff_t offset, int origin)
++{
++ ext2_loff_t result;
++ static int do_compat = 0;
++
++ if ((sizeof(off_t) >= sizeof(ext2_loff_t)) ||
++ (offset < ((ext2_loff_t) 1 << ((sizeof(off_t)*8) -1))))
++ return lseek(fd, (off_t) offset, origin);
++
++ if (do_compat) {
++ errno = EINVAL;
++ return -1;
++ }
++
++ result = my_llseek (fd, offset, origin);
++ if (result == -1 && errno == ENOSYS) {
++ /*
++ * Just in case this code runs on top of an old kernel
++ * which does not support the llseek system call
++ */
++ do_compat++;
++ errno = EINVAL;
++ }
++ return result;
++}
++
++#else /* !linux */
++
++#ifndef EINVAL
++#define EINVAL EXT2_ET_INVALID_ARGUMENT
++#endif
++
++ext2_loff_t ext2fs_llseek (int fd, ext2_loff_t offset, int origin)
++{
++#if defined(HAVE_LSEEK64) && defined(HAVE_LSEEK64_PROTOTYPE)
++ return lseek64 (fd, offset, origin);
++#else
++ if ((sizeof(off_t) < sizeof(ext2_loff_t)) &&
++ (offset >= ((ext2_loff_t) 1 << ((sizeof(off_t)*8) -1)))) {
++ errno = EINVAL;
++ return -1;
++ }
++ return lseek (fd, (off_t) offset, origin);
++#endif
++}
++
++#endif /* linux */
++
++
+diff -Nur busybox-1.00/e2fsprogs/ext2fs/lookup.c busybox/e2fsprogs/ext2fs/lookup.c
+--- busybox-1.00/e2fsprogs/ext2fs/lookup.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/lookup.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,69 @@
++/*
++ * 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 <stdio.h>
++#include <string.h>
++#if HAVE_UNISTD_H
++#include <unistd.h>
++#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 -Nur busybox-1.00/e2fsprogs/ext2fs/mkdir.c busybox/e2fsprogs/ext2fs/mkdir.c
+--- busybox-1.00/e2fsprogs/ext2fs/mkdir.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/mkdir.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,142 @@
++/*
++ * 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 <stdio.h>
++#include <string.h>
++#if HAVE_UNISTD_H
++#include <unistd.h>
++#endif
++#include <fcntl.h>
++#include <time.h>
++#if HAVE_SYS_STAT_H
++#include <sys/stat.h>
++#endif
++#if HAVE_SYS_TYPES_H
++#include <sys/types.h>
++#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 = 0;
++
++ 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:
++ if (block)
++ ext2fs_free_mem(&block);
++ return retval;
++
++}
++
++
+diff -Nur busybox-1.00/e2fsprogs/ext2fs/mkjournal.c busybox/e2fsprogs/ext2fs/mkjournal.c
+--- busybox-1.00/e2fsprogs/ext2fs/mkjournal.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/mkjournal.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,425 @@
++/*
++ * 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 <stdio.h>
++#include <string.h>
++#if HAVE_UNISTD_H
++#include <unistd.h>
++#endif
++#if HAVE_ERRNO_H
++#include <errno.h>
++#endif
++#include <fcntl.h>
++#include <time.h>
++#if HAVE_SYS_STAT_H
++#include <sys/stat.h>
++#endif
++#if HAVE_SYS_TYPES_H
++#include <sys/types.h>
++#endif
++#if HAVE_SYS_IOCTL_H
++#include <sys/ioctl.h>
++#endif
++#if HAVE_NETINET_IN_H
++#include <netinet/in.h>
++#endif
++
++#include "ext2_fs.h"
++#include "e2p/e2p.h"
++#include "ext2fs.h"
++#include "jfs_user.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 = 0;
++ 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(0);
++ 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;
++
++ if ((retval = ext2fs_check_mount_point(fs->device_name, &mount_flags,
++ jfile, sizeof(jfile)-10)))
++ 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 -Nur busybox-1.00/e2fsprogs/ext2fs/namei.c busybox/e2fsprogs/ext2fs/namei.c
+--- busybox-1.00/e2fsprogs/ext2fs/namei.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/namei.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,205 @@
++/*
++ * 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 <stdio.h>
++#include <string.h>
++#if HAVE_UNISTD_H
++#include <unistd.h>
++#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 = 0;
++ 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);
++ if (buffer)
++ 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 -Nur busybox-1.00/e2fsprogs/ext2fs/native.c busybox/e2fsprogs/ext2fs/native.c
+--- busybox-1.00/e2fsprogs/ext2fs/native.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/native.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,27 @@
++/*
++ * native.c --- returns the ext2_flag for a native byte order
++ *
++ * Copyright (C) 1996 Theodore Ts'o.
++ *
++ * %Begin-Header%
++ * This file may be redistributed under the terms of the GNU Public
++ * License.
++ * %End-Header%
++ */
++
++#include <stdio.h>
++
++#include "ext2_fs.h"
++#include "ext2fs.h"
++
++int ext2fs_native_flag(void)
++{
++#ifdef WORDS_BIGENDIAN
++ return EXT2_FLAG_SWAP_BYTES;
++#else
++ return 0;
++#endif
++}
++
++
++
+diff -Nur busybox-1.00/e2fsprogs/ext2fs/newdir.c busybox/e2fsprogs/ext2fs/newdir.c
+--- busybox-1.00/e2fsprogs/ext2fs/newdir.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/newdir.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,72 @@
++/*
++ * 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 <stdio.h>
++#include <string.h>
++#if HAVE_UNISTD_H
++#include <unistd.h>
++#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 -Nur busybox-1.00/e2fsprogs/ext2fs/openfs.c busybox/e2fsprogs/ext2fs/openfs.c
+--- busybox-1.00/e2fsprogs/ext2fs/openfs.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/openfs.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,326 @@
++/*
++ * 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 <stdio.h>
++#include <string.h>
++#if HAVE_UNISTD_H
++#include <unistd.h>
++#endif
++#include <fcntl.h>
++#include <time.h>
++#if HAVE_SYS_STAT_H
++#include <sys/stat.h>
++#endif
++#if HAVE_SYS_TYPES_H
++#include <sys/types.h>
++#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 j, groups_per_block, blocks_per_group;
++ blk_t group_block, blk;
++ char *dest, *cp;
++ struct ext2_group_desc *gdp;
++
++ 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);
++
++#ifdef EXT2FS_ENABLE_SWAPFS
++ 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;
++#ifdef EXT2FS_ENABLE_SWAPFS
++ 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 -Nur busybox-1.00/e2fsprogs/ext2fs/read_bb.c busybox/e2fsprogs/ext2fs/read_bb.c
+--- busybox-1.00/e2fsprogs/ext2fs/read_bb.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/read_bb.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,97 @@
++/*
++ * 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 <stdio.h>
++#include <string.h>
++#if HAVE_UNISTD_H
++#include <unistd.h>
++#endif
++#include <fcntl.h>
++#include <time.h>
++#if HAVE_SYS_STAT_H
++#include <sys/stat.h>
++#endif
++#if HAVE_SYS_TYPES_H
++#include <sys/types.h>
++#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 -Nur busybox-1.00/e2fsprogs/ext2fs/read_bb_file.c busybox/e2fsprogs/ext2fs/read_bb_file.c
+--- busybox-1.00/e2fsprogs/ext2fs/read_bb_file.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/read_bb_file.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,97 @@
++/*
++ * 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 <stdio.h>
++#include <string.h>
++#if HAVE_UNISTD_H
++#include <unistd.h>
++#endif
++#include <fcntl.h>
++#include <time.h>
++#if HAVE_SYS_STAT_H
++#include <sys/stat.h>
++#endif
++#if HAVE_SYS_TYPES_H
++#include <sys/types.h>
++#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 -Nur busybox-1.00/e2fsprogs/ext2fs/res_gdt.c busybox/e2fsprogs/ext2fs/res_gdt.c
+--- busybox-1.00/e2fsprogs/ext2fs/res_gdt.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/res_gdt.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,220 @@
++/*
++ * 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 <stdio.h>
++#include <string.h>
++#include <time.h>
++#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(0);
++ }
++
++ 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(0);
++ retval2 = ext2fs_write_inode(fs, EXT2_RESIZE_INO, &inode);
++ if (!retval)
++ retval = retval2;
++ }
++out_free:
++ ext2fs_free_mem((void **)&dindir_buf);
++ return retval;
++}
++
+diff -Nur busybox-1.00/e2fsprogs/ext2fs/rs_bitmap.c busybox/e2fsprogs/ext2fs/rs_bitmap.c
+--- busybox-1.00/e2fsprogs/ext2fs/rs_bitmap.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/rs_bitmap.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,106 @@
++/*
++ * 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 <stdio.h>
++#include <string.h>
++#if HAVE_UNISTD_H
++#include <unistd.h>
++#endif
++#include <fcntl.h>
++#include <time.h>
++#ifdef HAVE_SYS_STAT_H
++#include <sys/stat.h>
++#endif
++#ifdef HAVE_SYS_TYPES_H
++#include <sys/types.h>
++#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 -Nur busybox-1.00/e2fsprogs/ext2fs/rw_bitmaps.c busybox/e2fsprogs/ext2fs/rw_bitmaps.c
+--- busybox-1.00/e2fsprogs/ext2fs/rw_bitmaps.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/rw_bitmaps.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,300 @@
++/*
++ * 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 <stdio.h>
++#include <string.h>
++#if HAVE_UNISTD_H
++#include <unistd.h>
++#endif
++#include <fcntl.h>
++#include <time.h>
++#ifdef HAVE_SYS_STAT_H
++#include <sys/stat.h>
++#endif
++#ifdef HAVE_SYS_TYPES_H
++#include <sys/types.h>
++#endif
++
++#include "ext2_fs.h"
++#include "ext2fs.h"
++#include "e2image.h"
++
++#if defined(__powerpc__) && defined(EXT2FS_ENABLE_SWAPFS)
++/*
++ * 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 = 0, *inode_bitmap = 0;
++ 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) {
++ if (fs->block_map)
++ 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) {
++ if (fs->inode_map)
++ 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);
++ fs->block_map = 0;
++ }
++ if (do_inode) {
++ ext2fs_free_mem(&fs->inode_map);
++ fs->inode_map = 0;
++ }
++ if (buf)
++ 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 -Nur busybox-1.00/e2fsprogs/ext2fs/sparse.c busybox/e2fsprogs/ext2fs/sparse.c
+--- busybox-1.00/e2fsprogs/ext2fs/sparse.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/sparse.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,78 @@
++/*
++ * 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 <stdio.h>
++
++#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 -Nur busybox-1.00/e2fsprogs/ext2fs/swapfs.c busybox/e2fsprogs/ext2fs/swapfs.c
+--- busybox-1.00/e2fsprogs/ext2fs/swapfs.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/swapfs.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,237 @@
++/*
++ * 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 <stdio.h>
++#if HAVE_UNISTD_H
++#include <unistd.h>
++#endif
++#include <string.h>
++#include <time.h>
++
++#include "ext2_fs.h"
++#include "ext2fs.h"
++#include <ext2fs/ext2_ext_attr.h>
++
++#ifdef EXT2FS_ENABLE_SWAPFS
++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 -Nur busybox-1.00/e2fsprogs/ext2fs/test_io.c busybox/e2fsprogs/ext2fs/test_io.c
+--- busybox-1.00/e2fsprogs/ext2fs/test_io.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/test_io.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,382 @@
++/*
++ * 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 <stdio.h>
++#include <string.h>
++#if HAVE_UNISTD_H
++#include <unistd.h>
++#endif
++#include <fcntl.h>
++#include <time.h>
++#if HAVE_SYS_STAT_H
++#include <sys/stat.h>
++#endif
++#if HAVE_SYS_TYPES_H
++#include <sys/types.h>
++#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(value, "w");
++ 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:
++ if (io)
++ ext2fs_free_mem(&io);
++ if (data)
++ 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);
++ if (channel->name)
++ 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 -Nur busybox-1.00/e2fsprogs/ext2fs/unix_io.c busybox/e2fsprogs/ext2fs/unix_io.c
+--- busybox-1.00/e2fsprogs/ext2fs/unix_io.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/unix_io.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,707 @@
++/*
++ * 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 <stdio.h>
++#include <string.h>
++#if HAVE_UNISTD_H
++#include <unistd.h>
++#endif
++#if HAVE_ERRNO_H
++#include <errno.h>
++#endif
++#include <fcntl.h>
++#include <time.h>
++#ifdef __linux__
++#include <sys/utsname.h>
++#endif
++#if HAVE_SYS_STAT_H
++#include <sys/stat.h>
++#endif
++#if HAVE_SYS_TYPES_H
++#include <sys/types.h>
++#endif
++#if HAVE_SYS_RESOURCE_H
++#include <sys/resource.h>
++#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 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;
++ if (cache->buf)
++ 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);
++ }
++ if (io)
++ 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);
++ if (channel->name)
++ 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 -Nur busybox-1.00/e2fsprogs/ext2fs/unlink.c busybox/e2fsprogs/ext2fs/unlink.c
+--- busybox-1.00/e2fsprogs/ext2fs/unlink.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/unlink.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,99 @@
++/*
++ * 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 <stdio.h>
++#include <string.h>
++#if HAVE_UNISTD_H
++#include <unistd.h>
++#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 -Nur busybox-1.00/e2fsprogs/ext2fs/valid_blk.c busybox/e2fsprogs/ext2fs/valid_blk.c
+--- busybox-1.00/e2fsprogs/ext2fs/valid_blk.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/valid_blk.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,56 @@
++/*
++ * 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 <stdio.h>
++#if HAVE_UNISTD_H
++#include <unistd.h>
++#endif
++#include <string.h>
++#include <time.h>
++
++#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 -Nur busybox-1.00/e2fsprogs/ext2fs/version.c busybox/e2fsprogs/ext2fs/version.c
+--- busybox-1.00/e2fsprogs/ext2fs/version.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/version.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,52 @@
++/*
++ * 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 <unistd.h>
++#endif
++#include <string.h>
++#include <stdio.h>
++#include <ctype.h>
++
++#include "ext2_fs.h"
++#include "ext2fs.h"
++
++//#include "../../version.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 -Nur busybox-1.00/e2fsprogs/ext2fs/write_bb_file.c busybox/e2fsprogs/ext2fs/write_bb_file.c
+--- busybox-1.00/e2fsprogs/ext2fs/write_bb_file.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/ext2fs/write_bb_file.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,34 @@
++/*
++ * 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 <stdio.h>
++
++#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 -Nur busybox-1.00/e2fsprogs/lsattr.c busybox/e2fsprogs/lsattr.c
+--- busybox-1.00/e2fsprogs/lsattr.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/lsattr.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,141 @@
++/*
++ * lsattr.c - List file attributes on an ext2 file system
++ *
++ * Copyright (C) 1993, 1994 Remy Card <card@masi.ibp.fr>
++ * 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 <sys/types.h>
++#include <dirent.h>
++#include <errno.h>
++#include <fcntl.h>
++#include <getopt.h>
++#include <stdio.h>
++#include <unistd.h>
++#include <stdlib.h>
++#include <string.h>
++#include <sys/param.h>
++#include <sys/stat.h>
++
++#include <ext2fs/ext2_fs.h>
++#include "e2fsbb.h"
++#include "e2p/e2p.h"
++
++#ifdef __GNUC__
++# define EXT2FS_ATTR(x) __attribute__(x)
++#else
++# define EXT2FS_ATTR(x)
++#endif
++
++#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;
++
++#ifdef CONFIG_LFS
++# define LSTAT lstat64
++# define STRUCT_STAT struct stat64
++#else
++# define LSTAT lstat
++# define STRUCT_STAT struct stat
++#endif
++
++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_flags(stdout, fsflags, PFOPT_LONG);
++ printf("\n");
++ } else {
++ print_flags(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 EXT2FS_ATTR((unused)))
++{
++ 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);
++ printf("\n");
++ }
++ }
++ }
++
++ free(path);
++
++ return 0;
++}
++
++int lsattr_main(int argc, char **argv)
++{
++ int i;
++
++ flags = bb_getopt_ulflags(argc, argv, "Radlv");
++
++ if (optind > argc - 1)
++ lsattr_args(".");
++ else
++ for (i = optind; i < argc; i++)
++ lsattr_args(argv[i]);
++
++ return EXIT_SUCCESS;
++}
+diff -Nur busybox-1.00/e2fsprogs/util.c busybox/e2fsprogs/util.c
+--- busybox-1.00/e2fsprogs/util.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/util.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,207 @@
++/*
++ * util.c --- helper functions used by tune2fs and mke2fs
++ *
++ * Copyright 1995, 1996, 1997, 1998, 1999, 2000 by Theodore Ts'o.
++ *
++ * %Begin-Header%
++ * This file may be redistributed under the terms of the GNU Public
++ * License.
++ * %End-Header%
++ */
++
++#include <stdio.h>
++#include <string.h>
++#include <errno.h>
++#include <linux/major.h>
++#include <sys/stat.h>
++
++#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 val;
++#ifdef CONFIG_LFS
++ struct stat64 s;
++ val = stat64(device, &s);
++#else
++ struct stat s;
++ val = stat(device, &s);
++#endif
++
++ if(val == -1)
++ bb_perror_msg_and_die("Could not 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("Could not determine if %s is mounted", device);
++ return;
++ }
++ if (!(mount_flags & EXT2_MF_MOUNTED))
++ return;
++
++ bb_error_msg("%s is mounted !", device);
++ if (force)
++ bb_error_msg("forcing anyways and ignoring /etc/mtab status");
++ else
++ bb_error_msg_and_die("will not make a %s here!", type);
++}
++
++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;
++#if 0
++ int len;
++ len = strlen(opts);
++ buf = xmalloc(len+1);
++ strcpy(buf, opts);
++#else
++ buf = bb_xstrdup(opts);
++#endif
++ 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=<journal size in megabytes>\n"
++ "\tdevice=<journal device>\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 < 262144)
++ j_blocks = 4096;
++ else
++ j_blocks = 8192;
++
++ 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));
++}
+diff -Nur busybox-1.00/e2fsprogs/util.h busybox/e2fsprogs/util.h
+--- busybox-1.00/e2fsprogs/util.h 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/util.h 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,18 @@
++/*
++ * 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);
++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);
+diff -Nur busybox-1.00/e2fsprogs/uuid/clear.c busybox/e2fsprogs/uuid/clear.c
+--- busybox-1.00/e2fsprogs/uuid/clear.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/uuid/clear.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,43 @@
++/*
++ * clear.c -- Clear a 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 "string.h"
++
++#include "uuidP.h"
++
++void uuid_clear(uuid_t uu)
++{
++ memset(uu, 0, 16);
++}
++
+diff -Nur busybox-1.00/e2fsprogs/uuid/compare.c busybox/e2fsprogs/uuid/compare.c
+--- busybox-1.00/e2fsprogs/uuid/compare.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/uuid/compare.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,55 @@
++/*
++ * 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 <string.h>
++
++#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 -Nur busybox-1.00/e2fsprogs/uuid/copy.c busybox/e2fsprogs/uuid/copy.c
+--- busybox-1.00/e2fsprogs/uuid/copy.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/uuid/copy.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,45 @@
++/*
++ * copy.c --- copy 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 "uuidP.h"
++
++void uuid_copy(uuid_t dst, const uuid_t src)
++{
++ unsigned char *cp1;
++ const unsigned char *cp2;
++ int i;
++
++ for (i=0, cp1 = dst, cp2 = src; i < 16; i++)
++ *cp1++ = *cp2++;
++}
+diff -Nur busybox-1.00/e2fsprogs/uuid/gen_uuid.c busybox/e2fsprogs/uuid/gen_uuid.c
+--- busybox-1.00/e2fsprogs/uuid/gen_uuid.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/uuid/gen_uuid.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,310 @@
++/*
++ * 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%
++ */
++
++#ifdef HAVE_UNISTD_H
++#include <unistd.h>
++#endif
++#ifdef HAVE_STDLIB_H
++#include <stdlib.h>
++#endif
++#include <string.h>
++#include <fcntl.h>
++#include <errno.h>
++#include <sys/types.h>
++#include <sys/time.h>
++#include <sys/stat.h>
++#include <sys/file.h>
++#ifdef HAVE_SYS_IOCTL_H
++#include <sys/ioctl.h>
++#endif
++#ifdef HAVE_SYS_SOCKET_H
++#include <sys/socket.h>
++#endif
++#ifdef HAVE_SYS_SOCKIO_H
++#include <sys/sockio.h>
++#endif
++#ifdef HAVE_NET_IF_H
++#include <net/if.h>
++#endif
++#ifdef HAVE_NETINET_IN_H
++#include <netinet/in.h>
++#endif
++#ifdef HAVE_NET_IF_DL_H
++#include <net/if_dl.h>
++#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;
++ return;
++}
++
++/*
++ * 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(ifr.ifr_name, ifrp->ifr_name, IFNAMSIZ);
++#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 -Nur busybox-1.00/e2fsprogs/uuid/isnull.c busybox/e2fsprogs/uuid/isnull.c
+--- busybox-1.00/e2fsprogs/uuid/isnull.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/uuid/isnull.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,48 @@
++/*
++ * isnull.c --- Check whether or not the UUID is null
++ *
++ * 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"
++
++/* Returns 1 if the uuid is the NULL uuid */
++int uuid_is_null(const uuid_t uu)
++{
++ const unsigned char *cp;
++ int i;
++
++ for (i=0, cp = uu; i < 16; i++)
++ if (*cp++)
++ return 0;
++ return 1;
++}
++
+diff -Nur busybox-1.00/e2fsprogs/uuid/pack.c busybox/e2fsprogs/uuid/pack.c
+--- busybox-1.00/e2fsprogs/uuid/pack.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/uuid/pack.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,69 @@
++/*
++ * 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 <string.h>
++#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 -Nur busybox-1.00/e2fsprogs/uuid/parse.c busybox/e2fsprogs/uuid/parse.c
+--- busybox-1.00/e2fsprogs/uuid/parse.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/uuid/parse.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,79 @@
++/*
++ * 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 <stdlib.h>
++#include <stdio.h>
++#include <ctype.h>
++#include <string.h>
++
++#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 -Nur busybox-1.00/e2fsprogs/uuid/unpack.c busybox/e2fsprogs/uuid/unpack.c
+--- busybox-1.00/e2fsprogs/uuid/unpack.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/uuid/unpack.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,63 @@
++/*
++ * 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 <string.h>
++#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 -Nur busybox-1.00/e2fsprogs/uuid/unparse.c busybox/e2fsprogs/uuid/unparse.c
+--- busybox-1.00/e2fsprogs/uuid/unparse.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/uuid/unparse.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,76 @@
++/*
++ * 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 <stdio.h>
++
++#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 -Nur busybox-1.00/e2fsprogs/uuid/uuid.h busybox/e2fsprogs/uuid/uuid.h
+--- busybox-1.00/e2fsprogs/uuid/uuid.h 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/uuid/uuid.h 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,101 @@
++/*
++ * 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
++
++#include <sys/types.h>
++#include <sys/time.h>
++#include <time.h>
++
++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 __attribute__ ((unused)) = {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);
++
++/* 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);
++
++/* 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);
++
++/* 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 -Nur busybox-1.00/e2fsprogs/uuid/uuidP.h busybox/e2fsprogs/uuid/uuidP.h
+--- busybox-1.00/e2fsprogs/uuid/uuidP.h 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/uuid/uuidP.h 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,63 @@
++/*
++ * 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%
++ */
++
++#ifdef HAVE_INTTYPES_H
++#include <inttypes.h>
++#else
++#include <uuid/uuid_types.h>
++#endif
++#include <sys/types.h>
++
++#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 -Nur busybox-1.00/e2fsprogs/uuid/uuid_time.c busybox/e2fsprogs/uuid/uuid_time.c
+--- busybox-1.00/e2fsprogs/uuid/uuid_time.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/e2fsprogs/uuid/uuid_time.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,161 @@
++/*
++ * 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 <stdio.h>
++#include <unistd.h>
++#include <stdlib.h>
++#include <sys/types.h>
++#include <sys/time.h>
++#include <time.h>
++
++#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:
++ printf("\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 -Nur busybox-1.00/editors/sed.c busybox/editors/sed.c
+--- busybox-1.00/editors/sed.c 2004-05-26 12:03:33.000000000 +0200
++++ busybox/editors/sed.c 2005-06-04 08:20:21.000000000 +0200
+@@ -34,7 +34,10 @@
+ resulting sed_cmd_t structures are appended to a linked list
+ (sed_cmd_head/sed_cmd_tail).
+
+- process_file() does actual sedding, reading data lines from an input FILE *
++ add_input_file() adds a FILE * to the list of input files. We need to
++ know them all ahead of time to find the last line for the $ match.
++
++ process_files() does actual sedding, reading data lines from each input FILE *
+ (which could be stdin) and applying the sed command list (sed_cmd_head) to
+ each of the resulting lines.
+
+@@ -54,7 +57,7 @@
+ - grouped commands: {cmd1;cmd2}
+ - transliteration (y/source-chars/dest-chars/)
+ - pattern space hold space storing / swapping (g, h, x)
+- - labels / branching (: label, b, t)
++ - labels / branching (: label, b, t, T)
+
+ (Note: Specifying an address (range) to match is *optional*; commands
+ default to the whole pattern space if no specific address match was
+@@ -62,7 +65,7 @@
+
+ Unsupported features:
+
+- - GNU extensions
++ - most GNU extensions
+ - and more.
+
+ Todo:
+@@ -112,17 +115,20 @@
+
+ /* globals */
+ /* options */
+-static int be_quiet = 0, in_place=0, regex_type=0;
+-FILE *nonstdout;
+-char *outname;
+-
++static int be_quiet, in_place, regex_type;
++static FILE *nonstdout;
++static char *outname,*hold_space;
++
++/* List of input files */
++static int input_file_count,current_input_file;
++static FILE **input_file_list;
+
+ static const char bad_format_in_subst[] =
+ "bad format in substitution expression";
+-const char *const semicolon_whitespace = "; \n\r\t\v";
++static const char *const semicolon_whitespace = "; \n\r\t\v";
+
+-regmatch_t regmatch[10];
+-static regex_t *previous_regex_ptr = NULL;
++static regmatch_t regmatch[10];
++static regex_t *previous_regex_ptr;
+
+ /* linked list of sed commands */
+ static sed_cmd_t sed_cmd_head;
+@@ -133,7 +139,7 @@
+ char *string;
+ struct append_list *next;
+ };
+-struct append_list *append_head=NULL, *append_tail=NULL;
++static struct append_list *append_head=NULL, *append_tail=NULL;
+
+ #ifdef CONFIG_FEATURE_CLEAN_UP
+ static void free_and_close_stuff(void)
+@@ -169,6 +175,11 @@
+ free(sed_cmd);
+ sed_cmd = sed_cmd_next;
+ }
++
++ if(hold_space) free(hold_space);
++
++ while(current_input_file<input_file_count)
++ fclose(input_file_list[current_input_file++]);
+ }
+ #endif
+
+@@ -429,7 +440,7 @@
+ if(sed_cmd->cmd=='w')
+ sed_cmd->file=bb_xfopen(sed_cmd->string,"w");
+ /* handle branch commands */
+- } else if (strchr(":bt", sed_cmd->cmd)) {
++ } else if (strchr(":btT", sed_cmd->cmd)) {
+ int length;
+
+ while(isspace(*cmdstr)) cmdstr++;
+@@ -471,7 +482,7 @@
+
+ /* Parse address+command sets, skipping comment lines. */
+
+-void add_cmd(char *cmdstr)
++static void add_cmd(char *cmdstr)
+ {
+ static char *add_cmd_line=NULL;
+ sed_cmd_t *sed_cmd;
+@@ -563,7 +574,9 @@
+ }
+ }
+
+-struct pipeline {
++/* Append to a string, reallocating memory as necessary. */
++
++static struct pipeline {
+ char *buf; /* Space to hold string */
+ int idx; /* Space used */
+ int len; /* Space allocated */
+@@ -571,7 +584,7 @@
+
+ #define PIPE_GROW 64
+
+-void pipe_putc(char c)
++static void pipe_putc(char c)
+ {
+ if(pipeline.idx==pipeline.len) {
+ pipeline.buf = xrealloc(pipeline.buf, pipeline.len + PIPE_GROW);
+@@ -716,20 +729,29 @@
+ append_head=append_tail=NULL;
+ }
+
+-/* Get next line of input, flushing append buffer and noting if we hit EOF
+- * without a newline on the last line.
++static void add_input_file(FILE *file)
++{
++ input_file_list=xrealloc(input_file_list,(input_file_count+1)*sizeof(FILE *));
++ input_file_list[input_file_count++]=file;
++}
++
++/* Get next line of input from input_file_list, flushing append buffer and
++ * noting if we ran out of files without a newline on the last line we read.
+ */
+-static char *get_next_line(FILE * file, int *no_newline)
++static char *get_next_line(int *no_newline)
+ {
+- char *temp;
++ char *temp=NULL;
+ int len;
+
+ flush_append();
+- temp=bb_get_line_from_file(file);
+- if(temp) {
+- len=strlen(temp);
+- if(len && temp[len-1]=='\n') temp[len-1]=0;
+- else *no_newline=1;
++ while(current_input_file<input_file_count) {
++ temp=bb_get_line_from_file(input_file_list[current_input_file]);
++ if(temp) {
++ len=strlen(temp);
++ *no_newline=!(len && temp[len-1]=='\n');
++ if(!*no_newline) temp[len-1]=0;
++ break;
++ } else fclose(input_file_list[current_input_file++]);
+ }
+
+ return temp;
+@@ -755,15 +777,15 @@
+
+ #define sed_puts(s,n) missing_newline=puts_maybe_newline(s,nonstdout,missing_newline,n)
+
+-static void process_file(FILE *file)
++static void process_files(void)
+ {
+- char *pattern_space, *next_line, *hold_space=NULL;
+- static int linenum = 0, missing_newline=0;
++ char *pattern_space, *next_line;
++ int linenum = 0, missing_newline=0;
+ int no_newline,next_no_newline=0;
+
+- next_line = get_next_line(file,&next_no_newline);
++ next_line = get_next_line(&next_no_newline);
+
+- /* go through every line in the file */
++ /* go through every line in each file */
+ for(;;) {
+ sed_cmd_t *sed_cmd;
+ int substituted=0;
+@@ -773,7 +795,7 @@
+ no_newline=next_no_newline;
+
+ /* Read one line in advance so we can act on the last line, the '$' address */
+- next_line = get_next_line(file,&next_no_newline);
++ next_line = get_next_line(&next_no_newline);
+ linenum++;
+ restart:
+ /* for every line, go through all the commands */
+@@ -908,7 +930,7 @@
+ /* Cut and paste text (replace) */
+ case 'c':
+ /* Only triggers on last line of a matching range. */
+- if (!sed_cmd->in_match) sed_puts(sed_cmd->string,1);
++ if (!sed_cmd->in_match) sed_puts(sed_cmd->string,0);
+ goto discard_line;
+
+ /* Read file, append contents to output */
+@@ -942,7 +964,7 @@
+ free(pattern_space);
+ pattern_space = next_line;
+ no_newline=next_no_newline;
+- next_line = get_next_line(file,&next_no_newline);
++ next_line = get_next_line(&next_no_newline);
+ linenum++;
+ break;
+ }
+@@ -972,17 +994,21 @@
+ pattern_space[len]='\n';
+ strcpy(pattern_space+len+1, next_line);
+ no_newline=next_no_newline;
+- next_line = get_next_line(file,&next_no_newline);
++ next_line = get_next_line(&next_no_newline);
+ linenum++;
+ }
+ break;
+ }
+
+- /* Test if substition worked, branch if so. */
++ /* Test/branch if substitution occurred */
+ case 't':
+- if (!substituted) break;
++ if(!substituted) break;
+ substituted=0;
+- /* Fall through */
++ /* Fall through */
++ /* Test/branch if substitution didn't occur */
++ case 'T':
++ if (substituted) break;
++ /* Fall through */
+ /* Branch to label */
+ case 'b':
+ if (!sed_cmd->string) goto discard_commands;
+@@ -1007,10 +1033,7 @@
+ }
+ case 'g': /* Replace pattern space with hold space */
+ free(pattern_space);
+- if (hold_space) {
+- pattern_space = strdup(hold_space);
+- no_newline=0;
+- }
++ pattern_space = strdup(hold_space ? hold_space : "");
+ break;
+ case 'G': /* Append newline and hold space to pattern space */
+ {
+@@ -1096,9 +1119,7 @@
+
+ extern int sed_main(int argc, char **argv)
+ {
+- int status = EXIT_SUCCESS;
+- int opt;
+- uint8_t getpat = 1;
++ int status = EXIT_SUCCESS, opt, getpat = 1;
+
+ #ifdef CONFIG_FEATURE_CLEAN_UP
+ /* destroy command strings on exit */
+@@ -1153,8 +1174,7 @@
+ }
+ }
+
+- /* if we didn't get a pattern from a -e and no command file was specified,
+- * argv[optind] should be the pattern. no pattern, no worky */
++ /* if we didn't get a pattern from -e or -f, use argv[optind] */
+ if(getpat) {
+ if (argv[optind] == NULL)
+ bb_show_usage();
+@@ -1171,49 +1191,47 @@
+ * files were specified or '-' was specified, take input from stdin.
+ * Otherwise, we process all the files specified. */
+ if (argv[optind] == NULL) {
+- if(in_place) {
+- fprintf(stderr,"sed: Filename required for -i\n");
+- exit(1);
+- }
+- process_file(stdin);
++ if(in_place) bb_error_msg_and_die("Filename required for -i");
++ add_input_file(stdin);
++ process_files();
+ } else {
+ int i;
+ FILE *file;
+
+ for (i = optind; i < argc; i++) {
+ if(!strcmp(argv[i], "-") && !in_place) {
+- process_file(stdin);
++ add_input_file(stdin);
++ process_files();
+ } else {
+ file = bb_wfopen(argv[i], "r");
+ if (file) {
+ if(in_place) {
+ struct stat statbuf;
++ int nonstdoutfd;
++
+ outname=bb_xstrndup(argv[i],strlen(argv[i])+6);
+ strcat(outname,"XXXXXX");
++ if(-1==(nonstdoutfd=mkstemp(outname)))
++ bb_error_msg_and_die("no temp file");
++ nonstdout=fdopen(nonstdoutfd,"w");
+ /* Set permissions of output file */
+ fstat(fileno(file),&statbuf);
+- mkstemp(outname);
+- nonstdout=bb_wfopen(outname,"w");
+- /* Set permissions of output file */
+- fstat(fileno(file),&statbuf);
+- fchmod(fileno(nonstdout),statbuf.st_mode);
+- atexit(cleanup_outname);
+- }
+- process_file(file);
+- fclose(file);
+- if(in_place) {
++ fchmod(nonstdoutfd,statbuf.st_mode);
++ add_input_file(file);
++ process_files();
+ fclose(nonstdout);
+ nonstdout=stdout;
+ unlink(argv[i]);
+ rename(outname,argv[i]);
+ free(outname);
+ outname=0;
+- }
++ } else add_input_file(file);
+ } else {
+ status = EXIT_FAILURE;
+ }
+ }
+ }
++ if(input_file_count>current_input_file) process_files();
+ }
+
+ return status;
+diff -Nur busybox-1.00/editors/vi.c busybox/editors/vi.c
+--- busybox-1.00/editors/vi.c 2004-08-19 21:15:06.000000000 +0200
++++ busybox/editors/vi.c 2005-06-04 08:20:21.000000000 +0200
+@@ -19,7 +19,7 @@
+ */
+
+ static const char vi_Version[] =
+- "$Id$";
++ "$Id$";
+
+ /*
+ * To compile for standalone use:
+@@ -2340,7 +2340,7 @@
+ }
+
+ //----- IO Routines --------------------------------------------
+-static Byte get_one_char()
++static Byte get_one_char(void)
+ {
+ static Byte c;
+
+@@ -2600,25 +2600,25 @@
+ }
+
+ //----- Erase from cursor to end of line -----------------------
+-static void clear_to_eol()
++static void clear_to_eol(void)
+ {
+ write1(Ceol); // Erase from cursor to end of line
+ }
+
+ //----- Erase from cursor to end of screen -----------------------
+-static void clear_to_eos()
++static void clear_to_eos(void)
+ {
+ write1(Ceos); // Erase from cursor to end of screen
+ }
+
+ //----- Start standout mode ------------------------------------
+-static void standout_start() // send "start reverse video" sequence
++static void standout_start(void) // send "start reverse video" sequence
+ {
+ write1(SOs); // Start reverse video mode
+ }
+
+ //----- End standout mode --------------------------------------
+-static void standout_end() // send "end reverse video" sequence
++static void standout_end(void) // send "end reverse video" sequence
+ {
+ write1(SOn); // End reverse video mode
+ }
+@@ -2648,7 +2648,7 @@
+
+ //----- Screen[] Routines --------------------------------------
+ //----- Erase the Screen[] memory ------------------------------
+-static void screen_erase()
++static void screen_erase(void)
+ {
+ memset(screen, ' ', screensize); // clear new screen
+ }
+diff -Nur busybox-1.00/examples/depmod.pl busybox/examples/depmod.pl
+--- busybox-1.00/examples/depmod.pl 2004-03-15 09:28:33.000000000 +0100
++++ busybox/examples/depmod.pl 2005-06-04 08:20:22.000000000 +0200
+@@ -233,5 +233,5 @@
+
+ =cut
+
+-# $Id$
++# $Id$
+
+diff -Nur busybox-1.00/examples/zcip.script busybox/examples/zcip.script
+--- busybox-1.00/examples/zcip.script 1970-01-01 01:00:00.000000000 +0100
++++ busybox/examples/zcip.script 2005-06-04 08:20:22.000000000 +0200
+@@ -0,0 +1,38 @@
++#!/bin/sh
++
++# only for use as a "zcip" callback script
++if [ "x$interface" = x ]
++then
++ exit 1
++fi
++
++# zcip should start on boot/resume and various media changes
++case "$1" in
++init)
++ # for now, zcip requires the link to be already up,
++ # and it drops links when they go down. that isn't
++ # the most robust model...
++ exit 0
++ ;;
++config)
++ if [ "x$ip" = x ]
++ then
++ exit 1
++ fi
++ # remember $ip for $interface, to use on restart
++ if [ "x$IP" != x -a -w "$IP.$interface" ]
++ then
++ echo $ip > "$IP.$interface"
++ fi
++ exec ip address add dev $interface \
++ scope link local "$ip/16" broadcast +
++ ;;
++deconfig)
++ if [ x$ip = x ]
++ then
++ exit 1
++ fi
++ exec ip address del dev $interface local $ip
++ ;;
++esac
++exit 1
+diff -Nur busybox-1.00/findutils/find.c busybox/findutils/find.c
+--- busybox-1.00/findutils/find.c 2004-03-15 09:28:37.000000000 +0100
++++ busybox/findutils/find.c 2005-06-04 08:20:20.000000000 +0200
+@@ -59,7 +59,7 @@
+ #endif
+
+ #ifdef CONFIG_FEATURE_FIND_NEWER
+-time_t newer_mtime;
++static time_t newer_mtime;
+ #endif
+
+ #ifdef CONFIG_FEATURE_FIND_INUM
+diff -Nur busybox-1.00/findutils/grep.c busybox/findutils/grep.c
+--- busybox-1.00/findutils/grep.c 2004-10-08 10:10:57.000000000 +0200
++++ busybox/findutils/grep.c 2005-06-04 08:20:20.000000000 +0200
+@@ -98,7 +98,7 @@
+ }
+ last_line_printed = linenum;
+ #endif
+- if (print_filename)
++ if (print_filename > 0)
+ printf("%s%c", cur_file, decoration);
+ if (print_line_num)
+ printf("%i%c", linenum, decoration);
+@@ -219,7 +219,7 @@
+
+ /* grep -c: print [filename:]count, even if count is zero */
+ if (print_match_counts) {
+- if (print_filename)
++ if (print_filename > 0)
+ printf("%s:", cur_file);
+ printf("%d\n", nmatches);
+ }
+diff -Nur busybox-1.00/include/applets.h busybox/include/applets.h
+--- busybox-1.00/include/applets.h 2004-08-27 01:01:34.000000000 +0200
++++ busybox/include/applets.h 2005-06-04 08:20:17.000000000 +0200
+@@ -16,40 +16,41 @@
+
+
+ #if defined(PROTOTYPES)
+- #define APPLET(a,b,c,d) extern int b(int argc, char **argv);
+- #define APPLET_NOUSAGE(a,b,c,d) extern int b(int argc, char **argv);
+- #define APPLET_ODDNAME(a,b,c,d,e) extern int b(int argc, char **argv);
++# define APPLET(a,b,c,d) extern int b(int argc, char **argv);
++# define APPLET_NOUSAGE(a,b,c,d) extern int b(int argc, char **argv);
++# define APPLET_ODDNAME(a,b,c,d,e) extern int b(int argc, char **argv);
+ extern const char usage_messages[];
+ #elif defined(MAKE_USAGE)
+- #ifdef CONFIG_FEATURE_VERBOSE_USAGE
+- #define APPLET(a,b,c,d) a##_trivial_usage "\n\n" a##_full_usage "\0"
+- #define APPLET_NOUSAGE(a,b,c,d) "\b\0"
+- #define APPLET_ODDNAME(a,b,c,d,e) e##_trivial_usage "\n\n" e##_full_usage "\0"
+- #else
+- #define APPLET(a,b,c,d) a##_trivial_usage "\0"
+- #define APPLET_NOUSAGE(a,b,c,d) "\b\0"
+- #define APPLET_ODDNAME(a,b,c,d,e) e##_trivial_usage "\0"
+- #endif
++# ifdef CONFIG_FEATURE_VERBOSE_USAGE
++# define APPLET(a,b,c,d) a##_trivial_usage "\n\n" a##_full_usage "\0"
++# define APPLET_NOUSAGE(a,b,c,d) "\b\0"
++# define APPLET_ODDNAME(a,b,c,d,e) e##_trivial_usage "\n\n" e##_full_usage "\0"
++# else
++# define APPLET(a,b,c,d) a##_trivial_usage "\0"
++# define APPLET_NOUSAGE(a,b,c,d) "\b\0"
++# define APPLET_ODDNAME(a,b,c,d,e) e##_trivial_usage "\0"
++# endif
+ #elif defined(MAKE_LINKS)
+-# define APPLET(a,b,c,d) LINK c a
+-# define APPLET_NOUSAGE(a,b,c,d) LINK c a
+-# define APPLET_ODDNAME(a,b,c,d,e) LINK c a
++# define APPLET(a,b,c,d) LINK c a
++# define APPLET_NOUSAGE(a,b,c,d) LINK c a
++# define APPLET_ODDNAME(a,b,c,d,e) LINK c a
+ #else
+ const struct BB_applet applets[] = {
+- #define APPLET(a,b,c,d) {#a,b,c,d},
+- #define APPLET_NOUSAGE(a,b,c,d) {a,b,c,d},
+- #define APPLET_ODDNAME(a,b,c,d,e) {a,b,c,d},
++# define APPLET(a,b,c,d) {#a,b,c,d},
++# define APPLET_NOUSAGE(a,b,c,d) {a,b,c,d},
++# define APPLET_ODDNAME(a,b,c,d,e) {a,b,c,d},
+ #endif
+
+ #ifdef CONFIG_INSTALL_NO_USR
+-#define _BB_DIR_USR_BIN _BB_DIR_BIN
+-#define _BB_DIR_USR_SBIN _BB_DIR_SBIN
++# define _BB_DIR_USR_BIN _BB_DIR_BIN
++# define _BB_DIR_USR_SBIN _BB_DIR_SBIN
+ #endif
+
+
+
+ #ifdef CONFIG_TEST
+ APPLET_NOUSAGE("[", test_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
++ APPLET_NOUSAGE("[[", test_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+ #endif
+ #ifdef CONFIG_ADDGROUP
+ APPLET(addgroup, addgroup_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+@@ -88,6 +89,9 @@
+ #ifdef CONFIG_CAT
+ APPLET(cat, cat_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+ #endif
++#ifdef CONFIG_CHATTR
++ APPLET(chattr, chattr_main, _BB_DIR_BIN, _BB_SUID_NEVER)
++#endif
+ #ifdef CONFIG_CHGRP
+ APPLET(chgrp, chgrp_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+ #endif
+@@ -109,6 +113,9 @@
+ #ifdef CONFIG_CMP
+ APPLET(cmp, cmp_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+ #endif
++#ifdef CONFIG_COMM
++ APPLET(comm, comm_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
++#endif
+ #ifdef CONFIG_CP
+ APPLET(cp, cp_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+ #endif
+@@ -170,7 +177,7 @@
+ APPLET(dumpkmap, dumpkmap_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+ #endif
+ #ifdef CONFIG_DUMPLEASES
+- APPLET(dumpleases, dumpleases_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
++ APPLET(dumpleases, dumpleases_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+ #endif
+ #ifdef CONFIG_ECHO
+ APPLET(echo, echo_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+@@ -178,12 +185,21 @@
+ #if defined(CONFIG_FEATURE_GREP_EGREP_ALIAS)
+ APPLET_NOUSAGE("egrep", grep_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+ #endif
++#ifdef CONFIG_EJECT
++ APPLET(eject, eject_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
++#endif
+ #ifdef CONFIG_ENV
+ APPLET(env, env_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+ #endif
++#ifdef CONFIG_ETHER_WAKE
++ APPLET_ODDNAME("ether-wake", etherwake_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER, ether_wake)
++#endif
+ #ifdef CONFIG_EXPR
+ APPLET(expr, expr_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+ #endif
++#ifdef CONFIG_FAKEIDENTD
++ APPLET(fakeidentd, fakeidentd_main, _BB_DIR_USR_SBIN, _BB_SUID_NEVER)
++#endif
+ #ifdef CONFIG_FALSE
+ APPLET(false, false_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+ #endif
+@@ -355,6 +371,9 @@
+ #ifdef CONFIG_LS
+ APPLET(ls, ls_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+ #endif
++#ifdef CONFIG_LSATTR
++ APPLET(lsattr, lsattr_main, _BB_DIR_BIN, _BB_SUID_NEVER)
++#endif
+ #ifdef CONFIG_LSMOD
+ APPLET(lsmod, lsmod_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
+ #endif
+@@ -412,6 +431,9 @@
+ #ifdef CONFIG_NETSTAT
+ APPLET(netstat, netstat_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+ #endif
++#ifdef CONFIG_NICE
++ APPLET(nice, nice_main, _BB_DIR_BIN, _BB_SUID_NEVER)
++#endif
+ #ifdef CONFIG_NSLOOKUP
+ APPLET(nslookup, nslookup_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+ #endif
+@@ -440,11 +462,14 @@
+ APPLET_NOUSAGE("pipe_progress", pipe_progress_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+ #endif
+ #ifdef CONFIG_PIVOT_ROOT
+- APPLET(pivot_root, pivot_root_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
++ APPLET(pivot_root, pivot_root_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
+ #endif
+ #ifdef CONFIG_POWEROFF
+ APPLET(poweroff, poweroff_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
+ #endif
++#ifdef CONFIG_PRINTENV
++ APPLET(printenv, printenv_main, _BB_DIR_BIN, _BB_SUID_NEVER)
++#endif
+ #ifdef CONFIG_PRINTF
+ APPLET(printf, printf_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+ #endif
+@@ -460,6 +485,9 @@
+ #ifdef CONFIG_READLINK
+ APPLET(readlink, readlink_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+ #endif
++#ifdef CONFIG_READPROFILE
++ APPLET(readprofile, readprofile_main, _BB_DIR_USR_SBIN, _BB_SUID_NEVER)
++#endif
+ #ifdef CONFIG_REALPATH
+ APPLET(realpath, realpath_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+ #endif
+@@ -482,7 +510,7 @@
+ APPLET(rmmod, rmmod_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
+ #endif
+ #ifdef CONFIG_ROUTE
+- APPLET(route, route_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
++ APPLET(route, route_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
+ #endif
+ #ifdef CONFIG_RPM
+ APPLET(rpm, rpm_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+@@ -524,7 +552,10 @@
+ APPLET(sort, sort_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+ #endif
+ #ifdef CONFIG_START_STOP_DAEMON
+- APPLET_ODDNAME("start-stop-daemon", start_stop_daemon_main, _BB_DIR_SBIN, _BB_SUID_NEVER, start_stop_daemon)
++ APPLET_ODDNAME("start-stop-daemon", start_stop_daemon_main, _BB_DIR_SBIN, _BB_SUID_NEVER, start_stop_daemon)
++#endif
++#ifdef CONFIG_STAT
++ APPLET(stat, stat_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+ #endif
+ #ifdef CONFIG_STRINGS
+ APPLET(strings, strings_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+@@ -538,6 +569,9 @@
+ #ifdef CONFIG_SULOGIN
+ APPLET(sulogin, sulogin_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
+ #endif
++#ifdef CONFIG_SUM
++ APPLET(sum, sum_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
++#endif
+ #ifdef CONFIG_SWAPONOFF
+ APPLET(swapoff, swap_on_off_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
+ #endif
+@@ -599,7 +633,7 @@
+ APPLET(udhcpc, udhcpc_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
+ #endif
+ #ifdef CONFIG_UDHCPD
+- APPLET(udhcpd, udhcpd_main, _BB_DIR_USR_SBIN, _BB_SUID_NEVER)
++ APPLET(udhcpd, udhcpd_main, _BB_DIR_USR_SBIN, _BB_SUID_NEVER)
+ #endif
+ #ifdef CONFIG_UMOUNT
+ APPLET(umount, umount_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+@@ -670,6 +704,9 @@
+ #ifdef CONFIG_GUNZIP
+ APPLET(zcat, gunzip_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+ #endif
++#ifdef CONFIG_ZCIP
++ APPLET(zcip, zcip_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
++#endif
+
+ #if !defined(PROTOTYPES) && !defined(MAKE_USAGE)
+ { 0,NULL,0 }
+diff -Nur busybox-1.00/include/busybox.h busybox/include/busybox.h
+--- busybox-1.00/include/busybox.h 2004-03-15 09:28:38.000000000 +0100
++++ busybox/include/busybox.h 2005-06-04 08:20:17.000000000 +0200
+@@ -32,10 +32,10 @@
+ #include <sys/stat.h>
+ #include <sys/types.h>
+
+-#if __GNU_LIBRARY__ < 5
+-#ifndef __dietlibc__
+-#error "Sorry, libc5 is not supported"
+-#endif
++#if __GNU_LIBRARY__ < 5 && \
++ !defined(__dietlibc__) && \
++ !defined(_NEWLIB_VERSION)
++#error "Sorry, this libc version is not supported :("
+ #endif
+
+ #ifndef BB_EXTRA_VERSION
+diff -Nur busybox-1.00/include/inet_common.h busybox/include/inet_common.h
+--- busybox-1.00/include/inet_common.h 2004-03-10 08:42:37.000000000 +0100
++++ busybox/include/inet_common.h 2005-06-04 08:20:17.000000000 +0200
+@@ -4,7 +4,7 @@
+ *
+ * Heavily modified by Manuel Novoa III Mar 12, 2001
+ *
+- * Version: $Id$
++ * Version: $Id$
+ *
+ */
+
+@@ -29,5 +29,7 @@
+ extern int INET_rresolve(char *name, size_t len, struct sockaddr_in *s_in,
+ int numeric, unsigned int netmask);
+
++#ifdef CONFIG_FEATURE_IPV6
+ extern int INET6_resolve(const char *name, struct sockaddr_in6 *sin6);
+ extern int INET6_rresolve(char *name, size_t len, struct sockaddr_in6 *sin6, int numeric);
++#endif
+diff -Nur busybox-1.00/include/libbb.h busybox/include/libbb.h
+--- busybox-1.00/include/libbb.h 2004-09-15 05:04:07.000000000 +0200
++++ busybox/include/libbb.h 2005-06-04 08:20:17.000000000 +0200
+@@ -43,7 +43,7 @@
+
+ #include "config.h"
+ #ifdef CONFIG_SELINUX
+-#include <proc_secure.h>
++#include <selinux/selinux.h>
+ #endif
+
+ #include "pwd_.h"
+@@ -137,6 +137,7 @@
+ extern char *find_real_root_device_name(void);
+ extern char *bb_get_line_from_file(FILE *file);
+ extern char *bb_get_chomped_line_from_file(FILE *file);
++extern char *bb_get_chunk_from_file(FILE *file);
+ extern int bb_copyfd_size(int fd1, int fd2, const off_t size);
+ extern int bb_copyfd_eof(int fd1, int fd2);
+ extern void bb_xprint_and_close_file(FILE *file);
+@@ -150,6 +151,7 @@
+ extern int bb_fclose_nonstdin(FILE *f);
+ extern void bb_fflush_stdout_and_exit(int retval) __attribute__ ((noreturn));
+
++#define BB_GETOPT_ERROR 0x80000000UL
+ extern const char *bb_opt_complementaly;
+ extern const struct option *bb_applet_long_options;
+ extern unsigned long bb_getopt_ulflags(int argc, char **argv, const char *applet_opts, ...);
+@@ -323,6 +325,7 @@
+ extern const char * const bb_msg_memory_exhausted;
+ extern const char * const bb_msg_invalid_date;
+ extern const char * const bb_msg_io_error;
++extern const char * const bb_msg_read_error;
+ extern const char * const bb_msg_write_error;
+ extern const char * const bb_msg_name_longer_than_foo;
+ extern const char * const bb_msg_unknown;
+@@ -421,11 +424,11 @@
+ #define FAIL_DELAY 3
+ extern void change_identity ( const struct passwd *pw );
+ extern const char *change_identity_e2str ( const struct passwd *pw );
+-extern void run_shell ( const char *shell, int loginshell, const char *command, const char **additional_args
++extern void run_shell ( const char *shell, int loginshell, const char *command, const char **additional_args);
+ #ifdef CONFIG_SELINUX
+- , security_id_t sid
+-#endif
+-);
++extern void renew_current_security_context(void);
++extern void set_current_security_context(security_context_t sid);
++#endif
+ extern int run_parts(char **args, const unsigned char test_mode, char **env);
+ extern int restricted_shell ( const char *shell );
+ extern void setup_environment ( const char *shell, int loginshell, int changeenv, const struct passwd *pw );
+@@ -456,11 +459,7 @@
+ char short_cmd[16];
+ } procps_status_t;
+
+-extern procps_status_t * procps_scan(int save_user_arg0
+-#ifdef CONFIG_SELINUX
+- , int use_selinux, security_id_t *sid
+-#endif
+-);
++extern procps_status_t * procps_scan(int save_user_arg0);
+ extern unsigned short compare_string_array(const char *string_array[], const char *key);
+
+ extern int my_query_module(const char *name, int which, void **buf, size_t *bufsize, size_t *ret);
+diff -Nur busybox-1.00/include/usage.h busybox/include/usage.h
+--- busybox-1.00/include/usage.h 2004-09-14 18:23:56.000000000 +0200
++++ busybox/include/usage.h 2005-06-04 08:20:17.000000000 +0200
+@@ -46,7 +46,7 @@
+ "\t-v\t\tverbosely list files processed"
+
+ #define arping_trivial_usage \
+- "[-fqbDUA] [-c count] [-w timeout] [-I device] [-s sender] target\n"
++ "[-fqbDUA] [-c count] [-w timeout] [-I device] [-s sender] target"
+ #define arping_full_usage \
+ "Ping hosts by ARP requests/replies.\n\n" \
+ "Options:\n" \
+@@ -64,7 +64,7 @@
+
+ #define ash_trivial_usage \
+ "[FILE]...\n" \
+- "or: ash -c command [args]...\n"
++ "or: ash -c command [args]..."
+ #define ash_full_usage \
+ "The ash shell (command interpreter)"
+
+@@ -103,12 +103,12 @@
+ "Uncompress to stdout."
+
+ #define cal_trivial_usage \
+- "[-jy] [[month] year]"
++ "[-jy] [[month] year]"
+ #define cal_full_usage \
+- "Display a calendar.\n" \
+- "\nOptions:\n" \
+- "\t-j\tUse julian dates.\n" \
+- "\t-y\tDisplay the entire year."
++ "Display a calendar.\n" \
++ "\nOptions:\n" \
++ "\t-j\tUse julian dates\n" \
++ "\t-y\tDisplay the entire year"
+
+ #define cat_trivial_usage \
+ "[-u] [FILE]..."
+@@ -120,12 +120,36 @@
+ "$ cat /proc/uptime\n" \
+ "110716.72 17.67"
+
++#define chattr_trivial_usage \
++ "[-R] [-+=AacDdijsStTu] [-v version] files..."
++#define chattr_full_usage \
++ "change file attributes on an ext2 fs\n\n" \
++ "Modifiers:\n" \
++ "\t-\tremove attributes\n" \
++ "\t+\tadd attributes\n" \
++ "\t=\tset attributes\n" \
++ "Attributes:\n" \
++ "\tA\tdon't track atime\n" \
++ "\ta\tappend mode only\n" \
++ "\tc\tenable compress\n" \
++ "\tD\twrite dir contents synchronously\n" \
++ "\td\tdo not backup with dump\n" \
++ "\ti\tcannot be modified (immutable)\n" \
++ "\tj\twrite all data to journal first\n" \
++ "\ts\tzero disk storage when deleted\n" \
++ "\tS\twrite file contents synchronously\n" \
++ "\tt\tdisable tail-merging of partial blocks with other files\n" \
++ "\tu\tallow file to be undeleted\n" \
++ "Options:\n" \
++ "\t-R\trecursively list subdirectories\n" \
++ "\t-v\tset the file's version/generation number"
++
+ #define chgrp_trivial_usage \
+ "[OPTION]... GROUP FILE..."
+ #define chgrp_full_usage \
+ "Change the group membership of each FILE to GROUP.\n" \
+ "\nOptions:\n" \
+- "\t-R\tChanges files and directories recursively."
++ "\t-R\tChanges files and directories recursively"
+ #define chgrp_example_usage \
+ "$ ls -l /tmp/foo\n" \
+ "-r--r--r-- 1 andersen andersen 0 Apr 12 18:25 /tmp/foo\n" \
+@@ -139,7 +163,7 @@
+ "Each MODE is one or more of the letters ugoa, one of the\n" \
+ "symbols +-= and one or more of the letters rwxst.\n\n" \
+ "Options:\n" \
+- "\t-R\tChanges files and directories recursively."
++ "\t-R\tChanges files and directories recursively"
+ #define chmod_example_usage \
+ "$ ls -l /tmp/foo\n" \
+ "-rw-rw-r-- 1 root root 0 Apr 12 18:25 /tmp/foo\n" \
+@@ -155,8 +179,8 @@
+ #define chown_full_usage \
+ "Change the owner and/or group of each FILE to OWNER and/or GROUP.\n" \
+ "\nOptions:\n" \
+- "\t-R\tChanges files and directories recursively.\n" \
+- "\t-h\tDo not dereference symbolic links."
++ "\t-R\tChanges files and directories recursively\n" \
++ "\t-h\tDo not dereference symbolic links"
+ #define chown_example_usage \
+ "$ ls -l /tmp/foo\n" \
+ "-r--r--r-- 1 andersen andersen 0 Apr 12 18:25 /tmp/foo\n" \
+@@ -192,19 +216,29 @@
+ #define cmp_trivial_usage \
+ "[-l] [-s] FILE1 [FILE2]"
+ #define cmp_full_usage \
+- "Compare files. Compares FILE1 vs stdin if FILE2 is not specified.\n\n" \
++ "Compares FILE1 vs stdin if FILE2 is not specified.\n\n" \
+ "Options:\n" \
+ "\t-l\tWrite the byte numbers (decimal) and values (octal)\n" \
+- "\t\t for all differing bytes.\n" \
++ "\t\t for all differing bytes\n" \
+ "\t-s\tquiet mode - do not print"
+
++#define comm_trivial_usage \
++ "[-123] FILE1 FILE2"
++#define comm_full_usage \
++ "Compares FILE1 to FILE2, or to stdin if = is specified.\n\n" \
++ "Options:\n" \
++ "\t-1\tSuppress lines unique to FILE1\n" \
++ "\t-2\tSuppress lines unique to FILE2\n" \
++ "\t-3\tSuppress lines common to both files"
++
+ #define cp_trivial_usage \
+ "[OPTION]... SOURCE DEST"
+ #define cp_full_usage \
+ "Copies SOURCE to DEST, or multiple SOURCE(s) to DIRECTORY.\n" \
+ "\n" \
+ "\t-a\tSame as -dpR\n" \
+- "\t-d\tPreserves links\n" \
++ "\t-d,-P\tPreserves links\n" \
++ "\t-H,-L\tDereference all symlinks (implied by default)\n" \
+ "\t-p\tPreserves file attributes if possible\n" \
+ "\t-f\tforce (implied; ignored) - always set\n" \
+ "\t-i\tinteractive, prompt before overwrite\n" \
+@@ -259,9 +293,9 @@
+ "\t-f N\t\tPrint only these fields\n" \
+ "\t-n\t\tIgnored"
+ #define cut_example_usage \
+- "$ echo "Hello world" | cut -f 1 -d ' '\n" \
++ "$ echo \"Hello world\" | cut -f 1 -d ' '\n" \
+ "Hello\n" \
+- "$ echo "Hello world" | cut -f 2 -d ' '\n" \
++ "$ echo \"Hello world\" | cut -f 2 -d ' '\n" \
+ "world\n"
+
+ #ifdef CONFIG_FEATURE_DATE_ISOFMT
+@@ -277,10 +311,10 @@
+ "\nOptions:\n" \
+ "\t-R\t\tOutputs RFC-822 compliant date string\n" \
+ "\t-d STRING\tDisplays time described by STRING, not `now'\n" \
+- USAGE_DATE_ISOFMT("\t-I[TIMESPEC]\tOutputs an ISO-8601 compliant date/time string.\n" \
++ USAGE_DATE_ISOFMT("\t-I[TIMESPEC]\tOutputs an ISO-8601 compliant date/time string\n" \
+ "\t\t\tTIMESPEC=`date' (or missing) for date only,\n" \
+ "\t\t\t`hours', `minutes', or `seconds' for date and,\n" \
+- "\t\t\ttime to the indicated precision.\n") \
++ "\t\t\ttime to the indicated precision\n") \
+ "\t-s\t\tSets time described by STRING\n" \
+ "\t-r FILE\t\tDisplays the last modification time of FILE\n" \
+ "\t-u\t\tPrints or sets Coordinated Universal Time"
+@@ -292,14 +326,14 @@
+ "expression ..."
+ #define dc_full_usage \
+ "This is a Tiny RPN calculator that understands the\n" \
+- "following operations: +, add, -, sub, *, mul, /, div, %, mod, "\
++ "following operations: +, add, -, sub, *, mul, /, div, %, mod, " \
+ "**, exp, and, or, not, eor.\n" \
+ "For example: 'dc 2 2 add' -> 4, and 'dc 8 8 \\* 2 2 + /' -> 16.\n" \
+ "\nOptions:\n" \
+- "p - Prints the value on the top of the stack, without altering the stack.\n" \
+- "f - Prints the entire contents of the stack without altering anything.\n" \
+- "o - Pops the value off the top of the stack and uses it to set the output radix.\n" \
+- " Only 10 and 16 are supported."
++ "p - Prints the value on the top of the stack, without altering the stack\n" \
++ "f - Prints the entire contents of the stack without altering anything\n" \
++ "o - Pops the value off the top of the stack and uses it to set the output radix\n" \
++ " Only 10 and 16 are supported"
+ #define dc_example_usage \
+ "$ dc 2 2 + p\n" \
+ "4\n" \
+@@ -328,7 +362,7 @@
+ "\tconv=sync\tpad blocks with zeros\n" \
+ "\n" \
+ "Numbers may be suffixed by c (x1), w (x2), b (x512), kD (x1000), k (x1024),\n" \
+- "MD (x1000000), M (x1048576), GD (x1000000000) or G (x1073741824)."
++ "MD (x1000000), M (x1048576), GD (x1000000000) or G (x1073741824)"
+ #define dd_example_usage \
+ "$ dd if=/dev/zero of=/dev/ram1 bs=1M count=4\n" \
+ "4+0 records in\n" \
+@@ -350,13 +384,13 @@
+ "Deletes user USER from the system"
+
+ #ifdef CONFIG_DEVFSD_FG_NP
+- #define USAGE_DEVFSD_FG_NP(a) a
++# define USAGE_DEVFSD_FG_NP(a) a
+ #else
+- #define USAGE_DEVFSD_FG_NP(a)
++# define USAGE_DEVFSD_FG_NP(a)
+ #endif
+
+ #define devfsd_trivial_usage \
+- "mntpnt [-v]"\
++ "mntpnt [-v]" \
+ USAGE_DEVFSD_FG_NP("[-fg][-np]" )
+ #define devfsd_full_usage \
+ "Optional daemon for managing devfs permissions and old device name symlinks.\n" \
+@@ -370,11 +404,11 @@
+ "\t\tDo not poll for events.")
+
+ #ifdef CONFIG_FEATURE_HUMAN_READABLE
+- #define USAGE_HUMAN_READABLE(a) a
+- #define USAGE_NOT_HUMAN_READABLE(a)
++# define USAGE_HUMAN_READABLE(a) a
++# define USAGE_NOT_HUMAN_READABLE(a)
+ #else
+- #define USAGE_HUMAN_READABLE(a)
+- #define USAGE_NOT_HUMAN_READABLE(a) a
++# define USAGE_HUMAN_READABLE(a)
++# define USAGE_NOT_HUMAN_READABLE(a) a
+ #endif
+ #define df_trivial_usage \
+ "[-" USAGE_HUMAN_READABLE("hm") USAGE_NOT_HUMAN_READABLE("") "k] [FILESYSTEM ...]"
+@@ -508,9 +542,9 @@
+ "\t-a,\t--absolute\tInterpret lease times as expire time"
+
+ #ifdef CONFIG_FEATURE_FANCY_ECHO
+- #define USAGE_FANCY_ECHO(a) a
++# define USAGE_FANCY_ECHO(a) a
+ #else
+- #define USAGE_FANCY_ECHO(a)
++# define USAGE_FANCY_ECHO(a)
+ #endif
+
+ #define echo_trivial_usage \
+@@ -522,15 +556,22 @@
+ "\t-e\tinterpret backslash-escaped characters (i.e., \\t=tab)\n" \
+ "\t-E\tdisable interpretation of backslash-escaped characters")
+ #define echo_example_usage \
+- "$ echo "Erik is cool"\n" \
++ "$ echo \"Erik is cool\"\n" \
+ "Erik is cool\n" \
+- USAGE_FANCY_ECHO("$ echo -e "Erik\\nis\\ncool"\n" \
++ USAGE_FANCY_ECHO("$ echo -e \"Erik\\nis\\ncool\"\n" \
+ "Erik\n" \
+ "is\n" \
+ "cool\n" \
+- "$ echo "Erik\\nis\\ncool"\n" \
++ "$ echo \"Erik\\nis\\ncool\"\n" \
+ "Erik\\nis\\ncool\n")
+
++#define eject_trivial_usage \
++ "[-t] [DEVICE]"
++#define eject_full_usage \
++ "Eject specified DEVICE (or default /dev/cdrom).\n\n" \
++ "Options:\n" \
++ "\t-t\tclose tray"
++
+ #define env_trivial_usage \
+ "[-iu] [-] [name=value]... [command]"
+ #define env_full_usage \
+@@ -540,6 +581,17 @@
+ "\t-, -i\tstart with an empty environment\n" \
+ "\t-u\tremove variable from the environment"
+
++#define ether_wake_trivial_usage \
++ "[-b] [-i iface] [-p aa:bb:cc:dd[:ee:ff]] MAC"
++#define ether_wake_full_usage \
++ "Send a magic packet to wake up sleeping machines.\n" \
++ "MAC must be a station address (00:11:22:33:44:55) or\n" \
++ " a hostname with a known 'ethers' entry.\n\n" \
++ "Options:\n" \
++ "\t-b\t\tSend wake-up packet to the broadcast address\n" \
++ "\t-i iface\tUse interface ifname instead of the default \"eth0\"\n" \
++ "\t-p pass\tAppend the four or six byte password PW to the packet\n"
++
+ #define expr_trivial_usage \
+ "EXPRESSION"
+ #define expr_full_usage \
+@@ -574,6 +626,13 @@
+ "\\( and \\) or null; if \\( and \\) are not used, they return the number \n" \
+ "of characters matched or 0."
+
++#define fakeidentd_trivial_usage \
++ "[-b ip] [STRING]"
++#define fakeidentd_full_usage \
++ "Returns a set string to auth requests\n\n" \
++ "\t-b\tBind to ip address\n" \
++ "\tSTRING\tThe ident answer string (default is nobody)"
++
+ #define false_trivial_usage \
+ ""
+ #define false_full_usage \
+@@ -589,7 +648,7 @@
+ "Show and modify frame buffer settings"
+ #define fbset_example_usage \
+ "$ fbset\n" \
+- "mode "1024x768-76"\n" \
++ "mode \"1024x768-76\"\n" \
+ "\t# D: 78.653 MHz, H: 59.949 kHz, V: 75.694 Hz\n" \
+ "\tgeometry 1024 768 1024 768 16\n" \
+ "\ttimings 12714 128 32 16 4 128 4\n" \
+@@ -624,29 +683,29 @@
+ "\t-v Give fdisk version"
+
+ #ifdef CONFIG_FEATURE_FIND_TYPE
+- #define USAGE_FIND_TYPE(a) a
++# define USAGE_FIND_TYPE(a) a
+ #else
+- #define USAGE_FIND_TYPE(a)
++# define USAGE_FIND_TYPE(a)
+ #endif
+ #ifdef CONFIG_FEATURE_FIND_PERM
+- #define USAGE_FIND_PERM(a) a
++# define USAGE_FIND_PERM(a) a
+ #else
+- #define USAGE_FIND_PERM(a)
++# define USAGE_FIND_PERM(a)
+ #endif
+ #ifdef CONFIG_FEATURE_FIND_MTIME
+- #define USAGE_FIND_MTIME(a) a
++# define USAGE_FIND_MTIME(a) a
+ #else
+- #define USAGE_FIND_MTIME(a)
++# define USAGE_FIND_MTIME(a)
+ #endif
+ #ifdef CONFIG_FEATURE_FIND_NEWER
+- #define USAGE_FIND_NEWER(a) a
++# define USAGE_FIND_NEWER(a) a
+ #else
+- #define USAGE_FIND_NEWER(a)
++# define USAGE_FIND_NEWER(a)
+ #endif
+ #ifdef CONFIG_FEATURE_FIND_INUM
+- #define USAGE_FIND_INUM(a) a
++# define USAGE_FIND_INUM(a) a
+ #else
+- #define USAGE_FIND_INUM(a)
++# define USAGE_FIND_INUM(a)
+ #endif
+
+ #define find_trivial_usage \
+@@ -655,9 +714,9 @@
+ "Search for files in a directory hierarchy. The default PATH is\n" \
+ "the current directory; default EXPRESSION is '-print'\n" \
+ "\nEXPRESSION may consist of:\n" \
+- "\t-follow\t\tDereference symbolic links.\n" \
+- "\t-name PATTERN\tFile name (leading directories removed) matches PATTERN.\n" \
+- "\t-print\t\tPrint (default and assumed).\n" \
++ "\t-follow\t\tDereference symbolic links\n" \
++ "\t-name PATTERN\tFile name (leading directories removed) matches PATTERN\n" \
++ "\t-print\t\tPrint (default and assumed)\n" \
+ USAGE_FIND_TYPE( \
+ "\n\t-type X\t\tFiletype matches X (where X is one of: f,d,l,b,c,...)" \
+ ) USAGE_FIND_PERM( \
+@@ -711,7 +770,7 @@
+ "\t-v\tverbose\n" \
+ "\t-s\tOutputs super-block information\n" \
+ "\t-m\tActivates MINIX-like \"mode not cleared\" warnings\n" \
+- "\t-f\tForce file system check."
++ "\t-f\tForce file system check"
+
+ #define ftpget_trivial_usage \
+ "[options] remote-host local-file remote-file"
+@@ -748,42 +807,42 @@
+ "\t-T, --test Test for getopt(1) version\n" \
+ "\t-u, --unquoted Do not quote the output"
+ #define getopt_example_usage \
+- "$ cat getopt.test\n" \
+- "#!/bin/sh\n" \
+- "GETOPT=`getopt -o ab:c:: --long a-long,b-long:,c-long:: \\\n" \
+- " -n 'example.busybox' -- "$@"`\n" \
+- "if [ $? != 0 ] ; then exit 1 ; fi\n" \
+- "eval set -- "$GETOPT"\n" \
+- "while true ; do\n" \
+- " case $1 in\n" \
+- " -a|--a-long) echo \"Option a\" ; shift ;;\n" \
+- " -b|--b-long) echo \"Option b, argument `$2'\" ; shift 2 ;;\n" \
+- " -c|--c-long)\n" \
+- " case "$2" in\n" \
+- " \"\") echo \"Option c, no argument\"; shift 2 ;;\n" \
+- " *) echo \"Option c, argument `$2'\" ; shift 2 ;;\n" \
+- " esac ;;\n" \
+- " --) shift ; break ;;\n" \
+- " *) echo \"Internal error!\" ; exit 1 ;;\n" \
+- " esac\n" \
+- "done\n"
++ "$ cat getopt.test\n" \
++ "#!/bin/sh\n" \
++ "GETOPT=`getopt -o ab:c:: --long a-long,b-long:,c-long:: \\\n" \
++ " -n 'example.busybox' -- \"$@\"`\n" \
++ "if [ $? != 0 ] ; then exit 1 ; fi\n" \
++ "eval set -- \"$GETOPT\"\n" \
++ "while true ; do\n" \
++ " case $1 in\n" \
++ " -a|--a-long) echo \"Option a\" ; shift ;;\n" \
++ " -b|--b-long) echo \"Option b, argument `$2'\" ; shift 2 ;;\n" \
++ " -c|--c-long)\n" \
++ " case \"$2\" in\n" \
++ " \"\") echo \"Option c, no argument\"; shift 2 ;;\n" \
++ " *) echo \"Option c, argument `$2'\" ; shift 2 ;;\n" \
++ " esac ;;\n" \
++ " --) shift ; break ;;\n" \
++ " *) echo \"Internal error!\" ; exit 1 ;;\n" \
++ " esac\n" \
++ "done\n"
+
+ #define getty_trivial_usage \
+ "[OPTIONS]... baud_rate,... line [termtype]"
+ #define getty_full_usage \
+ "Opens a tty, prompts for a login name, then invokes /bin/login\n\n" \
+ "Options:\n" \
+- "\t-h\t\tEnable hardware (RTS/CTS) flow control.\n" \
+- "\t-i\t\tDo not display /etc/issue before running login.\n" \
+- "\t-L\t\tLocal line, so do not do carrier detect.\n" \
+- "\t-m\t\tGet baud rate from modem's CONNECT status message.\n" \
+- "\t-w\t\tWait for a CR or LF before sending /etc/issue.\n" \
+- "\t-n\t\tDo not prompt the user for a login name.\n" \
+- "\t-f issue_file\tDisplay issue_file instead of /etc/issue.\n" \
+- "\t-l login_app\tInvoke login_app instead of /bin/login.\n" \
+- "\t-t timeout\tTerminate after timeout if no username is read.\n" \
+- "\t-I initstring\tSets the init string to send before anything else.\n" \
+- "\t-H login_host\tLog login_host into the utmp file as the hostname."
++ "\t-h\t\tEnable hardware (RTS/CTS) flow control\n" \
++ "\t-i\t\tDo not display /etc/issue before running login\n" \
++ "\t-L\t\tLocal line, so do not do carrier detect\n" \
++ "\t-m\t\tGet baud rate from modem's CONNECT status message\n" \
++ "\t-w\t\tWait for a CR or LF before sending /etc/issue\n" \
++ "\t-n\t\tDo not prompt the user for a login name\n" \
++ "\t-f issue_file\tDisplay issue_file instead of /etc/issue\n" \
++ "\t-l login_app\tInvoke login_app instead of /bin/login\n" \
++ "\t-t timeout\tTerminate after timeout if no username is read\n" \
++ "\t-I initstring\tSets the init string to send before anything else\n" \
++ "\t-H login_host\tLog login_host into the utmp file as the hostname"
+
+
+ #define grep_trivial_usage \
+@@ -841,7 +900,7 @@
+ #define halt_full_usage \
+ "Halt the system.\n" \
+ "Options:\n" \
+- "\t-d\t\tdelay interval for halting."
++ "\t-d\t\tdelay interval for halting"
+
+ #ifdef CONFIG_FEATURE_HDPARM_GET_IDENTITY
+ #define USAGE_HDPARM_IDENT(a) a
+@@ -951,7 +1010,7 @@
+ "[-[bcdefnosvx]] [OPTION] FILE"
+ #define hexdump_full_usage \
+ "The hexdump utility is a filter which displays the specified files,\n" \
+- "or the standard input, if no files are specified, in a user specified\n"\
++ "or the standard input, if no files are specified, in a user specified\n" \
+ "format\n" \
+ "\t-b\t\tOne-byte octal display\n" \
+ "\t-c\t\tOne-byte character display\n" \
+@@ -985,26 +1044,26 @@
+ "sage\n"
+
+ #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
+- #define USAGE_HTTPD_BASIC_AUTH(a) a
+- #ifdef CONFIG_FEATURE_HTTPD_AUTH_MD5
+- #define USAGE_HTTPD_AUTH_MD5(a) a
+- #else
+- #define USAGE_HTTPD_AUTH_MD5(a)
+- #endif
++# define USAGE_HTTPD_BASIC_AUTH(a) a
++# ifdef CONFIG_FEATURE_HTTPD_AUTH_MD5
++# define USAGE_HTTPD_AUTH_MD5(a) a
++# else
++# define USAGE_HTTPD_AUTH_MD5(a)
++# endif
+ #else
+- #define USAGE_HTTPD_BASIC_AUTH(a)
+- #define USAGE_HTTPD_AUTH_MD5(a)
++# define USAGE_HTTPD_BASIC_AUTH(a)
++# define USAGE_HTTPD_AUTH_MD5(a)
+ #endif
+ #ifdef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
+- #define USAGE_HTTPD_STANDALONE(a)
+- #define USAGE_HTTPD_SETUID(a)
++# define USAGE_HTTPD_STANDALONE(a)
++# define USAGE_HTTPD_SETUID(a)
+ #else
+- #define USAGE_HTTPD_STANDALONE(a) a
+- #ifdef CONFIG_FEATURE_HTTPD_SETUID
+- #define USAGE_HTTPD_SETUID(a) a
+- #else
+- #define USAGE_HTTPD_SETUID(a)
+- #endif
++# define USAGE_HTTPD_STANDALONE(a) a
++# ifdef CONFIG_FEATURE_HTTPD_SETUID
++# define USAGE_HTTPD_SETUID(a) a
++# else
++# define USAGE_HTTPD_SETUID(a)
++# endif
+ #endif
+ #define httpd_trivial_usage \
+ "[-c <conf file>]" \
+@@ -1015,16 +1074,16 @@
+ " [-h home]" \
+ " [-d/-e <string>]"
+ #define httpd_full_usage \
+- "Listens for incoming http server requests.\n\n"\
+- "Options:\n" \
+- "\t-c FILE\t\tSpecifies configuration file. (default httpd.conf)\n" \
+- USAGE_HTTPD_STANDALONE("\t-p PORT\tServer port (default 80)\n") \
+- USAGE_HTTPD_SETUID("\t-u USER\tSet uid to USER after listening privileges port\n") \
+- USAGE_HTTPD_BASIC_AUTH("\t-r REALM\tAuthentication Realm for Basic Authentication\n") \
+- USAGE_HTTPD_AUTH_MD5("\t-m PASS\t\tCrypt PASS with md5 algorithm\n") \
+- "\t-h HOME \tSpecifies http HOME directory (default ./)\n" \
+- "\t-e STRING\tHtml encode STRING\n" \
+- "\t-d STRING\tURL decode STRING"
++ "Listens for incoming http server requests.\n\n" \
++ "Options:\n" \
++ "\t-c FILE\t\tSpecifies configuration file. (default httpd.conf)\n" \
++ USAGE_HTTPD_STANDALONE("\t-p PORT\tServer port (default 80)\n") \
++ USAGE_HTTPD_SETUID("\t-u USER\tSet uid to USER after listening privileges port\n") \
++ USAGE_HTTPD_BASIC_AUTH("\t-r REALM\tAuthentication Realm for Basic Authentication\n") \
++ USAGE_HTTPD_AUTH_MD5("\t-m PASS\t\tCrypt PASS with md5 algorithm\n") \
++ "\t-h HOME \tSpecifies http HOME directory (default ./)\n" \
++ "\t-e STRING\tHtml encode STRING\n" \
++ "\t-d STRING\tURL decode STRING"
+
+ #define hwclock_trivial_usage \
+ "[-r|--show] [-s|--hctosys] [-w|--systohc] [-l|--localtime] [-u|--utc]"
+@@ -1038,9 +1097,9 @@
+ "\t-l\tthe hardware clock is kept in local time"
+
+ #ifdef CONFIG_SELINUX
+- #define USAGE_SELINUX(a) a
++# define USAGE_SELINUX(a) a
+ #else
+- #define USAGE_SELINUX(a)
++# define USAGE_SELINUX(a)
+ #endif
+
+ #define id_trivial_usage \
+@@ -1058,29 +1117,29 @@
+ "uid=1000(andersen) gid=1000(andersen)\n"
+
+ #ifdef CONFIG_FEATURE_IFCONFIG_SLIP
+- #define USAGE_SIOCSKEEPALIVE(a) a
++# define USAGE_SIOCSKEEPALIVE(a) a
+ #else
+- #define USAGE_SIOCSKEEPALIVE(a)
++# define USAGE_SIOCSKEEPALIVE(a)
+ #endif
+ #ifdef CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ
+- #define USAGE_IFCONFIG_MII(a) a
++# define USAGE_IFCONFIG_MII(a) a
+ #else
+- #define USAGE_IFCONFIG_MII(a)
++# define USAGE_IFCONFIG_MII(a)
+ #endif
+ #ifdef CONFIG_FEATURE_IFCONFIG_HW
+- #define USAGE_IFCONFIG_HW(a) a
++# define USAGE_IFCONFIG_HW(a) a
+ #else
+- #define USAGE_IFCONFIG_HW(a)
++# define USAGE_IFCONFIG_HW(a)
+ #endif
+ #ifdef CONFIG_FEATURE_IFCONFIG_STATUS
+- #define USAGE_IFCONFIG_OPT_A(a) a
++# define USAGE_IFCONFIG_OPT_A(a) a
+ #else
+- #define USAGE_IFCONFIG_OPT_A(a)
++# define USAGE_IFCONFIG_OPT_A(a)
+ #endif
+ #ifdef CONFIG_FEATURE_IPV6
+- #define USAGE_IPV6(a) a
++# define USAGE_IPV6(a) a
+ #else
+- #define USAGE_IPV6(a)
++# define USAGE_IPV6(a)
+ #endif
+
+ #define ifconfig_trivial_usage \
+@@ -1094,7 +1153,7 @@
+ "\t[netmask <address>] [dstaddr <address>]\n" \
+ USAGE_SIOCSKEEPALIVE("\t[outfill <NN>] [keepalive <NN>]\n") \
+ "\t" USAGE_IFCONFIG_HW("[hw ether <address>] ") \
+- "[metric <NN>] [mtu <NN>]\n" \
++ "[metric <NN>] [mtu <NN>]\n" \
+ "\t[[-]trailers] [[-]arp] [[-]allmulti]\n" \
+ "\t[multicast] [[-]promisc] [txqueuelen <NN>] [[-]dynamic]\n" \
+ USAGE_IFCONFIG_MII("\t[mem_start <NN>] [io_addr <NN>] [irq <NN>]\n") \
+@@ -1134,7 +1193,7 @@
+ "Listens for network connections and launches programs\n\n" \
+ "Option:\n" \
+ "\t-q\tSets the size of the socket listen queue to\n" \
+- "\t\tthe specified value. Default is 128."
++ "\t\tthe specified value. Default is 128"
+
+ #define init_trivial_usage \
+ ""
+@@ -1172,7 +1231,7 @@
+ " WARNING: This field has a non-traditional meaning for BusyBox init!\n" \
+ " The id field is used by BusyBox init to specify the controlling tty for\n" \
+ " the specified process to run on. The contents of this field are\n" \
+-" appended to "/dev/" and used as-is. There is no need for this field to\n" \
++" appended to \"/dev/\" and used as-is. There is no need for this field to\n" \
+ " be unique, although if it isn't you may have strange results. If this\n" \
+ " field is left blank, the controlling tty is set to the console. Also\n" \
+ " note that if BusyBox detects that a serial console is in use, then only\n" \
+@@ -1218,7 +1277,7 @@
+ " it. Unlike sysvinit, BusyBox init does not stop processes from\n" \
+ " respawning out of control. The 'askfirst' actions acts just like\n" \
+ " respawn, except that before running the specified process it\n" \
+-" displays the line "Please press Enter to activate this console."\n" \
++" displays the line \"Please press Enter to activate this console.\"\n" \
+ " and then waits for the user to press enter before starting the\n" \
+ " specified process.\n" \
+ "\n" \
+@@ -1238,9 +1297,9 @@
+ " \n" \
+ " # /bin/sh invocations on selected ttys\n" \
+ " #\n" \
+-" # Start an "askfirst" shell on the console (whatever that may be)\n" \
++" # Start an \"askfirst\" shell on the console (whatever that may be)\n" \
+ " ::askfirst:-/bin/sh\n" \
+-" # Start an "askfirst" shell on /dev/tty2-4\n" \
++" # Start an \"askfirst\" shell on /dev/tty2-4\n" \
+ " tty2::askfirst:-/bin/sh\n" \
+ " tty3::askfirst:-/bin/sh\n" \
+ " tty4::askfirst:-/bin/sh\n" \
+@@ -1268,17 +1327,17 @@
+ " ::shutdown:/sbin/swapoff -a\n"
+
+ #ifdef CONFIG_FEATURE_INSMOD_LOAD_MAP
+- #define USAGE_INSMOD_MAP(a) a
++# define USAGE_INSMOD_MAP(a) a
+ #else
+- #define USAGE_INSMOD_MAP(a)
++# define USAGE_INSMOD_MAP(a)
+ #endif
+ #define insmod_trivial_usage \
+ "[OPTION]... MODULE [symbol=value]..."
+ #define insmod_full_usage \
+ "Loads the specified kernel modules into the kernel.\n\n" \
+ "Options:\n" \
+- "\t-f\tForce module to load into the wrong kernel version.\n" \
+- "\t-k\tMake module autoclean-able.\n" \
++ "\t-f\tForce module to load into the wrong kernel version\n" \
++ "\t-k\tMake module autoclean-able\n" \
+ "\t-v\tverbose output\n" \
+ "\t-q\tquiet output\n" \
+ "\t-L\tLock to prevent simultaneous loads of a module\n" \
+@@ -1319,21 +1378,21 @@
+ "\t\t\tSCOPE-ID := [ host | link | global | NUMBER ]"
+
+ #ifdef CONFIG_FEATURE_IPCALC_FANCY
+- #define XUSAGE_IPCALC_FANCY(a) a
++# define XUSAGE_IPCALC_FANCY(a) a
+ #else
+- #define XUSAGE_IPCALC_FANCY(a)
++# define XUSAGE_IPCALC_FANCY(a)
+ #endif
+ #define ipcalc_trivial_usage \
+ "[OPTION]... <ADDRESS>[[/]<NETMASK>] [NETMASK]"
+ #define ipcalc_full_usage \
+ "Calculate IP network settings from a IP address\n\n" \
+ "Options:\n" \
+- "\t-b\t--broadcast\tDisplay calculated broadcast address.\n" \
+- "\t-n\t--network\tDisplay calculated network address.\n" \
++ "\t-b\t--broadcast\tDisplay calculated broadcast address\n" \
++ "\t-n\t--network\tDisplay calculated network address\n" \
+ "\t-m\t--netmask\tDisplay default netmask for IP." \
+- XUSAGE_IPCALC_FANCY(\
++ XUSAGE_IPCALC_FANCY( \
+ "\n\t-p\t--prefix\tDisplay the prefix for IP/NETMASK." \
+- "\t-h\t--hostname\tDisplay first resolved host name.\n" \
++ "\t-h\t--hostname\tDisplay first resolved host name\n" \
+ "\t-s\t--silent\tDon't ever display error messages.")
+
+ #define iplink_trivial_usage \
+@@ -1368,9 +1427,9 @@
+ #define kill_trivial_usage \
+ "[-signal] process-id [process-id ...]"
+ #define kill_full_usage \
+- "Send a signal (default is SIGTERM) to the specified process(es).\n\n"\
++ "Send a signal (default is SIGTERM) to the specified process(es).\n\n" \
+ "Options:\n" \
+- "\t-l\tList all signal names and numbers."
++ "\t-l\tList all signal names and numbers"
+ #define kill_example_usage \
+ "$ ps | grep apache\n" \
+ "252 root root S [apache]\n" \
+@@ -1384,20 +1443,20 @@
+ #define killall_trivial_usage \
+ "[-q] [-signal] process-name [process-name ...]"
+ #define killall_full_usage \
+- "Send a signal (default is SIGTERM) to the specified process(es).\n\n"\
++ "Send a signal (default is SIGTERM) to the specified process(es).\n\n" \
+ "Options:\n" \
+- "\t-l\tList all signal names and numbers.\n"\
+- "\t-q\tDo not complain if no processes were killed."
++ "\t-l\tList all signal names and numbers\n" \
++ "\t-q\tDo not complain if no processes were killed"
+ #define killall_example_usage \
+ "$ killall apache\n"
+
+ #define klogd_trivial_usage \
+ "[-c n] [-n]"
+ #define klogd_full_usage \
+- "Kernel logger.\n"\
+- "Options:\n"\
+- "\t-c n\tSets the default log level of console messages to n.\n"\
+- "\t-n\tRun as a foreground process."
++ "Kernel logger.\n" \
++ "Options:\n" \
++ "\t-c n\tSets the default log level of console messages to n\n" \
++ "\t-n\tRun as a foreground process"
+
+ #define length_trivial_usage \
+ "STRING"
+@@ -1410,12 +1469,14 @@
+ #define ln_trivial_usage \
+ "[OPTION] TARGET... LINK_NAME|DIRECTORY"
+ #define ln_full_usage \
+- "Create a link named LINK_NAME or DIRECTORY to the specified TARGET\n"\
++ "Create a link named LINK_NAME or DIRECTORY to the specified TARGET\n" \
+ "\nYou may use '--' to indicate that all following arguments are non-options.\n\n" \
+ "Options:\n" \
+ "\t-s\tmake symbolic links instead of hard links\n" \
+ "\t-f\tremove existing destination files\n" \
+- "\t-n\tno dereference symlinks - treat like normal file"
++ "\t-n\tno dereference symlinks - treat like normal file\n" \
++ "\t-b\tmake a backup of the target (if exists) before link operation\n" \
++ "\t-S suffix\tuse suffix instead of ~ when making backup files"
+ #define ln_example_usage \
+ "$ ln -s BusyBox /tmp/ls\n" \
+ "$ ls -l /tmp/ls\n" \
+@@ -1440,12 +1501,12 @@
+ #define logger_full_usage \
+ "Write MESSAGE to the system log. If MESSAGE is omitted, log stdin.\n\n" \
+ "Options:\n" \
+- "\t-s\tLog to stderr as well as the system log.\n" \
+- "\t-t TAG\tLog using the specified tag (defaults to user name).\n" \
+- "\t-p PRIORITY\tEnter the message with the specified priority.\n" \
+- "\t\tThis may be numerical or a ``facility.level'' pair."
++ "\t-s\tLog to stderr as well as the system log\n" \
++ "\t-t TAG\tLog using the specified tag (defaults to user name)\n" \
++ "\t-p PRIORITY\tEnter the message with the specified priority\n" \
++ "\t\tThis may be numerical or a ``facility.level'' pair"
+ #define logger_example_usage \
+- "$ logger "hello"\n"
++ "$ logger \"hello\"\n"
+
+ #define login_trivial_usage \
+ "[OPTION]... [username] [ENV=VAR ...]"
+@@ -1453,8 +1514,8 @@
+ "Begin a new session on the system\n\n" \
+ "Options:\n" \
+ "\t-f\tDo not authenticate (user already authenticated)\n" \
+- "\t-h\tName of the remote host for this login.\n" \
+- "\t-p\tPreserve environment."
++ "\t-h\tName of the remote host for this login\n" \
++ "\t-p\tPreserve environment"
+
+ #define logname_trivial_usage \
+ ""
+@@ -1467,7 +1528,7 @@
+ #define logread_trivial_usage \
+ "[OPTION]..."
+ #define logread_full_usage \
+- "Shows the messages from syslogd (using circular buffer).\n\n" \
++ "Shows the messages from syslogd (using circular buffer).\n\n" \
+ "Options:\n" \
+ "\t-f\t\toutput data as the log grows"
+
+@@ -1477,38 +1538,38 @@
+ #define losetup_full_usage \
+ "Associate LOOPDEVICE with FILE.\n\n" \
+ "Options:\n" \
+- "\t-d\t\tDisassociate LOOPDEVICE.\n" \
+- "\t-o OFFSET\tStart OFFSET bytes into FILE."
++ "\t-d\t\tDisassociate LOOPDEVICE\n" \
++ "\t-o OFFSET\tStart OFFSET bytes into FILE"
+
+ #ifdef CONFIG_FEATURE_LS_TIMESTAMPS
+- #define USAGE_LS_TIMESTAMPS(a) a
++# define USAGE_LS_TIMESTAMPS(a) a
+ #else
+- #define USAGE_LS_TIMESTAMPS(a)
++# define USAGE_LS_TIMESTAMPS(a)
+ #endif
+ #ifdef CONFIG_FEATURE_LS_FILETYPES
+- #define USAGE_LS_FILETYPES(a) a
++# define USAGE_LS_FILETYPES(a) a
+ #else
+- #define USAGE_LS_FILETYPES(a)
++# define USAGE_LS_FILETYPES(a)
+ #endif
+ #ifdef CONFIG_FEATURE_LS_FOLLOWLINKS
+- #define USAGE_LS_FOLLOWLINKS(a) a
++# define USAGE_LS_FOLLOWLINKS(a) a
+ #else
+- #define USAGE_LS_FOLLOWLINKS(a)
++# define USAGE_LS_FOLLOWLINKS(a)
+ #endif
+ #ifdef CONFIG_FEATURE_LS_RECURSIVE
+- #define USAGE_LS_RECURSIVE(a) a
++# define USAGE_LS_RECURSIVE(a) a
+ #else
+- #define USAGE_LS_RECURSIVE(a)
++# define USAGE_LS_RECURSIVE(a)
+ #endif
+ #ifdef CONFIG_FEATURE_LS_SORTFILES
+- #define USAGE_LS_SORTFILES(a) a
++# define USAGE_LS_SORTFILES(a) a
+ #else
+- #define USAGE_LS_SORTFILES(a)
++# define USAGE_LS_SORTFILES(a)
+ #endif
+ #ifdef CONFIG_FEATURE_AUTOWIDTH
+- #define USAGE_AUTOWIDTH(a) a
++# define USAGE_AUTOWIDTH(a) a
+ #else
+- #define USAGE_AUTOWIDTH(a)
++# define USAGE_AUTOWIDTH(a)
+ #endif
+
+ #define ls_trivial_usage \
+@@ -1545,6 +1606,17 @@
+ USAGE_SELINUX("\t-k\tprint security context\n") \
+ USAGE_SELINUX("\t-K\tprint security context in long format\n")
+
++#define lsattr_trivial_usage \
++ "[-Radlv] [files...]"
++#define lsattr_full_usage \
++ "list file attributes on an ext2 fs\n\n" \
++ "Options:\n" \
++ "\t-R\trecursively list subdirectories\n" \
++ "\t-a\tdo not hide entries starting with .\n" \
++ "\t-d\tlist directory entries instead of contents\n" \
++ "\t-l\tprint long flag names\n" \
++ "\t-v\tlist the file's version/generation number"
++
+ #define lsmod_trivial_usage \
+ ""
+ #define lsmod_full_usage \
+@@ -1559,7 +1631,7 @@
+ "\tc or u:\tMake a character (un-buffered) device.\n" \
+ "\tp:\tMake a named pipe. MAJOR and MINOR are ignored for named pipes.\n\n" \
+ "FIRST specifies the number appended to NAME to create the first device.\n" \
+- "LAST specifies the number of the last item that should be created.\n" \
++ "LAST specifies the number of the last item that should be created\n" \
+ "If 's' is the last argument, the base device is created as well.\n\n" \
+ "For example:\n" \
+ "\tmakedevs /dev/ttyS c 4 66 2 63 -> ttyS2-ttyS63\n" \
+@@ -1602,8 +1674,8 @@
+ "[y|n]"
+ #define mesg_full_usage \
+ "mesg controls write access to your terminal\n" \
+- "\ty\tAllow write access to your terminal.\n" \
+- "\tn\tDisallow write access to your terminal.\n"
++ "\ty\tAllow write access to your terminal\n" \
++ "\tn\tDisallow write access to your terminal"
+
+ #define mkdir_trivial_usage \
+ "[OPTION] DIRECTORY..."
+@@ -1645,9 +1717,9 @@
+ "Options:\n" \
+ "\t-m\tcreate the special file using the specified mode (default a=rw)\n\n" \
+ "TYPEs include:\n" \
+- "\tb:\tMake a block (buffered) device.\n" \
+- "\tc or u:\tMake a character (un-buffered) device.\n" \
+- "\tp:\tMake a named pipe. MAJOR and MINOR are ignored for named pipes."
++ "\tb:\tMake a block (buffered) device\n" \
++ "\tc or u:\tMake a character (un-buffered) device\n" \
++ "\tp:\tMake a named pipe. MAJOR and MINOR are ignored for named pipes"
+ #define mknod_example_usage \
+ "$ mknod /dev/fd0 b 2 0\n" \
+ "$ mknod -m 644 /tmp/pipe p\n"
+@@ -1657,10 +1729,10 @@
+ #define mkswap_full_usage \
+ "Prepare a disk partition to be used as a swap partition.\n\n" \
+ "Options:\n" \
+- "\t-c\t\tCheck for read-ability.\n" \
+- "\t-v0\t\tMake version 0 swap [max 128 Megs].\n" \
+- "\t-v1\t\tMake version 1 swap [big!] (default for kernels >\n\t\t\t2.1.117).\n" \
+- "\tblock-count\tNumber of block to use (default is entire partition)."
++ "\t-c\t\tCheck for read-ability\n" \
++ "\t-v0\t\tMake version 0 swap [max 128 Megs]\n" \
++ "\t-v1\t\tMake version 1 swap [big!] (default for kernels >\n\t\t\t2.1.117)\n" \
++ "\tblock-count\tNumber of block to use (default is entire partition)"
+
+ #define mktemp_trivial_usage \
+ "[-dq] TEMPLATE"
+@@ -1681,12 +1753,12 @@
+ #define modprobe_full_usage \
+ "Used for high level module loading and unloading.\n\n" \
+ "Options:\n" \
+- "\t-k\tMake module autoclean-able.\n" \
+- "\t-n\tJust show what would be done.\n" \
+- "\t-q\tQuiet output.\n" \
+- "\t-r\tRemove module (stacks) or do autoclean.\n" \
+- "\t-s\tReport via syslog instead of stderr.\n" \
+- "\t-v\tVerbose output."
++ "\t-k\tMake module autoclean-able\n" \
++ "\t-n\tJust show what would be done\n" \
++ "\t-q\tQuiet output\n" \
++ "\t-r\tRemove module (stacks) or do autoclean\n" \
++ "\t-s\tReport via syslog instead of stderr\n" \
++ "\t-v\tVerbose output"
+ #define modprobe_example_usage \
+ "$ modprobe cdrom\n"
+
+@@ -1698,14 +1770,14 @@
+ "$ dmesg | more\n"
+
+ #ifdef CONFIG_FEATURE_MOUNT_LOOP
+- #define USAGE_MOUNT_LOOP(a) a
++# define USAGE_MOUNT_LOOP(a) a
+ #else
+- #define USAGE_MOUNT_LOOP(a)
++# define USAGE_MOUNT_LOOP(a)
+ #endif
+ #ifdef CONFIG_FEATURE_MTAB_SUPPORT
+- #define USAGE_MTAB(a) a
++# define USAGE_MTAB(a) a
+ #else
+- #define USAGE_MTAB(a)
++# define USAGE_MTAB(a)
+ #endif
+ #define mount_trivial_usage \
+ "[flags] DEVICE NODE [-o options,more-options]"
+@@ -1713,30 +1785,30 @@
+ "Mount a filesystem. Autodetection of filesystem type requires the\n" \
+ "/proc filesystem be already mounted.\n\n" \
+ "Flags:\n" \
+- "\t-a:\t\tMount all filesystems in fstab.\n" \
++ "\t-a:\t\tMount all filesystems in fstab\n" \
+ USAGE_MTAB( \
+- "\t-f:\t\t\"Fake\" Add entry to mount table but don't mount it.\n" \
+- "\t-n:\t\tDon't write a mount table entry.\n" \
++ "\t-f:\t\t\"Fake\" Add entry to mount table but don't mount it\n" \
++ "\t-n:\t\tDon't write a mount table entry\n" \
+ ) \
+- "\t-o option:\tOne of many filesystem options, listed below.\n" \
+- "\t-r:\t\tMount the filesystem read-only.\n" \
+- "\t-t fs-type:\tSpecify the filesystem type.\n" \
+- "\t-w:\t\tMount for reading and writing (default).\n" \
++ "\t-o option:\tOne of many filesystem options, listed below\n" \
++ "\t-r:\t\tMount the filesystem read-only\n" \
++ "\t-t fs-type:\tSpecify the filesystem type\n" \
++ "\t-w:\t\tMount for reading and writing (default)\n" \
+ "\n" \
+ "Options for use with the \"-o\" flag:\n" \
+- "\tasync/sync:\tWrites are asynchronous / synchronous.\n" \
+- "\tatime/noatime:\tEnable / disable updates to inode access times.\n" \
+- "\tdev/nodev:\tAllow use of special device files / disallow them.\n" \
+- "\texec/noexec:\tAllow use of executable files / disallow them.\n" \
++ "\tasync/sync:\tWrites are asynchronous / synchronous\n" \
++ "\tatime/noatime:\tEnable / disable updates to inode access times\n" \
++ "\tdev/nodev:\tAllow use of special device files / disallow them\n" \
++ "\texec/noexec:\tAllow use of executable files / disallow them\n" \
+ USAGE_MOUNT_LOOP( \
+- "\tloop:\t\tMounts a file via loop device.\n" \
++ "\tloop:\t\tMounts a file via loop device\n" \
+ ) \
+- "\tsuid/nosuid:\tAllow set-user-id-root programs / disallow them.\n" \
+- "\tremount:\tRe-mount a mounted filesystem, changing its flags.\n" \
+- "\tro/rw:\t\tMount for read-only / read-write.\n" \
+- "\tbind:\t\tUse the linux 2.4.x \"bind\" feature.\n" \
+- "\nThere are EVEN MORE flags that are specific to each filesystem.\n" \
+- "You'll have to see the written documentation for those filesystems."
++ "\tsuid/nosuid:\tAllow set-user-id-root programs / disallow them\n" \
++ "\tremount:\tRe-mount a mounted filesystem, changing its flags\n" \
++ "\tro/rw:\t\tMount for read-only / read-write\n" \
++ "\tbind:\t\tUse the linux 2.4.x \"bind\" feature\n" \
++ "\nThere are EVEN MORE flags that are specific to each filesystem\n" \
++ "You'll have to see the written documentation for those filesystems"
+ #define mount_example_usage \
+ "$ mount\n" \
+ "/dev/hda3 on / type minix (rw)\n" \
+@@ -1772,13 +1844,18 @@
+ "Nameif renaming network interface while it in the down state.\n\n" \
+ "Options:\n" \
+ "\t-c FILE\t\tUse configuration file (default is /etc/mactab)\n" \
+- "\t-s\t\tUse syslog (LOCAL0 facility).\n" \
++ "\t-s\t\tUse syslog (LOCAL0 facility)\n" \
+ "\tIFNAME MACADDR\tnew_interface_name interface_mac_address"
+ #define nameif_example_usage \
+ "$ nameif -s dmz0 00:A0:C9:8C:F6:3F\n" \
+ " or\n" \
+ "$ nameif -c /etc/my_mactab_file\n" \
+
++#ifdef CONFIG_NC_GAPING_SECURITY_HOLE
++# define USAGE_NC_EXEC(a) a
++#else
++# define USAGE_NC_EXEC(a)
++#endif
+ #define nc_trivial_usage \
+ "[OPTIONS] [IP] [port]"
+ #define nc_full_usage \
+@@ -1787,7 +1864,10 @@
+ "\t-l\t\tlisten mode, for inbound connects\n" \
+ "\t-p PORT\t\tlocal port number\n" \
+ "\t-i SECS\t\tdelay interval for lines sent\n" \
+- "\t-e PROG\t\tprogram to exec after connect (dangerous!)"
++ USAGE_NC_EXEC( \
++ "\t-e PROG\t\tprogram to exec after connect (dangerous!)\n" \
++ ) \
++ "\t-w SECS\t\ttimeout for connects and final net reads"
+ #define nc_example_usage \
+ "$ nc foobar.somedomain.com 25\n" \
+ "220 foobar ESMTP Exim 3.12 #1 Sat, 15 Apr 2000 00:03:02 -0600\n" \
+@@ -1813,6 +1893,13 @@
+ "\t-w raw sockets\n" \
+ "\t-x unix sockets"
+
++#define nice_trivial_usage \
++ "[-n ADJUST] [COMMAND [ARG] ...]"
++#define nice_full_usage \
++ "Nice runs a program with modified scheduling priority.\n\n" \
++ "Options:\n" \
++ "\t-n ADJUST\tAdjust the scheduling priority by ADJUST"
++
+ #define nslookup_trivial_usage \
+ "[HOST] [SERVER]"
+ #define nslookup_full_usage \
+@@ -1829,7 +1916,7 @@
+ #define od_trivial_usage \
+ "[-aBbcDdeFfHhIiLlOovXx] [FILE]"
+ #define od_full_usage \
+- "Write an unambiguous representation, octal bytes by default, of FILE\n"\
++ "Write an unambiguous representation, octal bytes by default, of FILE\n" \
+ "to standard output. With no FILE, or when FILE is -, read standard input."
+
+ #define openvt_trivial_usage \
+@@ -1840,9 +1927,9 @@
+ "openvt 2 /bin/ash\n"
+
+ #ifdef CONFIG_FEATURE_SHA1_PASSWORDS
+- #define PASSWORD_ALG_TYPES(a) a
++# define PASSWORD_ALG_TYPES(a) a
+ #else
+- #define PASSWORD_ALG_TYPES(a)
++# define PASSWORD_ALG_TYPES(a)
+ #endif
+ #define passwd_trivial_usage \
+ "[OPTION] [name]"
+@@ -1850,12 +1937,12 @@
+ "Change a user password. If no name is specified,\n" \
+ "changes the password for the current user.\n" \
+ "Options:\n" \
+- "\t-a\tDefine which algorithm shall be used for the password.\n" \
++ "\t-a\tDefine which algorithm shall be used for the password\n" \
+ "\t\t\t(Choices: des, md5" \
+ PASSWORD_ALG_TYPES(", sha1") \
+- ")\n\t-d\tDelete the password for the specified user account.\n" \
+- "\t-l\tLocks (disables) the specified user account.\n" \
+- "\t-u\tUnlocks (re-enables) the specified user account."
++ ")\n\t-d\tDelete the password for the specified user account\n" \
++ "\t-l\tLocks (disables) the specified user account\n" \
++ "\t-u\tUnlocks (re-enables) the specified user account"
+
+ #define patch_trivial_usage \
+ "[-p<num>]"
+@@ -1870,7 +1957,7 @@
+ "Lists the PIDs of all processes with names that match the\n" \
+ "names on the command line.\n" \
+ "Options:\n" \
+- "\t-s\t\tdisplay only a single PID."
++ "\t-s\t\tdisplay only a single PID"
+ #define pidof_example_usage \
+ "$ pidof init\n" \
+ "1\n"
+@@ -1884,10 +1971,10 @@
+ #define ping_full_usage \
+ "Send ICMP ECHO_REQUEST packets to network hosts.\n\n" \
+ "Options:\n" \
+- "\t-c COUNT\tSend only COUNT pings.\n" \
+- "\t-s SIZE\t\tSend SIZE data bytes in packets (default=56).\n" \
++ "\t-c COUNT\tSend only COUNT pings\n" \
++ "\t-s SIZE\t\tSend SIZE data bytes in packets (default=56)\n" \
+ "\t-q\t\tQuiet mode, only displays output at start\n" \
+- "\t\t\tand when finished."
++ "\t\t\tand when finished"
+ #endif
+ #define ping_example_usage \
+ "$ ping localhost\n" \
+@@ -1907,10 +1994,10 @@
+ #define ping6_full_usage \
+ "Send ICMP ECHO_REQUEST packets to network hosts.\n\n" \
+ "Options:\n" \
+- "\t-c COUNT\tSend only COUNT pings.\n" \
+- "\t-s SIZE\t\tSend SIZE data bytes in packets (default=56).\n" \
++ "\t-c COUNT\tSend only COUNT pings\n" \
++ "\t-s SIZE\t\tSend SIZE data bytes in packets (default=56)\n" \
+ "\t-q\t\tQuiet mode, only displays output at start\n" \
+- "\t\t\tand when finished."
++ "\t\t\tand when finished"
+ #endif
+ #define ping6_example_usage \
+ "$ ping6 ip6-localhost\n" \
+@@ -1932,7 +2019,13 @@
+ #define poweroff_full_usage \
+ "Halt the system and request that the kernel shut off the power.\n" \
+ "Options:\n" \
+- "\t-d\t\tdelay interval for shutting off."
++ "\t-d\t\tdelay interval for shutting off"
++
++#define printenv_trivial_usage \
++ "[VARIABLES...]"
++#define printenv_full_usage \
++ "print all or part of environment\n\n" \
++ "If no environment VARIABLE specified, print them all."
+
+ #define printf_trivial_usage \
+ "FORMAT [ARGUMENT...]"
+@@ -1940,7 +2033,7 @@
+ "Formats and prints ARGUMENT(s) according to FORMAT,\n" \
+ "Where FORMAT controls the output exactly as in C printf."
+ #define printf_example_usage \
+- "$ printf "Val=%d\\n" 5\n" \
++ "$ printf \"Val=%d\\n\" 5\n" \
+ "Val=5\n"
+
+ #ifdef CONFIG_SELINUX
+@@ -1982,13 +2075,36 @@
+ #define rdate_full_usage \
+ "Get and possibly set the system date and time from a remote HOST.\n\n" \
+ "Options:\n" \
+- "\t-s\tSet the system date and time (default).\n" \
+- "\t-p\tPrint the date and time."
++ "\t-s\tSet the system date and time (default)\n" \
++ "\t-p\tPrint the date and time"
++
++#ifdef CONFIG_FEATURE_READLINK_FOLLOW
++#define USAGE_READLINK_FOLLOW(a) a
++#else
++#define USAGE_READLINK_FOLLOW(a)
++#endif
+
+ #define readlink_trivial_usage \
+- ""
++ USAGE_READLINK_FOLLOW("[-f] ") "FILE"
+ #define readlink_full_usage \
+- "Displays the value of a symbolic link."
++ "Displays the value of a symbolic link." \
++ USAGE_READLINK_FOLLOW("\n\nOptions:\n" \
++ "\t-f\tcanonicalize by following all symlinks")
++
++#define readprofile_trivial_usage \
++ "[OPTIONS]..."
++#define readprofile_full_usage \
++ "Options:\n" \
++ "\t -m <mapfile> (default: /boot/System.map)\n" \
++ "\t -p <profile> (default: /proc/profile)\n" \
++ "\t -M <mult> set the profiling multiplier to <mult>\n" \
++ "\t -i print only info about the sampling step\n" \
++ "\t -v print verbose data\n" \
++ "\t -a print all symbols, even if count is 0\n" \
++ "\t -b print individual histogram-bin counts\n" \
++ "\t -s print individual counters within functions\n" \
++ "\t -r reset all the counters (root only)\n" \
++ "\t -n disable byte order auto-detection"
+
+ #define realpath_trivial_usage \
+ "pathname ..."
+@@ -2000,14 +2116,17 @@
+ #define reboot_full_usage \
+ "Reboot the system.\n" \
+ "Options:\n" \
+- "\t-d\t\tdelay interval for rebooting."
++ "\t-d\t\tdelay interval for rebooting"
+
+ #define renice_trivial_usage \
+- "priority pid [pid ...]"
++ "{{-n INCREMENT} | PRIORITY} [[ -p | -g | -u ] ID ...]"
+ #define renice_full_usage \
+- "Changes priority of running processes. Allowed priorities range\n" \
+- "from 20 (the process runs only when nothing else is running) to 0\n" \
+- "(default priority) to -20 (almost nothing else ever gets to run)."
++ "Changes priority of running processes.\n\n" \
++ "Options:\n" \
++ "\t-n\tadjusts current nice value (smaller is faster)\n" \
++ "\t-p\tprocess id(s) (default)\n" \
++ "\t-g\tprocess group id(s)\n" \
++ "\t-u\tprocess user name(s) and/or id(s)"
+
+ #define reset_trivial_usage \
+ ""
+@@ -2043,9 +2162,9 @@
+ "$ rmmod tulip\n"
+
+ #ifdef CONFIG_FEATURE_IPV6
+- #define USAGE_ROUTE_IPV6(a) a
++# define USAGE_ROUTE_IPV6(a) a
+ #else
+- #define USAGE_ROUTE_IPV6(a) "\t"
++# define USAGE_ROUTE_IPV6(a) "\t"
+ #endif
+
+
+@@ -2054,9 +2173,9 @@
+ #define route_full_usage \
+ "Edit the kernel's routing tables.\n\n" \
+ "Options:\n" \
+- "\t-n\t\tDont resolve names.\n" \
+- "\t-e\t\tDisplay other/more information.\n" \
+- "\t-A inet" USAGE_ROUTE_IPV6("{6}") "\tSelect address family."
++ "\t-n\t\tDont resolve names\n" \
++ "\t-e\t\tDisplay other/more information\n" \
++ "\t-A inet" USAGE_ROUTE_IPV6("{6}") "\tSelect address family"
+
+ #define rpm_trivial_usage \
+ "-i -q[ildc]p package.rpm"
+@@ -2081,9 +2200,9 @@
+ #define run_parts_full_usage \
+ "Run a bunch of scripts in a directory.\n\n" \
+ "Options:\n" \
+- "\t-t\tPrints what would be run, but does not actually run anything.\n" \
+- "\t-a ARG\tPass ARG as an argument for every program invoked.\n" \
+- "\t-u MASK\tSet the umask to MASK before executing every program."
++ "\t-t\tPrints what would be run, but does not actually run anything\n" \
++ "\t-a ARG\tPass ARG as an argument for every program invoked\n" \
++ "\t-u MASK\tSet the umask to MASK before executing every program"
+
+ #define rx_trivial_usage \
+ "FILE"
+@@ -2098,18 +2217,18 @@
+ "Options:\n" \
+ "\t-e script\tadd the script to the commands to be executed\n" \
+ "\t-f scriptfile\tadd script-file contents to the\n" \
+- "\t\t\tcommands to be executed\n" \
++ "\t\t\tcommands to be executed\n" \
+ "\t-i\t\tedit files in-place\n" \
+ "\t-n\t\tsuppress automatic printing of pattern space\n" \
+ "\t-r\t\tuse extended regular expression syntax\n" \
+ "\n" \
+- "If no -e or -f is given, the first non-option argument is taken as the sed\n"\
+- "script to interpret. All remaining arguments are names of input files; if no\n"\
++ "If no -e or -f is given, the first non-option argument is taken as the sed\n" \
++ "script to interpret. All remaining arguments are names of input files; if no\n" \
+ "input files are specified, then the standard input is read. Source files\n" \
+ "will not be modified unless -i option is given."
+
+ #define sed_example_usage \
+- "$ echo "foo" | sed -e 's/f[a-zA-Z]o/bar/g'\n" \
++ "$ echo \"foo\" | sed -e 's/f[a-zA-Z]o/bar/g'\n" \
+ "bar\n"
+
+ #define seq_trivial_usage \
+@@ -2142,7 +2261,7 @@
+ "Use lash just as you would use any other shell. It properly handles pipes,\n" \
+ "redirects, job control, can be used as the shell for scripts, and has a\n" \
+ "sufficient set of builtins to do what is needed. It does not (yet) support\n" \
+- "Bourne Shell syntax. If you need things like "if-then-else", "while", and such\n" \
++ "Bourne Shell syntax. If you need things like \"if-then-else\", \"while\", and such\n" \
+ "use ash or bash. If you just need a very simple and extremely small shell,\n" \
+ "this will do the job."
+
+@@ -2165,11 +2284,11 @@
+ "\t-w\twarn about improperly formated SHA1 checksum lines")
+
+ #ifdef CONFIG_FEATURE_FANCY_SLEEP
+- #define USAGE_FANCY_SLEEP(a) a
+- #define USAGE_NOT_FANCY_SLEEP(a)
++# define USAGE_FANCY_SLEEP(a) a
++# define USAGE_NOT_FANCY_SLEEP(a)
+ #else
+- #define USAGE_FANCY_SLEEP(a)
+- #define USAGE_NOT_FANCY_SLEEP(a) a
++# define USAGE_FANCY_SLEEP(a)
++# define USAGE_NOT_FANCY_SLEEP(a) a
+ #endif
+
+ #define sleep_trivial_usage \
+@@ -2185,24 +2304,39 @@
+ USAGE_FANCY_SLEEP("$ sleep 1d 3h 22m 8s\n" \
+ "[98528 second delay results]\n")
+
+-#ifdef CONFIG_FEATURE_SORT_UNIQUE
+- #define USAGE_SORT_UNIQUE(a) a
+-#else
+- #define USAGE_SORT_UNIQUE(a)
+-#endif
+-#ifdef CONFIG_FEATURE_SORT_REVERSE
+- #define USAGE_SORT_REVERSE(a) a
++#ifdef CONFIG_SORT_BIG
++# define USAGE_SORT_BIG(a) a
+ #else
+- #define USAGE_SORT_REVERSE(a)
++# define USAGE_SORT_BIG(a)
+ #endif
++
+ #define sort_trivial_usage \
+- "[-n" USAGE_SORT_REVERSE("r") USAGE_SORT_UNIQUE("u") "] [FILE]..."
++ "[-nru" USAGE_SORT_BIG("gMcszbdfimSTokt] [-o outfile] [-k start[.offset][opts][,end[.offset][opts]] [-t char") "] [FILE]..."
+ #define sort_full_usage \
+- "Sorts lines of text in the specified files\n\n"\
++ "Sorts lines of text in the specified files\n\n" \
+ "Options:\n" \
+- USAGE_SORT_UNIQUE("\t-u\tsuppress duplicate lines\n") \
+- USAGE_SORT_REVERSE("\t-r\tsort in reverse order\n") \
+- "\t-n\tsort numerics"
++ USAGE_SORT_BIG( \
++ "\t-b\tignore leading blanks\n" \
++ "\t-c\tcheck whether input is sorted\n" \
++ "\t-d\tdictionary order (blank or alphanumeric only)\n" \
++ "\t-f\tignore case\n" \
++ "\t-g\tgeneral numerical sort\n" \
++ "\t-i\tignore unprintable characters\n" \
++ "\t-k\tspecify sort key\n" \
++ "\t-M\tsort month\n" \
++ ) \
++ "\t-n\tsort numbers\n" \
++ USAGE_SORT_BIG( \
++ "\t-o\toutput to file\n" \
++ "\t-k\tsort by key\n" \
++ "\t-t\tuse key separator other than whitespace\n" \
++ ) \
++ "\t-r\treverse sort order\n" \
++ USAGE_SORT_BIG("\t-s\tstable (don't sort ties alphabetically)\n") \
++ "\t-u\tsuppress duplicate lines" \
++ USAGE_SORT_BIG("\n\t-z\tinput terminated by nulls, not newlines\n") \
++ USAGE_SORT_BIG("\t-mST\tignored for GNU compatability") \
++ ""
+ #define sort_example_usage \
+ "$ echo -e \"e\\nf\\nb\\nd\\nc\\na\" | sort\n" \
+ "a\n" \
+@@ -2210,34 +2344,98 @@
+ "c\n" \
+ "d\n" \
+ "e\n" \
+- "f\n"
++ "f\n" \
++ USAGE_SORT_BIG( \
++ "$ echo -e \"c 3\\nb 2\\nd 2\" | $SORT -k 2,2n -k 1,1r\n" \
++ "d 2\n" \
++ "b 2\n" \
++ "c 3\n" \
++ ) \
++ ""
+
+ #define start_stop_daemon_trivial_usage \
+- "[OPTIONS] [--start|--stop] ... [-- arguments...]\n"
++ "[OPTIONS] [--start|--stop] ... [-- arguments...]"
+ #define start_stop_daemon_full_usage \
+- "Program to start and stop services."\
+- "\n\nOptions:"\
+- "\n\t-S|--start\t\t\tstart"\
+- "\n\t-K|--stop\t\t\tstop"\
+- "\n\t-a|--startas <pathname>\t\tstarts process specified by pathname"\
+- "\n\t-b|--background\t\t\tforce process into background"\
+- "\n\t-u|--user <username>|<uid>\tstop this user's processes"\
+- "\n\t-x|--exec <executable>\t\tprogram to either start or check"\
+- "\n\t-m|--make-pidfile <filename>\tcreate the -p file and enter pid in it"\
+- "\n\t-n|--name <process-name>\tstop processes with this name"\
+- "\n\t-p|--pidfile <pid-file>\t\tsave or load pid using a pid-file"\
++ "Program to start and stop services." \
++ "\n\nOptions:" \
++ "\n\t-S|--start\t\t\tstart" \
++ "\n\t-K|--stop\t\t\tstop" \
++ "\n\t-a|--startas <pathname>\t\tstarts process specified by pathname" \
++ "\n\t-b|--background\t\t\tforce process into background" \
++ "\n\t-u|--user <username>|<uid>\tstop this user's processes" \
++ "\n\t-x|--exec <executable>\t\tprogram to either start or check" \
++ "\n\t-m|--make-pidfile <filename>\tcreate the -p file and enter pid in it" \
++ "\n\t-n|--name <process-name>\tstop processes with this name" \
++ "\n\t-p|--pidfile <pid-file>\t\tsave or load pid using a pid-file" \
+ "\n\t-q|--quiet\t\t\tbe quiet" \
+ "\n\t-s|--signal <signal>\t\tsignal to send (default TERM)"
+
++#ifdef CONFIG_FEATURE_STAT_FORMAT
++# define USAGE_STAT_FORMAT(a) a
++#else
++# define USAGE_STAT_FORMAT(a)
++#endif
++#define stat_trivial_usage \
++ "[OPTION] FILE..."
++#define stat_full_usage \
++ "display file (default) or filesystem status.\n\n" \
++ "Options:\n" \
++ USAGE_STAT_FORMAT("\t-c fmt\tuse the specified format\n") \
++ "\t-f\tdisplay filesystem status\n" \
++ "\t-L,-l\tdereference links\n" \
++ "\t-t\tdisplay info in terse form\n" \
++ USAGE_STAT_FORMAT( \
++ "\nValid format sequences for files:\n" \
++ " %a Access rights in octal\n" \
++ " %A Access rights in human readable form\n" \
++ " %b Number of blocks allocated (see %B)\n" \
++ " %B The size in bytes of each block reported by %b\n" \
++ " %d Device number in decimal\n" \
++ " %D Device number in hex\n" \
++ " %f Raw mode in hex\n" \
++ " %F File type\n" \
++ " %g Group ID of owner\n" \
++ " %G Group name of owner\n" \
++ " %h Number of hard links\n" \
++ " %i Inode number\n" \
++ " %n File name\n" \
++ " %N Quoted file name with dereference if symbolic link\n" \
++ " %o I/O block size\n" \
++ " %s Total size, in bytes\n" \
++ " %t Major device type in hex\n" \
++ " %T Minor device type in hex\n" \
++ " %u User ID of owner\n" \
++ " %U User name of owner\n" \
++ " %x Time of last access\n" \
++ " %X Time of last access as seconds since Epoch\n" \
++ " %y Time of last modification\n" \
++ " %Y Time of last modification as seconds since Epoch\n" \
++ " %z Time of last change\n" \
++ " %Z Time of last change as seconds since Epoch\n" \
++ "\nValid format sequences for file systems:\n" \
++ " %a Free blocks available to non-superuser\n" \
++ " %b Total data blocks in file system\n" \
++ " %c Total file nodes in file system\n" \
++ " %d Free file nodes in file system\n" \
++ " %f Free blocks in file system\n" \
++ " %i File System ID in hex\n" \
++ " %l Maximum length of filenames\n" \
++ " %n File name\n" \
++ " %s Block size (for faster transfers)\n" \
++ " %S Fundamental block size (for block counts)\n" \
++ " %t Type in hex\n" \
++ " %T Type in human readable form\n" \
++ )
++
+ #define strings_trivial_usage \
+ "[-afo] [-n length] [file ... ]"
+ #define strings_full_usage \
+ "Display printable strings in a binary file." \
+ "\n\nOptions:" \
+- "\n\t-a\tScan the whole files (this is the default)."\
++ "\n\t-a\tScan the whole files (this is the default)." \
+ "\n\t-f\tPrecede each string with the name of the file where it was found." \
+ "\n\t-n N\tSpecifies that at least N characters forms a sequence (default 4)" \
+- "\n\t-o\tEach string is preceded by its decimal offset in the file."
++ "\n\t-o\tEach string is preceded by its decimal offset in the file"
+
+ #define stty_trivial_usage \
+ "[-a|g] [-F DEVICE] [SETTING]..."
+@@ -2263,8 +2461,16 @@
+ "Single user login\n" \
+ "Options:\n" \
+ "\t-f\tDo not authenticate (user already authenticated)\n" \
+- "\t-h\tName of the remote host for this login.\n" \
+- "\t-p\tPreserve environment."
++ "\t-h\tName of the remote host for this login\n" \
++ "\t-p\tPreserve environment"
++
++#define sum_trivial_usage \
++ "[rs] [files...]"
++#define sum_full_usage \
++ "checksum and count the blocks in a file\n\n" \
++ "Options:\n" \
++ "\t-r\tuse BSD sum algorithm (1K blocks)\n" \
++ "\t-s\tuse System V sum algorithm (512byte blocks)"
+
+ #define swapoff_trivial_usage \
+ "[OPTION] [DEVICE]"
+@@ -2287,32 +2493,32 @@
+
+
+ #ifdef CONFIG_FEATURE_ROTATE_LOGFILE
+- #define USAGE_ROTATE_LOGFILE(a) a
++# define USAGE_ROTATE_LOGFILE(a) a
+ #else
+- #define USAGE_ROTATE_LOGFILE(a)
++# define USAGE_ROTATE_LOGFILE(a)
+ #endif
+ #ifdef CONFIG_FEATURE_REMOTE_LOG
+- #define USAGE_REMOTE_LOG(a) a
++# define USAGE_REMOTE_LOG(a) a
+ #else
+- #define USAGE_REMOTE_LOG(a)
++# define USAGE_REMOTE_LOG(a)
+ #endif
+ #ifdef CONFIG_FEATURE_IPC_SYSLOG
+- #define USAGE_IPC_LOG(a) a
++# define USAGE_IPC_LOG(a) a
+ #else
+- #define USAGE_IPC_LOG(a)
++# define USAGE_IPC_LOG(a)
+ #endif
+
+ #ifdef CONFIG_SYSCTL
+ #define sysctl_trivial_usage \
+- "[OPTIONS]... [VALUE]...\n"
++ "[OPTIONS]... [VALUE]..."
+ #define sysctl_full_usage
+ "sysctl - configure kernel parameters at runtime\n\n" \
+ "Options:\n" \
+- "\t-n\tUse this option to disable printing of the key name when printing values.\n" \
+- "\t-w\tUse this option when you want to change a sysctl setting.\n" \
+- "\t-p\tLoad in sysctl settings from the file specified or /etc/sysctl.conf if none given.\n" \
+- "\t-a\tDisplay all values currently available.\n" \
+- "\t-A\tDisplay all values currently available in table form."
++ "\t-n\tUse this option to disable printing of the key name when printing values\n" \
++ "\t-w\tUse this option when you want to change a sysctl setting\n" \
++ "\t-p\tLoad in sysctl settings from the file specified or /etc/sysctl.conf if none given\n" \
++ "\t-a\tDisplay all values currently available\n" \
++ "\t-A\tDisplay all values currently available in table form"
+ #define sysctl_example_usage
+ "sysctl [-n] variable ...\n" \
+ "sysctl [-n] -w variable=value ...\n" \
+@@ -2345,9 +2551,9 @@
+
+
+ #ifndef CONFIG_FEATURE_FANCY_TAIL
+- #define USAGE_UNSIMPLE_TAIL(a)
++# define USAGE_UNSIMPLE_TAIL(a)
+ #else
+- #define USAGE_UNSIMPLE_TAIL(a) a
++# define USAGE_UNSIMPLE_TAIL(a) a
+ #endif
+ #define tail_trivial_usage \
+ "[OPTION]... [FILE]..."
+@@ -2370,29 +2576,29 @@
+ "nameserver 10.0.0.1\n"
+
+ #ifdef CONFIG_FEATURE_TAR_CREATE
+- #define USAGE_TAR_CREATE(a) a
++# define USAGE_TAR_CREATE(a) a
+ #else
+- #define USAGE_TAR_CREATE(a)
++# define USAGE_TAR_CREATE(a)
+ #endif
+ #ifdef CONFIG_FEATURE_TAR_EXCLUDE
+- #define USAGE_TAR_EXCLUDE(a) a
++# define USAGE_TAR_EXCLUDE(a) a
+ #else
+- #define USAGE_TAR_EXCLUDE(a)
++# define USAGE_TAR_EXCLUDE(a)
+ #endif
+ #ifdef CONFIG_FEATURE_TAR_GZIP
+- #define USAGE_TAR_GZIP(a) a
++# define USAGE_TAR_GZIP(a) a
+ #else
+- #define USAGE_TAR_GZIP(a)
++# define USAGE_TAR_GZIP(a)
+ #endif
+ #ifdef CONFIG_FEATURE_TAR_BZIP2
+- #define USAGE_TAR_BZIP2(a) a
++# define USAGE_TAR_BZIP2(a) a
+ #else
+- #define USAGE_TAR_BZIP2(a)
++# define USAGE_TAR_BZIP2(a)
+ #endif
+ #ifdef CONFIG_FEATURE_TAR_COMPRESS
+- #define USAGE_TAR_COMPRESS(a) a
++# define USAGE_TAR_COMPRESS(a) a
+ #else
+- #define USAGE_TAR_COMPRESS(a)
++# define USAGE_TAR_COMPRESS(a)
+ #endif
+
+ #define tar_trivial_usage \
+@@ -2430,7 +2636,7 @@
+ "\t-a\tappend to the given FILEs, do not overwrite\n" \
+ "\t-i\tignore interrupt signals (SIGINT)"
+ #define tee_example_usage \
+- "$ echo "Hello" | tee /tmp/foo\n" \
++ "$ echo \"Hello\" | tee /tmp/foo\n" \
+ "$ cat /tmp/foo\n" \
+ "Hello\n"
+
+@@ -2441,17 +2647,17 @@
+ "Telnet is used to establish interactive communication with another\n" \
+ "computer over a network using the TELNET protocol.\n\n" \
+ "Options:\n" \
+- "\t-a\t\tAttempt an automatic login with the USER variable.\n" \
+- "\t-l USER\t\tAttempt an automatic login with the USER argument.\n" \
++ "\t-a\t\tAttempt an automatic login with the USER variable\n" \
++ "\t-l USER\t\tAttempt an automatic login with the USER argument\n" \
+ "\tHOST\t\tThe official name, alias or the IP address of the\n" \
+ "\t\t\tremote host.\n" \
+- "\tPORT\t\tThe remote port number to connect to. If it is not\n" \
++ "\tPORT\t\tThe remote port number to connect to. If it is not\n" \
+ "\t\t\tspecified, the default telnet (23) port is used."
+ #else
+ #define telnet_trivial_usage \
+ "HOST [PORT]"
+ #define telnet_full_usage \
+- "Telnet is used to establish interactive communication with another\n"\
++ "Telnet is used to establish interactive communication with another\n" \
+ "computer over a network using the TELNET protocol."
+ #endif
+
+@@ -2459,19 +2665,19 @@
+ #define telnetd_trivial_usage \
+ "(inetd mode) [OPTION]"
+ #define telnetd_full_usage \
+- "Telnetd uses incoming TELNET connections via inetd.\n"\
++ "Telnetd uses incoming TELNET connections via inetd.\n" \
+ "Options:\n" \
+ "\t-l LOGIN\texec LOGIN on connect (default /bin/sh)\n" \
+- "\t-f issue_file\tDisplay issue_file instead of /etc/issue."
++ "\t-f issue_file\tDisplay issue_file instead of /etc/issue"
+ #else
+ #define telnetd_trivial_usage \
+ "[OPTION]"
+ #define telnetd_full_usage \
+- "Telnetd listens for incoming TELNET connections on PORT.\n"\
++ "Telnetd listens for incoming TELNET connections on PORT.\n" \
+ "Options:\n" \
+- "\t-p PORT\tlisten for connections on PORT (default 23)\n"\
+- "\t-l LOGIN\texec LOGIN on connect (default /bin/sh)\n"\
+- "\t-f issue_file\tDisplay issue_file instead of /etc/issue."
++ "\t-p PORT\tlisten for connections on PORT (default 23)\n" \
++ "\t-l LOGIN\texec LOGIN on connect (default /bin/sh)\n" \
++ "\t-f issue_file\tDisplay issue_file instead of /etc/issue"
+ #endif
+
+ #define test_trivial_usage \
+@@ -2494,19 +2700,19 @@
+ "1\n"
+
+ #ifdef CONFIG_FEATURE_TFTP_GET
+- #define USAGE_TFTP_GET(a) a
++# define USAGE_TFTP_GET(a) a
+ #else
+- #define USAGE_TFTP_GET(a)
++# define USAGE_TFTP_GET(a)
+ #endif
+ #ifdef CONFIG_FEATURE_TFTP_PUT
+- #define USAGE_TFTP_PUT(a) a
++# define USAGE_TFTP_PUT(a) a
+ #else
+- #define USAGE_TFTP_PUT(a)
++# define USAGE_TFTP_PUT(a)
+ #endif
+ #ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
+- #define USAGE_TFTP_BS(a) a
++# define USAGE_TFTP_BS(a) a
+ #else
+- #define USAGE_TFTP_BS(a)
++# define USAGE_TFTP_BS(a)
+ #endif
+
+ #define tftp_trivial_usage \
+@@ -2514,16 +2720,16 @@
+ #define tftp_full_usage \
+ "Transfers a file from/to a tftp server using \"octet\" mode.\n\n" \
+ "Options:\n" \
+- "\t-l FILE\tLocal FILE.\n" \
+- "\t-r FILE\tRemote FILE." \
+- USAGE_TFTP_GET( \
+- "\n\t-g\tGet file." \
+- ) \
+- USAGE_TFTP_PUT( \
+- "\n\t-p\tPut file." \
++ "\t-l FILE\tLocal FILE\n" \
++ "\t-r FILE\tRemote FILE" \
++ USAGE_TFTP_GET( \
++ "\n\t-g\tGet file" \
++ ) \
++ USAGE_TFTP_PUT( \
++ "\n\t-p\tPut file" \
+ ) \
+ USAGE_TFTP_BS( \
+- "\n\t-b SIZE\tTransfer blocks of SIZE octets." \
++ "\n\t-b SIZE\tTransfer blocks of SIZE octets" \
+ )
+ #define time_trivial_usage \
+ "[OPTION]... COMMAND [ARGS...]"
+@@ -2531,7 +2737,7 @@
+ "Runs the program COMMAND with arguments ARGS. When COMMAND finishes,\n" \
+ "COMMAND's resource usage information is displayed\n\n" \
+ "Options:\n" \
+- "\t-v\tDisplays verbose resource usage information."
++ "\t-v\tDisplays verbose resource usage information"
+
+ #define top_trivial_usage \
+ "[-d <seconds>]"
+@@ -2565,11 +2771,11 @@
+ "\t-d\tdelete input characters coded STRING1\n" \
+ "\t-s\tsqueeze multiple output characters of STRING2 into one character"
+ #define tr_example_usage \
+- "$ echo "gdkkn vnqkc" | tr [a-y] [b-z]\n" \
++ "$ echo \"gdkkn vnqkc\" | tr [a-y] [b-z]\n" \
+ "hello world\n"
+
+ #define traceroute_trivial_usage \
+- "[-dnrv] [-m max_ttl] [-p port#] [-q nqueries]\n"\
++ "[-dnrv] [-m max_ttl] [-p port#] [-q nqueries]\n" \
+ "\t[-s src_addr] [-t tos] [-w wait] host [data size]"
+ #define traceroute_full_usage \
+ "trace the route ip packets follow going to \"host\"\n" \
+@@ -2587,7 +2793,7 @@
+ "\t-t tos\tSet the type-of-service in probe packets to the following value\n" \
+ "\t\t(default 0)\n" \
+ "\t-w wait\tSet the time (in seconds) to wait for a response to a probe\n" \
+- "\t\t(default 3 sec.)."
++ "\t\t(default 3 sec.)"
+
+
+ #define true_trivial_usage \
+@@ -2602,7 +2808,7 @@
+ #define tty_trivial_usage \
+ ""
+ #define tty_full_usage \
+- "Print the file name of the terminal connected to standard input.\n\n"\
++ "Print the file name of the terminal connected to standard input.\n\n" \
+ "Options:\n" \
+ "\t-s\tprint nothing, only return an exit status"
+ #define tty_example_usage \
+@@ -2616,9 +2822,9 @@
+ "\t-H,\t--hostname=HOSTNAME\tClient hostname\n" \
+ "\t-h,\t \tAlias for -H\n" \
+ "\t-f,\t--foreground\tDo not fork after getting lease\n" \
+- "\t-b,\t--background\tFork to background if lease cannot be immediately negotiated.\n" \
++ "\t-b,\t--background\tFork to background if lease cannot be immediately negotiated\n" \
+ "\t-i,\t--interface=INTERFACE\tInterface to use (default: eth0)\n" \
+- "\t-n,\t--now\tExit with failure if lease cannot be immediately negotiated.\n" \
++ "\t-n,\t--now\tExit with failure if lease cannot be immediately negotiated\n" \
+ "\t-p,\t--pidfile=file\tStore process ID of daemon in file\n" \
+ "\t-q,\t--quit\tQuit after obtaining lease\n" \
+ "\t-r,\t--request=IP\tIP address to request (default: none)\n" \
+@@ -2632,9 +2838,9 @@
+ ""
+
+ #ifdef CONFIG_FEATURE_MOUNT_FORCE
+- #define USAGE_MOUNT_FORCE(a) a
++# define USAGE_MOUNT_FORCE(a) a
+ #else
+- #define USAGE_MOUNT_FORCE(a)
++# define USAGE_MOUNT_FORCE(a)
+ #endif
+ #define umount_trivial_usage \
+ "[flags] FILESYSTEM|DIRECTORY"
+@@ -2769,7 +2975,7 @@
+ #define vi_full_usage \
+ "edit FILE.\n\n" \
+ "Options:\n" \
+- "\t-R\tRead-only- do not write to the file."
++ "\t-R\tRead-only- do not write to the file"
+
+ #define vlock_trivial_usage \
+ "[OPTIONS]"
+@@ -2783,7 +2989,7 @@
+ #define watch_full_usage \
+ "Executes a program periodically.\n" \
+ "Options:\n" \
+- "\t-n\tLoop period in seconds - default is 2."
++ "\t-n\tLoop period in seconds - default is 2"
+ #define watch_example_usage \
+ "$ watch date\n" \
+ "Mon Dec 17 10:31:40 GMT 2000\n" \
+@@ -2795,7 +3001,7 @@
+ #define watchdog_full_usage \
+ "Periodically write to watchdog device DEV.\n" \
+ "Options:\n" \
+- "\t-t\tTimer period in seconds - default is 30."
++ "\t-t\tTimer period in seconds - default is 30"
+
+ #define wc_trivial_usage \
+ "[OPTION]... [FILE]..."
+@@ -2832,9 +3038,9 @@
+ "/bin/login\n"
+
+ #define who_trivial_usage \
+- " "
++ " "
+ #define who_full_usage \
+- "Prints the current user names and related information"
++ "Prints the current user names and related information"
+
+ #define whoami_trivial_usage \
+ ""
+@@ -2867,7 +3073,7 @@
+ "\t-r\tDo not run command for empty readed lines\n" \
+ USAGE_XARGS_TERMOPT("\t-x\tExit if the size is exceeded\n") \
+ USAGE_XARGS_ZERO_TERM("\t-0\tInput filenames are terminated by a null character\n") \
+- "\t-t\tPrint the command line on stderr before executing it."
++ "\t-t\tPrint the command line on stderr before executing it"
+ #define xargs_example_usage \
+ "$ ls | xargs gzip\n" \
+ "$ find . -name '*.c' -print | xargs rm\n"
+@@ -2882,4 +3088,14 @@
+ #define zcat_full_usage \
+ "Uncompress to stdout."
+
++#define zcip_trivial_usage \
++ "[OPTIONS] ifname script"
++#define zcip_full_usage \
++ "zcip manages a ZeroConf IPv4 link-local address.\n" \
++ "Options:\n" \
++ "\t-f foreground mode\n" \
++ "\t-q quit after address (no daemon)\n" \
++ "\t-r 169.254.x.x request this address first\n" \
++ "\t-v verbose; show version\n"
++
+ #endif /* __BB_USAGE_H__ */
+diff -Nur busybox-1.00/init/init.c busybox/init/init.c
+--- busybox-1.00/init/init.c 2004-10-08 10:21:54.000000000 +0200
++++ busybox/init/init.c 2005-06-04 08:20:20.000000000 +0200
+@@ -453,6 +453,7 @@
+ signal(SIGINT, SIG_DFL);
+ signal(SIGTERM, SIG_DFL);
+ signal(SIGHUP, SIG_DFL);
++ signal(SIGQUIT, SIG_DFL);
+ signal(SIGCONT, SIG_DFL);
+ signal(SIGSTOP, SIG_DFL);
+ signal(SIGTSTP, SIG_DFL);
+@@ -693,6 +694,7 @@
+ /* first disable all our signals */
+ sigemptyset(&block_signals);
+ sigaddset(&block_signals, SIGHUP);
++ sigaddset(&block_signals, SIGQUIT);
+ sigaddset(&block_signals, SIGCHLD);
+ sigaddset(&block_signals, SIGUSR1);
+ sigaddset(&block_signals, SIGUSR2);
+@@ -737,6 +739,7 @@
+ /* unblock all signals, blocked in shutdown_system() */
+ sigemptyset(&unblock_signals);
+ sigaddset(&unblock_signals, SIGHUP);
++ sigaddset(&unblock_signals, SIGQUIT);
+ sigaddset(&unblock_signals, SIGCHLD);
+ sigaddset(&unblock_signals, SIGUSR1);
+ sigaddset(&unblock_signals, SIGUSR2);
+@@ -1097,6 +1100,7 @@
+ /* Set up sig handlers -- be sure to
+ * clear all of these in run() */
+ signal(SIGHUP, exec_signal);
++ signal(SIGQUIT, exec_signal);
+ signal(SIGUSR1, halt_signal);
+ signal(SIGUSR2, halt_signal);
+ signal(SIGINT, ctrlaltdel_signal);
+diff -Nur busybox-1.00/libbb/Makefile.in busybox/libbb/Makefile.in
+--- busybox-1.00/libbb/Makefile.in 2004-10-08 09:45:31.000000000 +0200
++++ busybox/libbb/Makefile.in 2005-06-04 08:20:15.000000000 +0200
+@@ -53,7 +53,7 @@
+ LIBBB_MSRC0:=$(srcdir)/messages.c
+ LIBBB_MOBJ0:=full_version.o \
+ memory_exhausted.o invalid_date.o io_error.o \
+- write_error.o name_longer_than_foo.o unknown.o \
++ read_error.o write_error.o name_longer_than_foo.o unknown.o \
+ can_not_create_raw_socket.o perm_denied_are_you_root.o \
+ shadow_file.o passwd_file.o group_file.o gshadow_file.o nologin_file.o \
+ securetty_file.o motd_file.o \
+diff -Nur busybox-1.00/libbb/concat_path_file.c busybox/libbb/concat_path_file.c
+--- busybox-1.00/libbb/concat_path_file.c 2004-03-15 09:28:41.000000000 +0100
++++ busybox/libbb/concat_path_file.c 2005-06-04 08:20:15.000000000 +0200
+@@ -34,11 +34,11 @@
+ char *lc;
+
+ if (!path)
+- path="";
++ path = "";
+ lc = last_char_is(path, '/');
+ while (*filename == '/')
+ filename++;
+- bb_xasprintf(&outbuf, "%s%s%s", path, (lc==NULL)? "/" : "", filename);
++ bb_xasprintf(&outbuf, "%s%s%s", path, (lc==NULL ? "/" : ""), filename);
+
+ return outbuf;
+ }
+diff -Nur busybox-1.00/libbb/copy_file.c busybox/libbb/copy_file.c
+--- busybox-1.00/libbb/copy_file.c 2004-04-19 14:28:02.000000000 +0200
++++ busybox/libbb/copy_file.c 2005-06-04 08:20:15.000000000 +0200
+@@ -54,10 +54,11 @@
+ }
+ } else {
+ if (source_stat.st_dev == dest_stat.st_dev &&
+- source_stat.st_ino == dest_stat.st_ino) {
+- bb_error_msg("`%s' and `%s' are the same file", source, dest);
+- return -1;
+- }
++ source_stat.st_ino == dest_stat.st_ino)
++ {
++ bb_error_msg("`%s' and `%s' are the same file", source, dest);
++ return -1;
++ }
+ dest_exists = 1;
+ }
+
+diff -Nur busybox-1.00/libbb/copyfd.c busybox/libbb/copyfd.c
+--- busybox-1.00/libbb/copyfd.c 2004-03-15 09:28:41.000000000 +0100
++++ busybox/libbb/copyfd.c 2005-06-04 08:20:15.000000000 +0200
+@@ -2,7 +2,7 @@
+ /*
+ * Utility routines.
+ *
+- * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
++ * Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -25,6 +25,7 @@
+ #include <unistd.h>
+
+ #include "busybox.h"
++#include "libbb.h"
+
+
+ #if BUFSIZ < 4096
+@@ -33,46 +34,59 @@
+ #endif
+
+
+-/* If size is 0 copy until EOF */
+-static size_t bb_full_fd_action(int src_fd, int dst_fd, const size_t size)
++static size_t bb_full_fd_action(int src_fd, int dst_fd, const size_t size2)
+ {
+- size_t read_total = 0;
+- RESERVE_CONFIG_BUFFER(buffer,BUFSIZ);
++ int status;
++ size_t xread, wrote, total, size = size2;
+
+- while ((size == 0) || (read_total < size)) {
+- size_t read_try;
+- ssize_t read_actual;
+-
+- if ((size == 0) || (size - read_total > BUFSIZ)) {
+- read_try = BUFSIZ;
+- } else {
+- read_try = size - read_total;
+- }
++ if (src_fd < 0) {
++ return -1;
++ }
+
+- read_actual = safe_read(src_fd, buffer, read_try);
+- if (read_actual > 0) {
+- if ((dst_fd >= 0) && (bb_full_write(dst_fd, buffer, (size_t) read_actual) != read_actual)) {
+- bb_perror_msg(bb_msg_write_error); /* match Read error below */
++ if (size == 0) {
++ /* If size is 0 copy until EOF */
++ size = ULONG_MAX;
++ }
++
++ {
++ RESERVE_CONFIG_BUFFER(buffer,BUFSIZ);
++ total = 0;
++ wrote = 0;
++ status = -1;
++ while (total < size)
++ {
++ xread = BUFSIZ;
++ if (size < (total + BUFSIZ))
++ xread = size - total;
++ xread = bb_full_read(src_fd, buffer, xread);
++ if (xread > 0) {
++ if (dst_fd < 0) {
++ /* A -1 dst_fd means we need to fake it... */
++ wrote = xread;
++ } else {
++ wrote = bb_full_write(dst_fd, buffer, xread);
++ }
++ if (wrote < xread) {
++ bb_perror_msg(bb_msg_write_error);
++ break;
++ }
++ total += wrote;
++ } else if (xread < 0) {
++ bb_perror_msg(bb_msg_read_error);
++ break;
++ } else if (xread == 0) {
++ /* All done. */
++ status = 0;
+ break;
+ }
+ }
+- else if (read_actual == 0) {
+- if (size) {
+- bb_error_msg("Unable to read all data");
+- }
+- break;
+- } else {
+- /* read_actual < 0 */
+- bb_perror_msg("Read error");
+- break;
+- }
+-
+- read_total += read_actual;
++ RELEASE_CONFIG_BUFFER(buffer);
+ }
+
+- RELEASE_CONFIG_BUFFER(buffer);
+-
+- return(read_total);
++ if (status == 0 || total)
++ return total;
++ /* Some sortof error occured */
++ return -1;
+ }
+
+
+diff -Nur busybox-1.00/libbb/find_pid_by_name.c busybox/libbb/find_pid_by_name.c
+--- busybox-1.00/libbb/find_pid_by_name.c 2004-03-15 09:28:42.000000000 +0100
++++ busybox/libbb/find_pid_by_name.c 2005-06-04 08:20:15.000000000 +0200
+@@ -45,11 +45,8 @@
+ procps_status_t * p;
+
+ pidList = xmalloc(sizeof(long));
+-#ifdef CONFIG_SELINUX
+- while ((p = procps_scan(0, 0, NULL)) != 0) {
+-#else
+- while ((p = procps_scan(0)) != 0) {
+-#endif
++ while ((p = procps_scan(0)) != 0)
++ {
+ if (strncmp(p->short_cmd, pidName, COMM_LEN-1) == 0) {
+ pidList=xrealloc( pidList, sizeof(long) * (i+2));
+ pidList[i++]=p->pid;
+diff -Nur busybox-1.00/libbb/get_line_from_file.c busybox/libbb/get_line_from_file.c
+--- busybox-1.00/libbb/get_line_from_file.c 2004-03-15 09:28:42.000000000 +0100
++++ busybox/libbb/get_line_from_file.c 2005-06-04 08:20:15.000000000 +0200
+@@ -44,7 +44,8 @@
+ linebuf = xrealloc(linebuf, linebufsz += GROWBY);
+ }
+ linebuf[idx++] = (char)ch;
+- if (ch == '\n' || ch == '\0') {
++ if (!ch) return linebuf;
++ if (c<2 && ch == '\n') {
+ if (c) {
+ --idx;
+ }
+@@ -71,6 +72,11 @@
+ return private_get_line_from_file(file, 1);
+ }
+
++extern char *bb_get_chunk_from_file(FILE *file)
++{
++ return private_get_line_from_file(file, 2);
++}
++
+
+ /* END CODE */
+ /*
+diff -Nur busybox-1.00/libbb/getopt_ulflags.c busybox/libbb/getopt_ulflags.c
+--- busybox-1.00/libbb/getopt_ulflags.c 2004-02-05 14:49:29.000000000 +0100
++++ busybox/libbb/getopt_ulflags.c 2005-06-04 08:20:15.000000000 +0200
+@@ -26,146 +26,270 @@
+ #include <stdlib.h>
+ #include "libbb.h"
+
+-/*
+-You can set bb_opt_complementaly as string with one or more
+-complementaly or incongruously options.
+-If sequential founded option haved from this string
+-then your incongruously pairs unsets and complementaly make add sets.
+-Format:
+-one char - option for check,
+-chars - complementaly option for add sets.
+-- chars - option triggered for unsets.
+-~ chars - option incongruously.
+-* - option list, called add_to_list(*ptr_from_usaged, optarg)
+-: - separator.
+-Example: du applet can have options "-s" and "-d size"
+-If getopt found -s then -d option flag unset or if found -d then -s unset.
+-For this result you must set bb_opt_complementaly = "s-d:d-s".
+-Result have last option flag only from called arguments.
+-Warning! You can check returned flag, pointer to "d:" argument seted
+-to own optarg always.
+-Example two: cut applet must only one type of list may be specified,
+-and -b, -c and -f incongruously option, overwited option is error also.
+-You must set bb_opt_complementaly = "b~cf:c~bf:f~bc".
+-If called have more one specified, return value have error flag -
+-high bite set (0x80000000UL).
+-Example three: grep applet can have one or more "-e pattern" arguments.
+-You should use bb_getopt_ulflags() as
+-llist_t *paterns;
+-bb_opt_complementaly = "e*";
+-bb_getopt_ulflags (argc, argv, "e:", &paterns);
++/* Documentation !
++
++unsigned long
++bb_getopt_ulflags (int argc, char **argv, const char *applet_opts, ...)
++
++ The command line options must be declared in const char
++ *applet_opts as a string of chars, for example:
++
++ flags = bb_getopt_ulflags(argc, argv, "rnug");
++
++ If one of the given options is found, a flag value is added to
++ the return value (an unsigned long).
++
++ The flag value is determined by the position of the char in
++ applet_opts string. For example, in the above case:
++
++ flags = bb_getopt_ulflags(argc, argv, "rnug");
++
++ "r" will add 1 (bit 1 : 0x01)
++ "n" will add 2 (bit 2 : 0x02)
++ "u will add 4 (bit 3 : 0x03)
++ "g" will add 8 (bit 4 : 0x04)
++
++ and so on. You can also look at the return value as a bit
++ field and each option sets one of bits.
++
++ ":" If one of the options requires an argument, then add a ":"
++ after the char in applet_opts and provide a pointer to store
++ the argument. For example:
++
++ char *pointer_to_arg_for_a;
++ char *pointer_to_arg_for_b;
++ char *pointer_to_arg_for_c;
++ char *pointer_to_arg_for_d;
++
++ flags = bb_getopt_ulflags(argc, argv, "a:b:c:d:",
++ &pointer_to_arg_for_a, &pointer_to_arg_for_b,
++ &pointer_to_arg_for_c, &pointer_to_arg_for_d);
++
++ The type of the pointer (char* or llist_t *) may be controlled
++ by the "*" special character that is set in the external string
++ bb_opt_complementaly (see below for more info).
++
++static const struct option bb_default_long_options[]
++
++ This struct allows you to define long options. The syntax for
++ declaring the array is just like that of getopt's longopts.
++
++ static const struct option applet_long_options[] = {
++ { "verbose", 0, 0, "v" },
++ { 0, 0, 0, 0 }
++ };
++ bb_applet_long_options = applet_long_options;
++
++ The first parameter is the long option name that you would pass
++ to the applet (without the dashes).
++
++ The second field determines whether the option has an argument.
++ You can set this to 0, 1, or 2, or you can use the long named
++ defines of no_argument, required_argument, and optional_argument.
++
++ The third argument is used only when the long option does not
++ have a corresponding short option. In that case, it should be
++ an integer pointer. Otherwise (and normally), it should just
++ bet set to NULL.
++
++ The last argument is the corresponding short option (if there
++ is one of course).
++
++ Note: a good applet will make long options configurable via the
++ config process and not a required feature. The current standard
++ is to name the config option CONFIG_FEATURE_<applet>_LONG_OPTIONS.
++
++const char *bb_opt_complementaly
++
++ ":" The colon (":") is used to separate groups of two or more chars
++ and/or groups of chars and special characters (stating some
++ conditions to be checked).
++
++ "abc" If groups of two or more chars are specified, the first char
++ is the main option and the other chars are secondary options.
++ Their flags will be turned on if the main option is found even
++ if they are not specifed on the command line. For example:
++
++ bb_opt_complementaly = "abc";
++
++ flags = bb_getopt_ulflags(argc, argv, "abcd")
++
++ If getopt() finds "-a" on the command line, then
++ bb_getopt_ulflags's return value will be as if "-a -b -c" were
++ found.
++
++Special characters:
++
++ "-" A dash between two options causes the second of the two
++ to be unset (and ignored) if it is given on the command line.
++
++ For example:
++ The du applet has the options "-s" and "-d depth". If
++ bb_getopt_ulflags finds -s, then -d is unset or if it finds -d
++ then -s is unset. (Note: busybox implements the GNU
++ "--max-depth" option as "-d".) To obtain this behavior, you
++ set bb_opt_complementaly = "s-d:d-s". Only one flag value is
++ added to bb_getopt_ulflags's return value depending on the
++ position of the options on the command line. If one of the
++ two options requires an argument pointer (":" in applet_opts
++ as in "d:") optarg is set accordingly.
++
++ char *smax_print_depth;
++
++ bb_opt_complementaly = "s-d:d-s";
++ opt = bb_getopt_ulflags(argc, argv, "sd:", &smax_print_depth);
++
++ if (opt & 2) {
++ max_print_depth = bb_xgetularg10_bnd(smax_print_depth,
++ 0, INT_MAX);
++ }
++
++ "~" A tilde between two options, or between an option and a group
++ of options, means that they are mutually exclusive. Unlike
++ the "-" case above, an error will be forced if the options
++ are used together.
++
++ For example:
++ The cut applet must have only one type of list specified, so
++ -b, -c and -f are mutally exclusive and should raise an error
++ if specified together. In this case you must set
++ bb_opt_complementaly = "b~cf:c~bf:f~bc". If two of the
++ mutually exclusive options are found, bb_getopt_ulflags's
++ return value will have the error flag set (BB_GETOPT_ERROR) so
++ that we can check for it:
++
++ if (flags & BB_GETOPT_ERROR)
++ bb_show_usage();
++
++ "*" A star after a char in bb_opt_complementaly means that the
++ option can occur multiple times:
++
++ For example:
++ The grep applet can have one or more "-e pattern" arguments.
++ In this case you should use bb_getopt_ulflags() as follows:
++
++ llist_t *patterns = NULL;
++
++ (this pointer must be initializated to NULL if the list is empty
++ as required by *llist_add_to(llist_t *old_head, char *new_item).)
++
++ bb_opt_complementaly = "e*";
++
++ bb_getopt_ulflags(argc, argv, "e:", &patterns);
++ $ grep -e user -e root /etc/passwd
++ root:x:0:0:root:/root:/bin/bash
++ user:x:500:500::/home/user:/bin/bash
++
+ */
+
+ const char *bb_opt_complementaly;
+
+-typedef struct
+-{
++typedef struct {
+ unsigned char opt;
+ char list_flg;
+ unsigned long switch_on;
+ unsigned long switch_off;
+ unsigned long incongruously;
+- void **optarg; /* char **optarg or llist_t **optarg */
++ void **optarg; /* char **optarg or llist_t **optarg */
+ } t_complementaly;
+
+ /* You can set bb_applet_long_options for parse called long options */
+
+ static const struct option bb_default_long_options[] = {
+- /* { "help", 0, NULL, '?' }, */
++/* { "help", 0, NULL, '?' }, */
+ { 0, 0, 0, 0 }
+ };
+
+ const struct option *bb_applet_long_options = bb_default_long_options;
+
+-
+ unsigned long
+ bb_getopt_ulflags (int argc, char **argv, const char *applet_opts, ...)
+ {
+- unsigned long flags = 0;
+- t_complementaly complementaly[sizeof(flags) * 8 + 1];
+- int c;
+- const unsigned char *s;
+- t_complementaly *on_off;
+- va_list p;
+-
+- va_start (p, applet_opts);
+-
+- /* skip GNU extension */
+- s = applet_opts;
+- if(*s == '+' || *s == '-')
+- s++;
+-
+- c = 0;
+- on_off = complementaly;
+- for (; *s; s++) {
+- if(c >= (sizeof(flags)*8))
+- break;
+- on_off->opt = *s;
+- on_off->switch_on = (1 << c);
+- on_off->list_flg = 0;
+- on_off->switch_off = 0;
+- on_off->incongruously = 0;
+- on_off->optarg = NULL;
+- if (s[1] == ':') {
+- on_off->optarg = va_arg (p, void **);
+- do
++ unsigned long flags = 0;
++ t_complementaly complementaly[sizeof(flags) * 8 + 1];
++ int c;
++ const unsigned char *s;
++ t_complementaly *on_off;
++ va_list p;
++
++ va_start (p, applet_opts);
++
++ /* skip GNU extension */
++ s = applet_opts;
++ if(*s == '+' || *s == '-')
+ s++;
+- while (s[1] == ':');
++
++ c = 0;
++ on_off = complementaly;
++ for (; *s; s++) {
++ if(c >= (sizeof(flags)*8))
++ break;
++ on_off->opt = *s;
++ on_off->switch_on = (1 << c);
++ on_off->list_flg = 0;
++ on_off->switch_off = 0;
++ on_off->incongruously = 0;
++ on_off->optarg = NULL;
++ if (s[1] == ':') {
++ on_off->optarg = va_arg (p, void **);
++ do
++ s++;
++ while (s[1] == ':');
++ }
++ on_off++;
++ c++;
+ }
+- on_off++;
+- c++;
+- }
+- on_off->opt = 0;
+- c = 0;
+- for (s = bb_opt_complementaly; s && *s; s++) {
+- t_complementaly *pair;
+-
+- if (*s == ':') {
+- c = 0;
+- continue;
+- }
+- if (c)
+- continue;
+- for (on_off = complementaly; on_off->opt; on_off++)
+- if (on_off->opt == *s)
+- break;
+- pair = on_off;
+- for(s++; *s && *s != ':'; s++) {
+- if (*s == '-' || *s == '~') {
+- c = *s;
+- } else if(*s == '*') {
+- pair->list_flg++;
+- } else {
+- unsigned long *pair_switch = &(pair->switch_on);
+-
+- if(c)
+- pair_switch = c == '-' ? &(pair->switch_off) : &(pair->incongruously);
+- for (on_off = complementaly; on_off->opt; on_off++)
+- if (on_off->opt == *s) {
+- *pair_switch |= on_off->switch_on;
+- break;
+- }
+- }
+- }
+- s--;
+- }
+-
+- while ((c = getopt_long (argc, argv, applet_opts,
+- bb_applet_long_options, NULL)) > 0) {
+- for (on_off = complementaly; on_off->opt != c; on_off++) {
+- if(!on_off->opt)
+- bb_show_usage ();
++ on_off->opt = 0;
++ c = 0;
++ for (s = bb_opt_complementaly; s && *s; s++) {
++ t_complementaly *pair;
++
++ if (*s == ':') {
++ c = 0;
++ continue;
++ }
++ if (c)
++ continue;
++ for (on_off = complementaly; on_off->opt; on_off++)
++ if (on_off->opt == *s)
++ break;
++ pair = on_off;
++ for(s++; *s && *s != ':'; s++) {
++ if (*s == '-' || *s == '~') {
++ c = *s;
++ } else if(*s == '*') {
++ pair->list_flg++;
++ } else {
++ unsigned long *pair_switch = &(pair->switch_on);
++ if(c)
++ pair_switch = c == '-' ? &(pair->switch_off) : &(pair->incongruously);
++ for (on_off = complementaly; on_off->opt; on_off++)
++ if (on_off->opt == *s) {
++ *pair_switch |= on_off->switch_on;
++ break;
++ }
++ }
++ }
++ s--;
+ }
+- if(flags & on_off->incongruously)
+- flags |= 0x80000000UL;
+- flags &= ~on_off->switch_off;
+- flags |= on_off->switch_on;
+- if(on_off->list_flg) {
+- *(llist_t **)(on_off->optarg) =
+- llist_add_to(*(llist_t **)(on_off->optarg), optarg);
+- } else if (on_off->optarg) {
+- *(char **)(on_off->optarg) = optarg;
++
++ while ((c = getopt_long (argc, argv, applet_opts,
++ bb_applet_long_options, NULL)) > 0) {
++ for (on_off = complementaly; on_off->opt != c; on_off++) {
++ if(!on_off->opt)
++ bb_show_usage ();
++ }
++ if(flags & on_off->incongruously)
++ flags |= BB_GETOPT_ERROR;
++ flags &= ~on_off->switch_off;
++ flags |= on_off->switch_on;
++ if(on_off->list_flg) {
++ *(llist_t **)(on_off->optarg) =
++ llist_add_to(*(llist_t **)(on_off->optarg), optarg);
++ } else if (on_off->optarg) {
++ *(char **)(on_off->optarg) = optarg;
++ }
+ }
+- }
+- return flags;
++
++ return flags;
+ }
+diff -Nur busybox-1.00/libbb/hash_fd.c busybox/libbb/hash_fd.c
+--- busybox-1.00/libbb/hash_fd.c 2004-03-15 09:28:42.000000000 +0100
++++ busybox/libbb/hash_fd.c 2005-06-04 08:20:15.000000000 +0200
+@@ -197,7 +197,7 @@
+ static uint32_t bits[4] = { 0x80000000, 0x00800000, 0x00008000, 0x00000080 };
+ # endif /* __BYTE_ORDER */
+
+-void sha1_end(unsigned char hval[], struct sha1_ctx_t *ctx)
++static void sha1_end(unsigned char hval[], struct sha1_ctx_t *ctx)
+ {
+ uint32_t i, cnt = (uint32_t) (ctx->count[0] & SHA1_MASK);
+
+diff -Nur busybox-1.00/libbb/inet_common.c busybox/libbb/inet_common.c
+--- busybox-1.00/libbb/inet_common.c 2004-03-10 08:42:38.000000000 +0100
++++ busybox/libbb/inet_common.c 2005-06-04 08:20:15.000000000 +0200
+@@ -4,7 +4,7 @@
+ *
+ * Heavily modified by Manuel Novoa III Mar 12, 2001
+ *
+- * Version: $Id$
++ * Version: $Id$
+ *
+ */
+
+diff -Nur busybox-1.00/libbb/interface.c busybox/libbb/interface.c
+--- busybox-1.00/libbb/interface.c 2004-08-26 23:45:21.000000000 +0200
++++ busybox/libbb/interface.c 2005-06-04 08:20:15.000000000 +0200
+@@ -15,7 +15,7 @@
+ * that either displays or sets the characteristics of
+ * one or more of the system's networking interfaces.
+ *
+- * Version: $Id$
++ * Version: $Id$
+ *
+ * Author: Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+ * and others. Copyright 1993 MicroWalt Corporation
+@@ -888,6 +888,20 @@
+ return sfd;
+ }
+
++#ifdef CONFIG_FEATURE_CLEAN_UP
++static void sockets_close(void)
++{
++ struct aftype **aft;
++ for (aft = aftypes; *aft != NULL; aft++) {
++ struct aftype *af = *aft;
++ if( af->fd != -1 ) {
++ close(af->fd);
++ af->fd = -1;
++ }
++ }
++}
++#endif
++
+ /* like strcmp(), but knows about numbers */
+ static int nstrcmp(const char *a, const char *b)
+ {
+@@ -986,7 +1000,7 @@
+ return err;
+ }
+
+-char *get_name(char *name, char *p)
++static char *get_name(char *name, char *p)
+ {
+ /* Extract <name>[:<alias>] from nul-terminated p where p matches
+ <name>[:<alias>]: after leading whitespace.
+@@ -1223,17 +1237,13 @@
+ }
+ #endif
+
++#ifdef SIOCGIFMAP
+ strcpy(ifr.ifr_name, ifname);
+- if (ioctl(skfd, SIOCGIFMAP, &ifr) < 0)
+- memset(&ife->map, 0, sizeof(struct ifmap));
++ if (ioctl(skfd, SIOCGIFMAP, &ifr) == 0)
++ ife->map = ifr.ifr_map;
+ else
+- memcpy(&ife->map, &ifr.ifr_map, sizeof(struct ifmap));
+-
+- strcpy(ifr.ifr_name, ifname);
+- if (ioctl(skfd, SIOCGIFMAP, &ifr) < 0)
++#endif
+ memset(&ife->map, 0, sizeof(struct ifmap));
+- else
+- ife->map = ifr.ifr_map;
+
+ #ifdef HAVE_TXQUEUELEN
+ strcpy(ifr.ifr_name, ifname);
+@@ -1374,7 +1384,7 @@
+ #if HAVE_HWETHER
+ #include <net/if_arp.h>
+
+-#if __GLIBC__ >=2 && __GLIBC_MINOR >= 1
++#if (__GLIBC__ >=2 && __GLIBC_MINOR >= 1) || defined(_NEWLIB_VERSION)
+ #include <net/ethernet.h>
+ #else
+ #include <linux/if_ether.h>
+@@ -2078,6 +2088,8 @@
+
+ /* Do we have to show the current setup? */
+ status = if_print(ifname);
+- close(skfd);
++#ifdef CONFIG_FEATURE_CLEAN_UP
++ sockets_close();
++#endif
+ exit(status < 0);
+ }
+diff -Nur busybox-1.00/libbb/loop.c busybox/libbb/loop.c
+--- busybox-1.00/libbb/loop.c 2004-08-16 10:36:28.000000000 +0200
++++ busybox/libbb/loop.c 2005-06-04 08:20:15.000000000 +0200
+@@ -19,6 +19,10 @@
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
++#include <features.h>
++#if defined (__GLIBC__) && !defined(__UCLIBC__)
++#include <linux/posix_types.h>
++#endif
+ #include <stdio.h>
+ #include <errno.h>
+ #include <fcntl.h>
+@@ -30,7 +34,6 @@
+ /* Grumble... The 2.6.x kernel breaks asm/posix_types.h
+ * so we get to try and cope as best we can... */
+ #include <linux/version.h>
+-#include <asm/posix_types.h>
+
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+ #define __bb_kernel_dev_t __kernel_old_dev_t
+diff -Nur busybox-1.00/libbb/messages.c busybox/libbb/messages.c
+--- busybox-1.00/libbb/messages.c 2004-03-15 09:28:42.000000000 +0100
++++ busybox/libbb/messages.c 2005-06-04 08:20:15.000000000 +0200
+@@ -36,6 +36,9 @@
+ #ifdef L_write_error
+ const char * const bb_msg_write_error = "Write Error";
+ #endif
++#ifdef L_read_error
++ const char * const bb_msg_read_error = "Read Error";
++#endif
+ #ifdef L_name_longer_than_foo
+ const char * const bb_msg_name_longer_than_foo = "Names longer than %d chars not supported.";
+ #endif
+diff -Nur busybox-1.00/libbb/procps.c busybox/libbb/procps.c
+--- busybox-1.00/libbb/procps.c 2004-08-27 00:18:58.000000000 +0200
++++ busybox/libbb/procps.c 2005-06-04 08:20:15.000000000 +0200
+@@ -16,11 +16,7 @@
+
+ #include "libbb.h"
+
+-extern procps_status_t * procps_scan(int save_user_arg0
+-#ifdef CONFIG_SELINUX
+- , int use_selinux , security_id_t *sid
+-#endif
+- )
++extern procps_status_t * procps_scan(int save_user_arg0)
+ {
+ static DIR *dir;
+ struct dirent *entry;
+@@ -60,16 +56,9 @@
+ my_getpwuid(curstatus.user, sb.st_uid, sizeof(curstatus.user));
+
+ sprintf(status, "/proc/%d/stat", pid);
++
+ if((fp = fopen(status, "r")) == NULL)
+ continue;
+-#ifdef CONFIG_SELINUX
+- if(use_selinux)
+- {
+- if(fstat_secure(fileno(fp), &sb, sid))
+- continue;
+- }
+- else
+-#endif
+ name = fgets(buf, sizeof(buf), fp);
+ fclose(fp);
+ if(name == NULL)
+diff -Nur busybox-1.00/libbb/run_shell.c busybox/libbb/run_shell.c
+--- busybox-1.00/libbb/run_shell.c 2004-03-15 09:28:43.000000000 +0100
++++ busybox/libbb/run_shell.c 2005-06-04 08:20:15.000000000 +0200
+@@ -37,7 +37,33 @@
+ #include <ctype.h>
+ #include "libbb.h"
+ #ifdef CONFIG_SELINUX
+-#include <proc_secure.h>
++#include <selinux/selinux.h> /* for setexeccon */
++#endif
++
++#ifdef CONFIG_SELINUX
++static security_context_t current_sid=NULL;
++
++void
++renew_current_security_context(void)
++{
++ if (current_sid)
++ freecon(current_sid); /* Release old context */
++
++ getcon(¤t_sid); /* update */
++
++ return;
++}
++void
++set_current_security_context(security_context_t sid)
++{
++ if (current_sid)
++ freecon(current_sid); /* Release old context */
++
++ current_sid=sid;
++
++ return;
++}
++
+ #endif
+
+ /* Run SHELL, or DEFAULT_SHELL if SHELL is empty.
+@@ -45,11 +71,7 @@
+ If ADDITIONAL_ARGS is nonzero, pass it to the shell as more
+ arguments. */
+
+-void run_shell ( const char *shell, int loginshell, const char *command, const char **additional_args
+-#ifdef CONFIG_SELINUX
+- , security_id_t sid
+-#endif
+-)
++void run_shell ( const char *shell, int loginshell, const char *command, const char **additional_args)
+ {
+ const char **args;
+ int argno = 1;
+@@ -78,10 +100,11 @@
+ }
+ args [argno] = 0;
+ #ifdef CONFIG_SELINUX
+- if(sid)
+- execve_secure(shell, (char **) args, environ, sid);
+- else
++ if ( (current_sid) && (!setexeccon(current_sid)) ) {
++ freecon(current_sid);
++ execve(shell, (char **) args, environ);
++ } else
+ #endif
+- execv ( shell, (char **) args );
++ execv ( shell, (char **) args );
+ bb_perror_msg_and_die ( "cannot run %s", shell );
+ }
+diff -Nur busybox-1.00/libbb/syscalls.c busybox/libbb/syscalls.c
+--- busybox-1.00/libbb/syscalls.c 2004-03-15 09:28:43.000000000 +0100
++++ busybox/libbb/syscalls.c 2005-06-04 08:20:15.000000000 +0200
+@@ -29,7 +29,7 @@
+ #include <sys/syscall.h>
+ #include "libbb.h"
+
+-int sysfs( int option, unsigned int fs_index, char * buf)
++int sysfs(int option, unsigned int fs_index, char * buf)
+ {
+ return(syscall(__NR_sysfs, option, fs_index, buf));
+ }
+@@ -39,60 +39,59 @@
+ #ifndef __NR_pivot_root
+ #warning This kernel does not support the pivot_root syscall
+ #warning -> The pivot_root system call is being stubbed out...
+- /* BusyBox was compiled against a kernel that did not support
+- * the pivot_root system call. To make this application work,
+- * you will need to recompile with a kernel supporting the
+- * pivot_root system call.
+- */
+- bb_error_msg("\n\nTo make this application work, you will need to recompile\n"
+- "BusyBox with a kernel supporting the pivot_root system call.\n");
+- errno=ENOSYS;
+- return -1;
++ /* BusyBox was compiled against a kernel that did not support
++ * the pivot_root system call. To make this application work,
++ * you will need to recompile with a kernel supporting the
++ * pivot_root system call.
++ */
++ bb_error_msg("\n\nTo make this application work, you will need to recompile\n"
++ "BusyBox with a kernel supporting the pivot_root system call.\n");
++ errno = ENOSYS;
++ return -1;
+ #else
+- return(syscall(__NR_pivot_root, new_root, put_old));
+-#endif
++ return(syscall(__NR_pivot_root, new_root, put_old));
++#endif /* __NR_pivot_root */
+ }
+
+
+-
+-/* These syscalls are not included in ancient glibc versions */
++/* These syscalls are not included in ancient glibc versions,
++ so we have to define them ourselves, whee ! */
+ #if ((__GLIBC__ <= 2) && (__GLIBC_MINOR__ < 1))
+
+ int bdflush(int func, int data)
+ {
+- return(syscall(__NR_bdflush, func, data));
++ return(syscall(__NR_bdflush, func, data));
+ }
+
+ #ifndef __alpha__
+ # define __NR_klogctl __NR_syslog
+ int klogctl(int type, char *b, int len)
+ {
+- return(syscall(__NR_klogctl, type, b, len));
++ return(syscall(__NR_klogctl, type, b, len));
+ }
+-#endif
++#endif /* __alpha__ */
+
+
+ int umount2(const char * special_file, int flags)
+ {
+-#ifndef __NR_pivot_root
++#ifndef __NR_umount2
+ #warning This kernel does not support the umount2 syscall
+ #warning -> The umount2 system call is being stubbed out...
+- /* BusyBox was compiled against a kernel that did not support
+- * the umount2 system call. To make this application work,
+- * you will need to recompile with a kernel supporting the
+- * umount2 system call.
+- */
+- bb_error_msg("\n\nTo make this application work, you will need to recompile\n"
+- "BusyBox with a kernel supporting the umount2 system call.\n");
+- errno=ENOSYS;
+- return -1;
++ /* BusyBox was compiled against a kernel that did not support
++ * the umount2 system call. To make this application work,
++ * you will need to recompile with a kernel supporting the
++ * umount2 system call.
++ */
++ bb_error_msg("\n\nTo make this application work, you will need to recompile\n"
++ "BusyBox with a kernel supporting the umount2 system call.\n");
++ errno = ENOSYS;
++ return -1;
+ #else
+- return(syscall(__NR_umount2, special_file, flags));
+-#endif
++ return(syscall(__NR_umount2, special_file, flags));
++#endif /* __NR_pivot_root */
+ }
+
+-
+-#endif
++#endif /* old glibc check */
+
+
+ /* END CODE */
+diff -Nur busybox-1.00/loginutils/delgroup.c busybox/loginutils/delgroup.c
+--- busybox-1.00/loginutils/delgroup.c 2003-07-14 23:50:51.000000000 +0200
++++ busybox/loginutils/delgroup.c 2005-06-04 08:20:18.000000000 +0200
+@@ -59,4 +59,4 @@
+ return (EXIT_SUCCESS);
+ }
+
+-/* $Id$ */
++/* $Id$ */
+diff -Nur busybox-1.00/loginutils/delline.c busybox/loginutils/delline.c
+--- busybox-1.00/loginutils/delline.c 2003-07-14 23:50:51.000000000 +0200
++++ busybox/loginutils/delline.c 2005-06-04 08:20:18.000000000 +0200
+@@ -110,4 +110,4 @@
+ }
+
+
+-/* $Id$ */
++/* $Id$ */
+diff -Nur busybox-1.00/loginutils/deluser.c busybox/loginutils/deluser.c
+--- busybox-1.00/loginutils/deluser.c 2003-07-14 22:20:45.000000000 +0200
++++ busybox/loginutils/deluser.c 2005-06-04 08:20:18.000000000 +0200
+@@ -65,4 +65,4 @@
+ return (EXIT_SUCCESS);
+ }
+
+-/* $Id$ */
++/* $Id$ */
+diff -Nur busybox-1.00/loginutils/getty.c busybox/loginutils/getty.c
+--- busybox-1.00/loginutils/getty.c 2004-08-27 00:26:26.000000000 +0200
++++ busybox/loginutils/getty.c 2005-06-04 08:20:18.000000000 +0200
+@@ -85,7 +85,7 @@
+ #define DEF_QUIT CTL('\\') /* default quit char */
+ #define DEF_KILL CTL('U') /* default kill char */
+ #define DEF_EOF CTL('D') /* default EOF char */
+-#define DEF_EOL 0
++#define DEF_EOL '\n'
+ #define DEF_SWITCH 0 /* default switch char */
+
+ /*
+@@ -151,7 +151,7 @@
+
+ /* Storage for things detected while the login name was read. */
+
+-struct chardata {
++static struct chardata {
+ int erase; /* erase character */
+ int kill; /* kill character */
+ int eol; /* end-of-line character */
+@@ -161,7 +161,7 @@
+
+ /* Initial values for the above. */
+
+-struct chardata init_chardata = {
++static struct chardata init_chardata = {
+ DEF_ERASE, /* default erase character */
+ DEF_KILL, /* default kill character */
+ 13, /* default eol char */
+diff -Nur busybox-1.00/loginutils/login.c busybox/loginutils/login.c
+--- busybox-1.00/loginutils/login.c 2004-08-27 00:26:26.000000000 +0200
++++ busybox/loginutils/login.c 2005-06-04 08:20:18.000000000 +0200
+@@ -17,10 +17,10 @@
+
+ #include "busybox.h"
+ #ifdef CONFIG_SELINUX
+-#include <flask_util.h>
+-#include <get_sid_list.h>
+-#include <proc_secure.h>
+-#include <fs_secure.h>
++#include <selinux/selinux.h> /* for is_selinux_enabled() */
++#include <selinux/get_context_list.h> /* for get_default_context() */
++#include <selinux/flask.h> /* for security class definitions */
++#include <errno.h>
+ #endif
+
+ #ifdef CONFIG_FEATURE_U_W_TMP
+@@ -28,7 +28,7 @@
+ static void checkutmp(int picky);
+ static void setutmp(const char *name, const char *line);
+ /* Stuff global to this file */
+-struct utmp utent;
++static struct utmp utent;
+ #endif
+
+ // login defines
+@@ -79,8 +79,7 @@
+ char *opt_host = 0;
+ int alarmstarted = 0;
+ #ifdef CONFIG_SELINUX
+- int flask_enabled = is_flask_enabled();
+- security_id_t sid = 0, old_tty_sid, new_tty_sid;
++ security_context_t stat_sid = NULL, sid = NULL, old_tty_sid=NULL, new_tty_sid=NULL;
+ #endif
+
+ username[0]=0;
+@@ -225,41 +224,45 @@
+ #ifdef CONFIG_FEATURE_U_W_TMP
+ setutmp ( username, tty );
+ #endif
++
++ if ( *tty != '/' )
++ snprintf ( full_tty, sizeof( full_tty ) - 1, "/dev/%s", tty);
++ else
++ safe_strncpy ( full_tty, tty, sizeof( full_tty ) - 1 );
++
+ #ifdef CONFIG_SELINUX
+- if (flask_enabled)
++ if (is_selinux_enabled())
+ {
+ struct stat st;
++ int rc;
+
+- if (get_default_sid(username, 0, &sid))
++ if (get_default_context(username, NULL, &sid))
+ {
+ fprintf(stderr, "Unable to get SID for %s\n", username);
+ exit(1);
+ }
+- if (stat_secure(tty, &st, &old_tty_sid))
++ rc = getfilecon(full_tty,&stat_sid);
++ freecon(stat_sid);
++ if ((rc<0) || (stat(full_tty, &st)<0))
+ {
+- fprintf(stderr, "stat_secure(%.100s) failed: %.100s\n", tty, strerror(errno));
++ fprintf(stderr, "stat_secure(%.100s) failed: %.100s\n", full_tty, strerror(errno));
+ return EXIT_FAILURE;
+ }
+- if (security_change_sid (sid, old_tty_sid, SECCLASS_CHR_FILE, &new_tty_sid) != 0)
++ if (security_compute_relabel (sid, old_tty_sid, SECCLASS_CHR_FILE, &new_tty_sid) != 0)
+ {
+- fprintf(stderr, "security_change_sid(%.100s) failed: %.100s\n", tty, strerror(errno));
++ fprintf(stderr, "security_change_sid(%.100s) failed: %.100s\n", full_tty, strerror(errno));
+ return EXIT_FAILURE;
+ }
+- if(chsid(tty, new_tty_sid) != 0)
++ if(setfilecon(full_tty, new_tty_sid) != 0)
+ {
+- fprintf(stderr, "chsid(%.100s, %d) failed: %.100s\n", tty, new_tty_sid, strerror(errno));
++ fprintf(stderr, "chsid(%.100s, %s) failed: %.100s\n", full_tty, new_tty_sid, strerror(errno));
+ return EXIT_FAILURE;
+ }
++ freecon(sid);
++ freecon(old_tty_sid);
++ freecon(new_tty_sid);
+ }
+- else
+- sid = 0;
+ #endif
+-
+- if ( *tty != '/' )
+- snprintf ( full_tty, sizeof( full_tty ) - 1, "/dev/%s", tty);
+- else
+- safe_strncpy ( full_tty, tty, sizeof( full_tty ) - 1 );
+-
+ if ( !is_my_tty ( full_tty ))
+ syslog ( LOG_ERR, "unable to determine TTY name, got %s\n", full_tty );
+
+@@ -279,11 +282,10 @@
+
+ if ( pw-> pw_uid == 0 )
+ syslog ( LOG_INFO, "root login %s\n", fromhost );
+- run_shell ( tmp, 1, 0, 0
+ #ifdef CONFIG_SELINUX
+- , sid
++ set_current_security_context(sid);
+ #endif
+- ); /* exec the shell finally. */
++ run_shell ( tmp, 1, 0, 0); /* exec the shell finally. */
+
+ return EXIT_FAILURE;
+ }
+@@ -387,7 +389,7 @@
+ }
+
+
+-static void motd ( )
++static void motd (void)
+ {
+ FILE *fp;
+ register int c;
+diff -Nur busybox-1.00/loginutils/passwd.c busybox/loginutils/passwd.c
+--- busybox-1.00/loginutils/passwd.c 2004-09-15 04:39:09.000000000 +0200
++++ busybox/loginutils/passwd.c 2005-06-04 08:20:18.000000000 +0200
+@@ -21,7 +21,7 @@
+ static void set_filesize_limit(int blocks);
+
+
+-int get_algo(char *a)
++static int get_algo(char *a)
+ {
+ int x = 1; /* standard: MD5 */
+
+@@ -31,7 +31,7 @@
+ }
+
+
+-extern int update_passwd(const struct passwd *pw, char *crypt_pw)
++static int update_passwd(const struct passwd *pw, char *crypt_pw)
+ {
+ char filename[1024];
+ char buf[1025];
+diff -Nur busybox-1.00/loginutils/su.c busybox/loginutils/su.c
+--- busybox-1.00/loginutils/su.c 2004-03-15 09:28:46.000000000 +0100
++++ busybox/loginutils/su.c 2005-06-04 08:20:18.000000000 +0200
+@@ -147,11 +147,10 @@
+
+ change_identity ( pw );
+ setup_environment ( opt_shell, opt_loginshell, !opt_preserve, pw );
+- run_shell ( opt_shell, opt_loginshell, opt_command, (const char**)opt_args
+ #ifdef CONFIG_SELINUX
+- , 0
++ set_current_security_context(NULL);
+ #endif
+- );
++ run_shell ( opt_shell, opt_loginshell, opt_command, (const char**)opt_args);
+
+ return EXIT_FAILURE;
+ }
+diff -Nur busybox-1.00/loginutils/sulogin.c busybox/loginutils/sulogin.c
+--- busybox-1.00/loginutils/sulogin.c 2004-05-01 03:27:30.000000000 +0200
++++ busybox/loginutils/sulogin.c 2005-06-04 08:20:18.000000000 +0200
+@@ -153,6 +153,12 @@
+ puts("Entering System Maintenance Mode\n");
+ fflush(stdout);
+ syslog(LOG_INFO, "System Maintenance Mode\n");
++
++#ifdef CONFIG_SELINUX
++ renew_current_security_context();
++#endif
++
+ run_shell(pwent.pw_shell, 1, 0, 0);
++
+ return (0);
+ }
+diff -Nur busybox-1.00/loginutils/vlock.c busybox/loginutils/vlock.c
+--- busybox-1.00/loginutils/vlock.c 2004-05-01 03:27:30.000000000 +0200
++++ busybox/loginutils/vlock.c 2005-06-04 08:20:18.000000000 +0200
+@@ -51,7 +51,7 @@
+ static struct spwd *spw;
+
+ /* getspuid - get a shadow entry by uid */
+-struct spwd *getspuid(uid_t uid)
++static struct spwd *getspuid(uid_t uid)
+ {
+ struct spwd *sp;
+ struct passwd *mypw;
+diff -Nur busybox-1.00/miscutils/Config.in busybox/miscutils/Config.in
+--- busybox-1.00/miscutils/Config.in 2004-08-27 01:12:59.000000000 +0200
++++ busybox/miscutils/Config.in 2005-06-04 08:20:16.000000000 +0200
+@@ -83,6 +83,12 @@
+ help
+ Increases logging to stderr or syslog.
+
++config CONFIG_EJECT
++ bool "eject"
++ default n
++ help
++ Used to eject cdroms. (defaults to /dev/cdrom)
++
+ config CONFIG_LAST
+ bool "last"
+ default n
+diff -Nur busybox-1.00/miscutils/Makefile.in busybox/miscutils/Makefile.in
+--- busybox-1.00/miscutils/Makefile.in 2004-10-08 09:45:39.000000000 +0200
++++ busybox/miscutils/Makefile.in 2005-06-04 08:20:16.000000000 +0200
+@@ -24,19 +24,20 @@
+ srcdir=$(top_srcdir)/miscutils
+
+ MISCUTILS-y:=
+-MISCUTILS-$(CONFIG_ADJTIMEX) += adjtimex.o
+-MISCUTILS-$(CONFIG_CROND) += crond.o
+-MISCUTILS-$(CONFIG_CRONTAB) += crontab.o
+-MISCUTILS-$(CONFIG_DC) += dc.o
+-MISCUTILS-$(CONFIG_DEVFSD) += devfsd.o
+-MISCUTILS-$(CONFIG_HDPARM) += hdparm.o
+-MISCUTILS-$(CONFIG_LAST) += last.o
+-MISCUTILS-$(CONFIG_MAKEDEVS) += makedevs.o
+-MISCUTILS-$(CONFIG_MT) += mt.o
+-MISCUTILS-$(CONFIG_RX) += rx.o
+-MISCUTILS-$(CONFIG_STRINGS) += strings.o
+-MISCUTILS-$(CONFIG_TIME) += time.o
+-MISCUTILS-$(CONFIG_WATCHDOG) += watchdog.o
++MISCUTILS-$(CONFIG_ADJTIMEX) += adjtimex.o
++MISCUTILS-$(CONFIG_CROND) += crond.o
++MISCUTILS-$(CONFIG_CRONTAB) += crontab.o
++MISCUTILS-$(CONFIG_DC) += dc.o
++MISCUTILS-$(CONFIG_DEVFSD) += devfsd.o
++MISCUTILS-$(CONFIG_EJECT) += eject.o
++MISCUTILS-$(CONFIG_HDPARM) += hdparm.o
++MISCUTILS-$(CONFIG_LAST) += last.o
++MISCUTILS-$(CONFIG_MAKEDEVS) += makedevs.o
++MISCUTILS-$(CONFIG_MT) += mt.o
++MISCUTILS-$(CONFIG_RX) += rx.o
++MISCUTILS-$(CONFIG_STRINGS) += strings.o
++MISCUTILS-$(CONFIG_TIME) += time.o
++MISCUTILS-$(CONFIG_WATCHDOG) += watchdog.o
+
+ libraries-y+=$(MISCUTILS_DIR)$(MISCUTILS_AR)
+
+diff -Nur busybox-1.00/miscutils/eject.c busybox/miscutils/eject.c
+--- busybox-1.00/miscutils/eject.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/miscutils/eject.c 2005-06-04 08:20:16.000000000 +0200
+@@ -0,0 +1,63 @@
++/*
++ * eject implementation for busybox
++ *
++ * Copyright (C) 2004 Peter Willis <psyphreak@phreaker.net>
++ *
++ * 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
++ *
++ */
++
++/*
++ * This is a simple hack of eject based on something Erik posted in #uclibc.
++ * Most of the dirty work blatantly ripped off from cat.c =)
++ */
++
++#include <fcntl.h>
++#include <sys/ioctl.h>
++#include <unistd.h>
++#include <sys/mount.h>
++#include <mntent.h>
++#include "busybox.h"
++
++/* various defines swiped from linux/cdrom.h */
++#define CDROMCLOSETRAY 0x5319 /* pendant of CDROMEJECT */
++#define CDROMEJECT 0x5309 /* Ejects the cdrom media */
++#define DEFAULT_CDROM "/dev/cdrom"
++
++extern int eject_main(int argc, char **argv)
++{
++ unsigned long flags;
++ char *device;
++ struct mntent *m;
++
++ flags = bb_getopt_ulflags(argc, argv, "t");
++ device=argv[optind] ? : DEFAULT_CDROM;
++
++ if((m = find_mount_point(device, bb_path_mtab_file))) {
++ if(umount(m->mnt_dir))
++ bb_error_msg_and_die("Can't umount");
++#ifdef CONFIG_FEATURE_MTAB_SUPPORT
++ else
++ erase_mtab(m->mnt_fsname);
++#endif
++ }
++ if (ioctl(bb_xopen( device,
++ (O_RDONLY | O_NONBLOCK)),
++ ( flags ? CDROMCLOSETRAY : CDROMEJECT)))
++ {
++ bb_perror_msg_and_die(device);
++ }
++ return(EXIT_SUCCESS);
++}
+diff -Nur busybox-1.00/miscutils/hdparm.c busybox/miscutils/hdparm.c
+--- busybox-1.00/miscutils/hdparm.c 2004-07-21 00:53:59.000000000 +0200
++++ busybox/miscutils/hdparm.c 2005-06-04 08:20:16.000000000 +0200
+@@ -467,8 +467,8 @@
+
+ /* Busybox messages and functions */
+
+-const char * const bb_msg_shared_mem ="could not %s sharedmem buf";
+-const char * const bb_msg_op_not_supp =" operation not supported on %s disks";
++static const char * const bb_msg_shared_mem ="could not %s sharedmem buf";
++static const char * const bb_msg_op_not_supp =" operation not supported on %s disks";
+
+ static void bb_ioctl(int fd, int request, void *argp, const char *string)
+ {
+diff -Nur busybox-1.00/miscutils/rx.c busybox/miscutils/rx.c
+--- busybox-1.00/miscutils/rx.c 2004-03-15 09:28:46.000000000 +0100
++++ busybox/miscutils/rx.c 2005-06-04 08:20:16.000000000 +0200
+@@ -1,6 +1,6 @@
+ /*-------------------------------------------------------------------------
+ * Filename: xmodem.c
+- * Version: $Id$
++ * Version: $Id$
+ * Copyright: Copyright (C) 2001, Hewlett-Packard Company
+ * Author: Christopher Hoover <ch@hpl.hp.com>
+ * Description: xmodem functionality for uploading of kernels
+diff -Nur busybox-1.00/modutils/Config.in busybox/modutils/Config.in
+--- busybox-1.00/modutils/Config.in 2004-07-20 08:09:14.000000000 +0200
++++ busybox/modutils/Config.in 2005-06-04 08:20:10.000000000 +0200
+@@ -21,7 +21,7 @@
+ config CONFIG_FEATURE_2_6_MODULES
+ bool " Support version 2.6.x Linux kernels"
+ default n
+- depends on CONFIG_INSMOD
++ depends on CONFIG_INSMOD
+ help
+ Support module loading for newer 2.6.x Linux kernels.
+
+@@ -80,6 +80,14 @@
+ help
+ lsmod is used to display a list of loaded modules.
+
++config CONFIG_FEATURE_LSMOD_PRETTY_2_6_OUTPUT
++ bool " lsmod pretty output for 2.6.x Linux kernels "
++ default n
++ depends on CONFIG_LSMOD
++ help
++ This option makes output format of lsmod adjusted to
++ the format of module-init-tools for Linux kernel 2.6.
++
+ config CONFIG_FEATURE_QUERY_MODULE_INTERFACE
+ bool
+ default y
+diff -Nur busybox-1.00/modutils/insmod.c busybox/modutils/insmod.c
+--- busybox-1.00/modutils/insmod.c 2004-09-03 01:03:25.000000000 +0200
++++ busybox/modutils/insmod.c 2005-06-04 08:20:10.000000000 +0200
+@@ -109,6 +109,14 @@
+ #endif
+
+
++/* Alpha */
++#if defined(__alpha__)
++#define MATCH_MACHINE(x) (x == EM_ALPHA)
++#define SHT_RELM SHT_RELA
++#define Elf64_RelM Elf64_Rela
++#define ELFCLASSM ELFCLASS64
++#endif
++
+ /* ARM support */
+ #if defined(__arm__)
+ #define MATCH_MACHINE(x) (x == EM_ARM)
+@@ -135,6 +143,19 @@
+ #endif
+ #endif
+
++/* PA-RISC / HP-PA */
++#if defined(__hppa__)
++#define MATCH_MACHINE(x) (x == EM_PARISC)
++#define SHT_RELM SHT_RELA
++#if defined(__LP64__)
++#define Elf64_RelM Elf64_Rela
++#define ELFCLASSM ELFCLASS64
++#else
++#define Elf32_RelM Elf32_Rela
++#define ELFCLASSM ELFCLASS32
++#endif
++#endif
++
+ /* H8/300 */
+ #if defined(__H8300H__) || defined(__H8300S__)
+ #define MATCH_MACHINE(x) (x == EM_H8_300)
+@@ -269,8 +290,8 @@
+ /* X86_64 */
+ #if defined(__x86_64__)
+ #define MATCH_MACHINE(x) (x == EM_X86_64)
+-#define SHT_RELM SHT_REL
+-#define Elf64_RelM Elf64_Rel
++#define SHT_RELM SHT_RELA
++#define Elf64_RelM Elf64_Rela
+ #define ELFCLASSM ELFCLASS64
+ #endif
+
+@@ -308,7 +329,7 @@
+ #ifndef MODUTILS_MODULE_H
+ static const int MODUTILS_MODULE_H = 1;
+
+-#ident "$Id$"
++#ident "$Id$"
+
+ /*======================================================================*/
+ /* For sizeof() which are related to the module platform and not to the
+@@ -466,7 +487,7 @@
+ #ifndef MODUTILS_OBJ_H
+ static const int MODUTILS_OBJ_H = 1;
+
+-#ident "$Id$"
++#ident "$Id$"
+
+ /* The relocatable object is manipulated using elfin types. */
+
+@@ -3432,8 +3453,8 @@
+ ".text",
+ ".rodata",
+ ".data",
+- ".bss"
+- ".sbss"
++ ".bss",
++ ".sbss"
+ };
+
+ if (realpath(filename, real)) {
+diff -Nur busybox-1.00/modutils/lsmod.c busybox/modutils/lsmod.c
+--- busybox-1.00/modutils/lsmod.c 2004-03-15 09:28:47.000000000 +0100
++++ busybox/modutils/lsmod.c 2005-06-04 08:20:10.000000000 +0200
+@@ -164,10 +164,52 @@
+ {
+ printf("Module Size Used by");
+ check_tainted();
++#if defined(CONFIG_FEATURE_LSMOD_PRETTY_2_6_OUTPUT)
++ {
++ FILE *file;
++ char line[4096];
+
++ file = fopen("/proc/modules", "r");
++
++ if (!file)
++ bb_error_msg_and_die("Opening /proc/modules");
++
++ while (fgets(line, sizeof(line), file)) {
++ char *tok;
++
++ tok = strtok(line, " \t");
++ printf("%-19s", tok);
++ tok = strtok(NULL, " \t\n");
++ printf(" %8s", tok);
++ tok = strtok(NULL, " \t\n");
++ /* Null if no module unloading support. */
++ if (tok) {
++ printf(" %s", tok);
++ tok = strtok(NULL, "\n");
++ if (!tok)
++ tok = "";
++ /* New-style has commas, or -. If so,
++ truncate (other fields might follow). */
++ else if (strchr(tok, ',')) {
++ tok = strtok(tok, "\t ");
++ /* Strip trailing comma. */
++ if (tok[strlen(tok)-1] == ',')
++ tok[strlen(tok)-1] = '\0';
++ } else if (tok[0] == '-'
++ && (tok[1] == '\0' || isspace(tok[1])))
++ tok = "";
++ printf(" %s", tok);
++ }
++ printf("\n");
++ }
++ fclose(file);
++ }
++ return 0; /* Success */
++#else
+ if (bb_xprint_file_by_name("/proc/modules") < 0) {
+ return 0;
+ }
++#endif /* CONFIG_FEATURE_2_6_MODULES */
+ return 1;
+ }
+
+diff -Nur busybox-1.00/modutils/modprobe.c busybox/modutils/modprobe.c
+--- busybox-1.00/modutils/modprobe.c 2004-09-24 11:18:55.000000000 +0200
++++ busybox/modutils/modprobe.c 2005-06-04 08:20:10.000000000 +0200
+@@ -61,7 +61,7 @@
+ static int autoclean, show_only, quiet, do_syslog, verbose;
+ static int k_version;
+
+-int parse_tag_value ( char *buffer, char **ptag, char **pvalue )
++static int parse_tag_value ( char *buffer, char **ptag, char **pvalue )
+ {
+ char *tag, *value;
+
+diff -Nur busybox-1.00/networking/Config.in busybox/networking/Config.in
+--- busybox-1.00/networking/Config.in 2004-09-23 22:08:46.000000000 +0200
++++ busybox/networking/Config.in 2005-06-04 08:20:05.000000000 +0200
+@@ -18,6 +18,19 @@
+ help
+ Ping hosts by ARP packets
+
++config CONFIG_ETHER_WAKE
++ bool "ether-wake"
++ default n
++ help
++ Send a magic packet to wake up sleeping machines.
++
++config CONFIG_FAKEIDENTD
++ bool "fakeidentd"
++ default n
++ help
++ fakeidentd listens to the ident port and returns a set fake
++ value whatever it gets.
++
+ config CONFIG_FTPGET
+ bool "ftpget"
+ default n
+@@ -415,6 +428,14 @@
+ A simple Unix utility which reads and writes data across network
+ connections.
+
++config CONFIG_NC_GAPING_SECURITY_HOLE
++ bool "gaping security hole"
++ default n
++ depends on CONFIG_NC
++ help
++ Add support for executing a program after making or receiving a
++ successful connection (-e option).
++
+ config CONFIG_NETSTAT
+ bool "netstat"
+ default n
+@@ -630,5 +651,16 @@
+
+ source networking/udhcp/Config.in
+
++config CONFIG_ZCIP
++ bool "zcip"
++ default n
++ help
++ ZCIP provides ZeroConf IPv4 address selection, according to RFC 3927.
++ It's a daemon that allocates and defends a dynamically assigned
++ address on the 169.254/16 network, requiring no system administrator.
++
++ See http://www.zeroconf.org for further details, and "zcip.script"
++ in the busybox examples.
++
+ endmenu
+
+diff -Nur busybox-1.00/networking/Makefile.in busybox/networking/Makefile.in
+--- busybox-1.00/networking/Makefile.in 2004-10-08 09:45:43.000000000 +0200
++++ busybox/networking/Makefile.in 2005-06-04 08:20:05.000000000 +0200
+@@ -23,33 +23,36 @@
+ endif
+ srcdir=$(top_srcdir)/networking
+ NETWORKING-y:=
+-NETWORKING-$(CONFIG_ARPING) += arping.o
+-NETWORKING-$(CONFIG_FTPGET) += ftpgetput.o
+-NETWORKING-$(CONFIG_FTPPUT) += ftpgetput.o
+-NETWORKING-$(CONFIG_HOSTNAME) += hostname.o
+-NETWORKING-$(CONFIG_HTTPD) += httpd.o
+-NETWORKING-$(CONFIG_IFCONFIG) += ifconfig.o
+-NETWORKING-$(CONFIG_IFUPDOWN) += ifupdown.o
+-NETWORKING-$(CONFIG_INETD) += inetd.o
+-NETWORKING-$(CONFIG_IP) += ip.o
+-NETWORKING-$(CONFIG_IPCALC) += ipcalc.o
+-NETWORKING-$(CONFIG_IPADDR) += ipaddr.o
+-NETWORKING-$(CONFIG_IPLINK) += iplink.o
+-NETWORKING-$(CONFIG_IPROUTE) += iproute.o
+-NETWORKING-$(CONFIG_IPTUNNEL) += iptunnel.o
+-NETWORKING-$(CONFIG_NAMEIF) += nameif.o
+-NETWORKING-$(CONFIG_NC) += nc.o
+-NETWORKING-$(CONFIG_NETSTAT) += netstat.o
+-NETWORKING-$(CONFIG_NSLOOKUP) += nslookup.o
+-NETWORKING-$(CONFIG_PING) += ping.o
+-NETWORKING-$(CONFIG_PING6) += ping6.o
+-NETWORKING-$(CONFIG_ROUTE) += route.o
+-NETWORKING-$(CONFIG_TELNET) += telnet.o
+-NETWORKING-$(CONFIG_TELNETD) += telnetd.o
+-NETWORKING-$(CONFIG_TFTP) += tftp.o
+-NETWORKING-$(CONFIG_TRACEROUTE) += traceroute.o
+-NETWORKING-$(CONFIG_VCONFIG) += vconfig.o
+-NETWORKING-$(CONFIG_WGET) += wget.o
++NETWORKING-$(CONFIG_ARPING) += arping.o
++NETWORKING-$(CONFIG_ETHER_WAKE) += ether-wake.o
++NETWORKING-$(CONFIG_FAKEIDENTD) += fakeidentd.o
++NETWORKING-$(CONFIG_FTPGET) += ftpgetput.o
++NETWORKING-$(CONFIG_FTPPUT) += ftpgetput.o
++NETWORKING-$(CONFIG_HOSTNAME) += hostname.o
++NETWORKING-$(CONFIG_HTTPD) += httpd.o
++NETWORKING-$(CONFIG_IFCONFIG) += ifconfig.o
++NETWORKING-$(CONFIG_IFUPDOWN) += ifupdown.o
++NETWORKING-$(CONFIG_INETD) += inetd.o
++NETWORKING-$(CONFIG_IP) += ip.o
++NETWORKING-$(CONFIG_IPCALC) += ipcalc.o
++NETWORKING-$(CONFIG_IPADDR) += ipaddr.o
++NETWORKING-$(CONFIG_IPLINK) += iplink.o
++NETWORKING-$(CONFIG_IPROUTE) += iproute.o
++NETWORKING-$(CONFIG_IPTUNNEL) += iptunnel.o
++NETWORKING-$(CONFIG_NAMEIF) += nameif.o
++NETWORKING-$(CONFIG_NC) += nc.o
++NETWORKING-$(CONFIG_NETSTAT) += netstat.o
++NETWORKING-$(CONFIG_NSLOOKUP) += nslookup.o
++NETWORKING-$(CONFIG_PING) += ping.o
++NETWORKING-$(CONFIG_PING6) += ping6.o
++NETWORKING-$(CONFIG_ROUTE) += route.o
++NETWORKING-$(CONFIG_TELNET) += telnet.o
++NETWORKING-$(CONFIG_TELNETD) += telnetd.o
++NETWORKING-$(CONFIG_TFTP) += tftp.o
++NETWORKING-$(CONFIG_TRACEROUTE) += traceroute.o
++NETWORKING-$(CONFIG_VCONFIG) += vconfig.o
++NETWORKING-$(CONFIG_WGET) += wget.o
++NETWORKING-$(CONFIG_ZCIP) += zcip.o
+
+ libraries-y+=$(NETWORKING_DIR)$(NETWORKING_AR)
+
+@@ -57,7 +60,7 @@
+ needcrypt-$(CONFIG_FEATURE_HTTPD_AUTH_MD5) := y
+
+ ifeq ($(needcrypt-y),y)
+- LIBRARIES += -lcrypt
++ LIBRARIES += -lcrypt
+ endif
+
+ $(NETWORKING_DIR)$(NETWORKING_AR): $(patsubst %,$(NETWORKING_DIR)%, $(NETWORKING-y))
+@@ -65,4 +68,3 @@
+
+ $(NETWORKING_DIR)%.o: $(srcdir)/%.c
+ $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $<
+-
+diff -Nur busybox-1.00/networking/arping.c busybox/networking/arping.c
+--- busybox-1.00/networking/arping.c 2003-09-26 02:33:18.000000000 +0200
++++ busybox/networking/arping.c 2005-06-04 08:20:05.000000000 +0200
+@@ -106,7 +106,7 @@
+ return err;
+ }
+
+-void finish(void)
++static void finish(void)
+ {
+ if (!quiet) {
+ printf("Sent %d probes (%d broadcast(s))\n", sent, brd_sent);
+@@ -129,7 +129,7 @@
+ exit(!received);
+ }
+
+-void catcher(void)
++static void catcher(void)
+ {
+ struct timeval tv;
+ static struct timeval start;
+@@ -151,7 +151,7 @@
+ alarm(1);
+ }
+
+-int recv_pack(unsigned char *buf, int len, struct sockaddr_ll *FROM)
++static int recv_pack(unsigned char *buf, int len, struct sockaddr_ll *FROM)
+ {
+ struct timeval tv;
+ struct arphdr *ah = (struct arphdr *) buf;
+diff -Nur busybox-1.00/networking/ether-wake.c busybox/networking/ether-wake.c
+--- busybox-1.00/networking/ether-wake.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/networking/ether-wake.c 2005-06-04 08:20:05.000000000 +0200
+@@ -0,0 +1,300 @@
++/*
++ * ether-wake.c - Send a magic packet to wake up sleeping machines.
++ *
++ * 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.
++ *
++ * Author: Donald Becker, http://www.scyld.com/"; http://www.scyld.com/wakeonlan.html
++ * Busybox port: Christian Volkmann <haveaniceday@online.de>
++ * Used version of ether-wake.c: v1.09 11/12/2003 Donald Becker, http://www.scyld.com/";
++ */
++
++/* full usage according Donald Becker
++ * usage: ether-wake [-i <ifname>] [-p aa:bb:cc:dd[:ee:ff]] 00:11:22:33:44:55\n"
++ *
++ * This program generates and transmits a Wake-On-LAN (WOL)\n"
++ * \"Magic Packet\", used for restarting machines that have been\n"
++ * soft-powered-down (ACPI D3-warm state).\n"
++ * It currently generates the standard AMD Magic Packet format, with\n"
++ * an optional password appended.\n"
++ *
++ * The single required parameter is the Ethernet MAC (station) address\n"
++ * of the machine to wake or a host ID with known NSS 'ethers' entry.\n"
++ * The MAC address may be found with the 'arp' program while the target\n"
++ * machine is awake.\n"
++ *
++ * Options:\n"
++ * -b Send wake-up packet to the broadcast address.\n"
++ * -D Increase the debug level.\n"
++ * -i ifname Use interface IFNAME instead of the default 'eth0'.\n"
++ * -p <pw> Append the four or six byte password PW to the packet.\n"
++ * A password is only required for a few adapter types.\n"
++ * The password may be specified in ethernet hex format\n"
++ * or dotted decimal (Internet address)\n"
++ * -p 00:22:44:66:88:aa\n"
++ * -p 192.168.1.1\n";
++ *
++ *
++ * This program generates and transmits a Wake-On-LAN (WOL) "Magic Packet",
++ * used for restarting machines that have been soft-powered-down
++ * (ACPI D3-warm state). It currently generates the standard AMD Magic Packet
++ * format, with an optional password appended.
++ *
++ * This software may be used and distributed according to the terms
++ * of the GNU Public License, incorporated herein by reference.
++ * Contact the author for use under other terms.
++ *
++ * This source file was originally part of the network tricks package, and
++ * is now distributed to support the Scyld Beowulf system.
++ * Copyright 1999-2003 Donald Becker and Scyld Computing Corporation.
++ *
++ * The author may be reached as becker@scyld, or C/O
++ * Scyld Computing Corporation
++ * 914 Bay Ridge Road, Suite 220
++ * Annapolis MD 21403
++ *
++ * Notes:
++ * On some systems dropping root capability allows the process to be
++ * dumped, traced or debugged.
++ * If someone traces this program, they get control of a raw socket.
++ * Linux handles this safely, but beware when porting this program.
++ *
++ * An alternative to needing 'root' is using a UDP broadcast socket, however
++ * doing so only works with adapters configured for unicast+broadcast Rx
++ * filter. That configuration consumes more power.
++*/
++
++
++#include <unistd.h>
++#include <stdlib.h>
++#include <stdio.h>
++#include <errno.h>
++#include <ctype.h>
++#include <string.h>
++
++#include <sys/socket.h>
++#include <sys/types.h>
++#include <sys/ioctl.h>
++#include <features.h>
++#include <netpacket/packet.h>
++#include <net/ethernet.h>
++#include <netdb.h>
++#include <netinet/ether.h>
++
++#ifdef __linux__
++#include <linux/if.h>
++#endif
++
++#include "busybox.h"
++
++/* Note: PF_INET, SOCK_DGRAM, IPPROTO_UDP would allow SIOCGIFHWADDR to
++ * work as non-root, but we need SOCK_PACKET to specify the Ethernet
++ * destination address.
++ */
++#ifdef PF_PACKET
++# define whereto_t sockaddr_ll
++# define make_socket() socket(PF_PACKET, SOCK_RAW, 0)
++#else
++# define whereto_t sockaddr
++# define make_socket() socket(AF_INET, SOCK_PACKET, SOCK_PACKET)
++#endif
++
++#ifdef DEBUG
++# define bb_debug_msg(fmt, args...) fprintf(stderr, fmt, ## args)
++void bb_debug_dump_packet(unsigned char *outpack, int pktsize)
++{
++ int i;
++ printf("packet dump:\n");
++ for (i = 0; i < pktsize; ++i) {
++ printf("%2.2x ", outpack[i]);
++ if (i % 20 == 19) printf("\n");
++ }
++ printf("\n\n");
++}
++#else
++# define bb_debug_msg(fmt, args...)
++# define bb_debug_dump_packet(outpack, pktsize)
++#endif
++
++static inline void get_dest_addr(const char *arg, struct ether_addr *eaddr);
++static inline int get_fill(unsigned char *pkt, struct ether_addr *eaddr, int broadcast);
++static inline int get_wol_pw(const char *ethoptarg, unsigned char *wol_passwd);
++
++int etherwake_main(int argc, char *argv[])
++{
++ char *ifname = "eth0", *pass = NULL;
++ unsigned long flags;
++ unsigned char wol_passwd[6];
++ int wol_passwd_sz = 0;
++
++ int s; /* Raw socket */
++ int pktsize;
++ unsigned char outpack[1000];
++
++ struct ether_addr eaddr;
++ struct whereto_t whereto; /* who to wake up */
++
++ /* handle misc user options */
++ flags = bb_getopt_ulflags(argc, argv, "bi:p:", &ifname, &pass);
++ if (optind == argc)
++ bb_show_usage();
++ if (pass)
++ wol_passwd_sz = get_wol_pw(pass, wol_passwd);
++
++ /* create the raw socket */
++ s = make_socket();
++ if (s < 0)
++ bb_perror_msg_and_die(bb_msg_can_not_create_raw_socket);
++
++ /* now that we have a raw socket we can drop root */
++ setuid(getuid());
++
++ /* look up the dest mac address */
++ get_dest_addr(argv[optind], &eaddr);
++
++ /* fill out the header of the packet */
++ pktsize = get_fill(outpack, &eaddr, flags /*& 1 [OPT_BROADCAST]*/);
++
++ bb_debug_dump_packet(outpack, pktsize);
++
++ /* Fill in the source address, if possible. */
++#ifdef __linux__
++ {
++ struct ifreq if_hwaddr;
++
++ strcpy(if_hwaddr.ifr_name, ifname);
++ if (ioctl(s, SIOCGIFHWADDR, &if_hwaddr) < 0)
++ bb_perror_msg_and_die("SIOCGIFHWADDR on %s failed", ifname);
++
++ memcpy(outpack+6, if_hwaddr.ifr_hwaddr.sa_data, 6);
++
++# ifdef DEBUG
++ {
++ unsigned char *hwaddr = if_hwaddr.ifr_hwaddr.sa_data;
++ printf("The hardware address (SIOCGIFHWADDR) of %s is type %d "
++ "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x\n\n", ifname,
++ if_hwaddr.ifr_hwaddr.sa_family, hwaddr[0], hwaddr[1],
++ hwaddr[2], hwaddr[3], hwaddr[4], hwaddr[5]);
++ }
++# endif
++ }
++#endif /* __linux__ */
++
++ bb_debug_dump_packet(outpack, pktsize);
++
++ /* append the password if specified */
++ if (wol_passwd_sz > 0) {
++ memcpy(outpack+pktsize, wol_passwd, wol_passwd_sz);
++ pktsize += wol_passwd_sz;
++ }
++
++ bb_debug_dump_packet(outpack, pktsize);
++
++ /* This is necessary for broadcasts to work */
++ if (flags /*& 1 [OPT_BROADCAST]*/) {
++ int one = 1;
++ if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, (void *)&one, sizeof(one)) < 0)
++ bb_perror_msg("SO_BROADCAST");
++ }
++
++#if defined(PF_PACKET)
++ {
++ struct ifreq ifr;
++ strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
++ if (ioctl(s, SIOCGIFINDEX, &ifr) == -1)
++ bb_perror_msg_and_die("SIOCGIFINDEX");
++ memset(&whereto, 0, sizeof(whereto));
++ whereto.sll_family = AF_PACKET;
++ whereto.sll_ifindex = ifr.ifr_ifindex;
++ /* The manual page incorrectly claims the address must be filled.
++ We do so because the code may change to match the docs. */
++ whereto.sll_halen = ETH_ALEN;
++ memcpy(whereto.sll_addr, outpack, ETH_ALEN);
++ }
++#else
++ whereto.sa_family = 0;
++ strcpy(whereto.sa_data, ifname);
++#endif
++
++ if (sendto(s, outpack, pktsize, 0, (struct sockaddr *)&whereto, sizeof(whereto)) < 0)
++ bb_perror_msg(bb_msg_write_error);
++
++ close(s);
++
++ return EXIT_SUCCESS;
++}
++
++/* Convert the host ID string to a MAC address.
++ * The string may be a:
++ * Host name
++ * IP address string
++ * MAC address string
++*/
++static inline void get_dest_addr(const char *hostid, struct ether_addr *eaddr)
++{
++ struct ether_addr *eap;
++
++ eap = ether_aton(hostid);
++ if (eap) {
++ *eaddr = *eap;
++ bb_debug_msg("The target station address is %s\n\n", ether_ntoa(eaddr));
++ } else if (ether_hostton(hostid, eaddr) == 0) {
++ bb_debug_msg("Station address for hostname %s is %s\n\n", hostid, ether_ntoa(eaddr));
++ } else
++ bb_show_usage();
++}
++
++static inline int get_fill(unsigned char *pkt, struct ether_addr *eaddr, int broadcast)
++{
++ int offset, i;
++ unsigned char *station_addr = eaddr->ether_addr_octet;
++
++ if (broadcast)
++ memset(pkt+0, 0xff, 6);
++ else
++ memcpy(pkt, station_addr, 6);
++ memcpy(pkt+6, station_addr, 6);
++ pkt[12] = 0x08; /* Or 0x0806 for ARP, 0x8035 for RARP */
++ pkt[13] = 0x42;
++ offset = 14;
++
++ memset(pkt+offset, 0xff, 6);
++ offset += 6;
++
++ for (i = 0; i < 16; ++i) {
++ memcpy(pkt+offset, station_addr, 6);
++ offset += 6;
++ }
++
++ return offset;
++}
++
++static inline int get_wol_pw(const char *ethoptarg, unsigned char *wol_passwd)
++{
++ int passwd[6];
++ int byte_cnt, i;
++
++ /* handle MAC format */
++ byte_cnt = sscanf(ethoptarg, "%2x:%2x:%2x:%2x:%2x:%2x",
++ &passwd[0], &passwd[1], &passwd[2],
++ &passwd[3], &passwd[4], &passwd[5]);
++ /* handle IP format */
++ if (byte_cnt < 4)
++ byte_cnt = sscanf(ethoptarg, "%d.%d.%d.%d",
++ &passwd[0], &passwd[1], &passwd[2], &passwd[3]);
++ if (byte_cnt < 4) {
++ bb_error_msg("Unable to read the Wake-On-LAN pass");
++ return 0;
++ }
++
++ for (i = 0; i < byte_cnt; ++i)
++ wol_passwd[i] = passwd[i];
++
++ bb_debug_msg("password: %2.2x %2.2x %2.2x %2.2x (%d)\n\n",
++ wol_passwd[0], wol_passwd[1], wol_passwd[2], wol_passwd[3],
++ byte_cnt);
++
++ return byte_cnt;
++}
+diff -Nur busybox-1.00/networking/fakeidentd.c busybox/networking/fakeidentd.c
+--- busybox-1.00/networking/fakeidentd.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/networking/fakeidentd.c 2005-06-04 08:20:05.000000000 +0200
+@@ -0,0 +1,432 @@
++/* vi: set sw=4 ts=4: */
++/*
++ * A fake identd server
++ *
++ * Adapted to busybox by Thomas Lundquist <thomasez@zelow.no>
++ * Original Author: Tomi Ollila <too@iki.fi>
++ * http://www.guru-group.fi/~too/sw/
++ *
++ * 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 <unistd.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <stdarg.h>
++#include <string.h>
++#include <fcntl.h>
++#include <signal.h>
++#include <sys/syslog.h>
++
++#include <pwd.h>
++#include <netdb.h>
++
++#include <sys/syslog.h>
++#include <sys/types.h>
++#include <sys/time.h>
++#include <time.h>
++#include <sys/socket.h>
++#include <netinet/in.h>
++#include <errno.h>
++#include <arpa/inet.h>
++#include <sys/uio.h>
++
++#include "busybox.h"
++
++#define IDENT_PORT 113
++#define MAXCONNS 20
++#define MAXIDLETIME 45
++
++static const char ident_substr[] = " : USERID : UNIX : ";
++static const int ident_substr_len = sizeof(ident_substr) - 1;
++#define PIDFILE "/var/run/identd.pid"
++
++/*
++ * We have to track the 'first connection socket' so that we
++ * don't go around closing file descriptors for non-clients.
++ *
++ * descriptor setup normally
++ * 0 = server socket
++ * 1 = syslog fd (hopefully -- otherwise this won't work)
++ * 2 = connection socket after detached from tty. standard error before that
++ * 3 - 2 + MAXCONNS = rest connection sockets
++ *
++ * To try to make sure that syslog fd is what is "requested", the that fd
++ * is closed before openlog() call. It can only severely fail if fd 0
++ * is initially closed.
++ */
++#define FCS 2
++
++/*
++ * FD of the connection is always the index of the connection structure
++ * in `conns' array + FCS
++ */
++static struct {
++ char buf[20];
++ int len;
++ time_t lasttime;
++} conns[MAXCONNS];
++
++/* When using global variables, bind those at least to a structure. */
++static struct {
++ const char *identuser;
++ fd_set readfds;
++ int conncnt;
++} G;
++
++/*
++ * Prototypes
++ */
++static void reply(int s, char *buf);
++static void replyError(int s, char *buf);
++
++static const char *nobodystr = "nobody"; /* this needs to be declared like this */
++static char *bind_ip_address = "0.0.0.0";
++
++static inline void movefd(int from, int to)
++{
++ if (from != to) {
++ dup2(from, to);
++ close(from);
++ }
++}
++
++static void inetbind(void)
++{
++ int s, port;
++ struct sockaddr_in addr;
++ int len = sizeof(addr);
++ int one = 1;
++ struct servent *se;
++
++ if ((se = getservbyname("identd", "tcp")) == NULL)
++ port = IDENT_PORT;
++ else
++ port = se->s_port;
++
++ if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0)
++ bb_perror_msg_and_die("Cannot create server socket");
++
++ setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
++
++ memset(&addr, 0, sizeof(addr));
++ addr.sin_addr.s_addr = inet_addr(bind_ip_address);
++ addr.sin_family = AF_INET;
++ addr.sin_port = htons(port);
++
++ if (bind(s, (struct sockaddr *)&addr, len) < 0)
++ bb_perror_msg_and_die("Cannot bind() port %i", IDENT_PORT);
++
++ if (listen(s, 5) < 0)
++ bb_perror_msg_and_die("Cannot listen() on port %i", IDENT_PORT);
++
++ movefd(s, 0);
++}
++
++static void delpidfile(void)
++{
++ /*
++ * Usually nobody has no write/delete access to directory /var/run/
++ * therefore if file cannot be deleted, it is truncated
++ */
++ if (unlink(PIDFILE) < 0)
++ close(open(PIDFILE, O_WRONLY|O_CREAT|O_TRUNC, 0644));
++}
++
++static void handlexitsigs(int signum)
++{
++ delpidfile();
++ exit(0);
++}
++
++/* May succeed. If not, won't care. */
++static inline void writepid(uid_t nobody, uid_t nogrp)
++{
++ char buf[24];
++ int fd = open(PIDFILE, O_WRONLY|O_CREAT|O_TRUNC, 0664);
++
++ if (fd < 0)
++ return;
++
++ snprintf(buf, 23, "%d\n", getpid());
++ write(fd, buf, strlen(buf));
++ fchown(fd, nobody, nogrp);
++ close(fd);
++
++ /* should this handle ILL, ... (see signal(7)) */
++ signal(SIGTERM, handlexitsigs);
++ signal(SIGINT, handlexitsigs);
++ signal(SIGQUIT, handlexitsigs);
++}
++
++/* return 0 as parent, 1 as child */
++static int godaemon(void)
++{
++ uid_t nobody, nogrp;
++ struct passwd *pw;
++
++ switch (fork()) {
++ case -1:
++ bb_perror_msg_and_die("Could not fork");
++
++ case 0:
++ pw = getpwnam(nobodystr);
++ if (pw == NULL)
++ bb_error_msg_and_die("Cannot find uid/gid of user '%s'", nobodystr);
++ nobody = pw->pw_uid;
++ nogrp = pw->pw_gid;
++ writepid(nobody, nogrp);
++
++ close(0);
++ inetbind();
++ if (setgid(nogrp)) bb_error_msg_and_die("Could not setgid()");
++ if (setegid(nogrp)) bb_error_msg_and_die("Could not setegid()");
++ if (setuid(nobody)) bb_error_msg_and_die("Could not setuid()");
++ if (seteuid(nobody)) bb_error_msg_and_die("Could not seteuid()");
++ close(1);
++ close(2);
++
++ signal(SIGHUP, SIG_IGN);
++ signal(SIGPIPE, SIG_IGN); /* connection closed when writing (raises ???) */
++
++ setsid();
++
++ openlog(bb_applet_name, 0, LOG_DAEMON);
++ return 1;
++ }
++
++ return 0;
++}
++
++static void deleteConn(int s)
++{
++ int i = s - FCS;
++
++ close(s);
++
++ G.conncnt--;
++
++ /*
++ * Most of the time there is 0 connections. Most often that there
++ * is connections, there is just one connection. When this one connection
++ * closes, i == G.conncnt = 0 -> no copying.
++ * When there is more than one connection, the oldest connections closes
++ * earlier on average. When this happens, the code below starts copying
++ * the connection structure w/ highest index to the place which which is
++ * just deleted. This means that the connection structures are no longer
++ * in chronological order. I'd quess this means that when there is more
++ * than 1 connection, on average every other connection structure needs
++ * to be copied over the time all these connections are deleted.
++ */
++ if (i != G.conncnt) {
++ memcpy(&conns[i], &conns[G.conncnt], sizeof(conns[0]));
++ movefd(G.conncnt + FCS, s);
++ }
++
++ FD_CLR(G.conncnt + FCS, &G.readfds);
++}
++
++static int closeOldest(void)
++{
++ time_t min = conns[0].lasttime;
++ int idx = 0;
++ int i;
++
++ for (i = 1; i < MAXCONNS; i++)
++ if (conns[i].lasttime < min)
++ idx = i;
++
++ replyError(idx + FCS, "X-SERVER-TOO-BUSY");
++ close(idx + FCS);
++
++ return idx;
++}
++
++static int checkInput(char *buf, int len, int l)
++{
++ int i;
++ for (i = len; i < len + l; ++i)
++ if (buf[i] == '\n')
++ return 1;
++ return 0;
++}
++
++int fakeidentd_main(int argc, char **argv)
++{
++ memset(conns, 0, sizeof(conns));
++ memset(&G, 0, sizeof(G));
++ FD_ZERO(&G.readfds);
++ FD_SET(0, &G.readfds);
++
++ /* handle -b <ip> parameter */
++ bb_getopt_ulflags(argc, argv, "b:", &bind_ip_address);
++ /* handle optional REPLY STRING */
++ if (optind < argc)
++ G.identuser = argv[optind];
++ else
++ G.identuser = nobodystr;
++
++ /* daemonize and have the parent return */
++ if (godaemon() == 0)
++ return 0;
++
++ /* main loop where we process all events and never exit */
++ while (1) {
++ fd_set rfds = G.readfds;
++ struct timeval tv = { 15, 0 };
++ int i;
++ int tim = time(NULL);
++
++ select(G.conncnt + FCS, &rfds, NULL, NULL, G.conncnt? &tv: NULL);
++
++ for (i = G.conncnt - 1; i >= 0; i--) {
++ int s = i + FCS;
++
++ if (FD_ISSET(s, &rfds)) {
++ char *buf = conns[i].buf;
++ unsigned int len = conns[i].len;
++ unsigned int l;
++
++ if ((l = read(s, buf + len, sizeof(conns[0].buf) - len)) > 0) {
++ if (checkInput(buf, len, l)) {
++ reply(s, buf);
++ goto deleteconn;
++ } else if (len + l >= sizeof(conns[0].buf)) {
++ replyError(s, "X-INVALID-REQUEST");
++ goto deleteconn;
++ } else {
++ conns[i].len += l;
++ }
++ } else {
++ goto deleteconn;
++ }
++
++ conns[i].lasttime = tim;
++ continue;
++
++deleteconn:
++ deleteConn(s);
++ } else {
++ /* implement as time_after() in linux kernel sources ... */
++ if (conns[i].lasttime + MAXIDLETIME <= tim) {
++ replyError(s, "X-TIMEOUT");
++ deleteConn(s);
++ }
++ }
++ }
++
++ if (FD_ISSET(0, &rfds)) {
++ int s = accept(0, NULL, 0);
++
++ if (s < 0) {
++ if (errno != EINTR) /* EINTR */
++ syslog(LOG_ERR, "accept: %s", strerror(errno));
++ } else {
++ if (G.conncnt == MAXCONNS)
++ i = closeOldest();
++ else
++ i = G.conncnt++;
++
++ movefd(s, i + FCS); /* move if not already there */
++ FD_SET(i + FCS, &G.readfds);
++
++ conns[i].len = 0;
++ conns[i].lasttime = time(NULL);
++ }
++ }
++ } /* end of while(1) */
++
++ return 0;
++}
++
++static int parseAddrs(char *ptr, char **myaddr, char **heraddr);
++static void reply(int s, char *buf)
++{
++ char *myaddr, *heraddr;
++
++ myaddr = heraddr = NULL;
++
++ if (parseAddrs(buf, &myaddr, &heraddr))
++ replyError(s, "X-INVALID-REQUEST");
++ else {
++ struct iovec iv[6];
++ iv[0].iov_base = myaddr; iv[0].iov_len = strlen(myaddr);
++ iv[1].iov_base = ", "; iv[1].iov_len = 2;
++ iv[2].iov_base = heraddr; iv[2].iov_len = strlen(heraddr);
++ iv[3].iov_base = (void *)ident_substr; iv[3].iov_len = ident_substr_len;
++ iv[4].iov_base = (void *)G.identuser; iv[4].iov_len = strlen(G.identuser);
++ iv[5].iov_base = "\r\n"; iv[5].iov_len = 2;
++ writev(s, iv, 6);
++ }
++}
++
++static void replyError(int s, char *buf)
++{
++ struct iovec iv[3];
++ iv[0].iov_base = "0, 0 : ERROR : "; iv[0].iov_len = 15;
++ iv[1].iov_base = buf; iv[1].iov_len = strlen(buf);
++ iv[2].iov_base = "\r\n"; iv[2].iov_len = 2;
++ writev(s, iv, 3);
++}
++
++static int chmatch(char c, char *chars)
++{
++ for (; *chars; chars++)
++ if (c == *chars)
++ return 1;
++ return 0;
++}
++
++static int skipchars(char **p, char *chars)
++{
++ while (chmatch(**p, chars))
++ (*p)++;
++ if (**p == '\r' || **p == '\n')
++ return 0;
++ return 1;
++}
++
++static int parseAddrs(char *ptr, char **myaddr, char **heraddr)
++{
++ /* parse <port-on-server> , <port-on-client> */
++
++ if (!skipchars(&ptr, " \t"))
++ return -1;
++
++ *myaddr = ptr;
++
++ if (!skipchars(&ptr, "1234567890"))
++ return -1;
++
++ if (!chmatch(*ptr, " \t,"))
++ return -1;
++
++ *ptr++ = '\0';
++
++ if (!skipchars(&ptr, " \t,") )
++ return -1;
++
++ *heraddr = ptr;
++
++ skipchars(&ptr, "1234567890");
++
++ if (!chmatch(*ptr, " \n\r"))
++ return -1;
++
++ *ptr = '\0';
++
++ return 0;
++}
+diff -Nur busybox-1.00/networking/hostname.c busybox/networking/hostname.c
+--- busybox-1.00/networking/hostname.c 2003-07-14 23:21:01.000000000 +0200
++++ busybox/networking/hostname.c 2005-06-04 08:20:05.000000000 +0200
+@@ -1,6 +1,6 @@
+ /* vi: set sw=4 ts=4: */
+ /*
+- * $Id$
++ * $Id$
+ * Mini hostname implementation for busybox
+ *
+ * Copyright (C) 1999 by Randolph Chung <tausq@debian.org>
+diff -Nur busybox-1.00/networking/ifconfig.c busybox/networking/ifconfig.c
+--- busybox-1.00/networking/ifconfig.c 2004-03-31 13:30:08.000000000 +0200
++++ busybox/networking/ifconfig.c 2005-06-04 08:20:05.000000000 +0200
+@@ -15,7 +15,7 @@
+ * Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+- * $Id$
++ * $Id$
+ *
+ */
+
+@@ -46,8 +46,8 @@
+ #include <netpacket/packet.h>
+ #include <net/ethernet.h>
+ #else
+-#include <asm/types.h>
+-#include <linux/if_ether.h>
++#include <sys/types.h>
++#include <netinet/if_ether.h>
+ #endif
+ #include "inet_common.h"
+ #include "busybox.h"
+@@ -177,7 +177,7 @@
+
+ struct arg1opt {
+ const char *name;
+- unsigned short selector;
++ int selector;
+ unsigned short ifr_offset;
+ };
+
+diff -Nur busybox-1.00/networking/ifupdown.c busybox/networking/ifupdown.c
+--- busybox-1.00/networking/ifupdown.c 2004-09-14 19:24:58.000000000 +0200
++++ busybox/networking/ifupdown.c 2005-06-04 08:20:05.000000000 +0200
+@@ -150,7 +150,7 @@
+
+ static char no_act = 0;
+ static char verbose = 0;
+-static char **environ = NULL;
++static char **__myenviron = NULL;
+
+ #ifdef CONFIG_FEATURE_IFUPDOWN_IP
+
+@@ -460,7 +460,7 @@
+ { "loopback", loopback_up6, loopback_down6, },
+ };
+
+-struct address_family_t addr_inet6 = {
++static struct address_family_t addr_inet6 = {
+ "inet6",
+ sizeof(methods6) / sizeof(struct method_t),
+ methods6
+@@ -609,7 +609,7 @@
+ { "loopback", loopback_up, loopback_down, },
+ };
+
+-struct address_family_t addr_inet =
++static struct address_family_t addr_inet =
+ {
+ "inet",
+ sizeof(methods) / sizeof(struct method_t),
+@@ -961,16 +961,16 @@
+ const int n_env_entries = iface->n_options + 5;
+ char **ppch;
+
+- if (environ != NULL) {
+- for (ppch = environ; *ppch; ppch++) {
++ if (__myenviron != NULL) {
++ for (ppch = __myenviron; *ppch; ppch++) {
+ free(*ppch);
+ *ppch = NULL;
+ }
+- free(environ);
+- environ = NULL;
++ free(__myenviron);
++ __myenviron = NULL;
+ }
+- environ = xmalloc(sizeof(char *) * (n_env_entries + 1 /* for final NULL */ ));
+- environend = environ;
++ __myenviron = xmalloc(sizeof(char *) * (n_env_entries + 1 /* for final NULL */ ));
++ environend = __myenviron;
+ *environend = NULL;
+
+ for (i = 0; i < iface->n_options; i++) {
+@@ -1010,7 +1010,7 @@
+ case -1: /* failure */
+ return 0;
+ case 0: /* child */
+- execle(DEFAULT_SHELL, DEFAULT_SHELL, "-c", str, NULL, environ);
++ execle(DEFAULT_SHELL, DEFAULT_SHELL, "-c", str, NULL, __myenviron);
+ exit(127);
+ }
+ waitpid(child, &status, 0);
+diff -Nur busybox-1.00/networking/inetd.c busybox/networking/inetd.c
+--- busybox-1.00/networking/inetd.c 2004-06-22 10:40:54.000000000 +0200
++++ busybox/networking/inetd.c 2005-06-04 08:20:05.000000000 +0200
+@@ -560,7 +560,7 @@
+ if (sep != 0) {
+ int i;
+
+-#define SWAP(type, a, b) {type c=(type)a; (type)a=(type)b; (type)b=(type)c;}
++#define SWAP(type, a, b) {type c=(type)(a); (a)=(type)(b); (b)=(type)c;}
+
+ sigprocmask(SIG_BLOCK, &emptymask, &oldmask);
+ /*
+diff -Nur busybox-1.00/networking/libiproute/ipaddress.c busybox/networking/libiproute/ipaddress.c
+--- busybox-1.00/networking/libiproute/ipaddress.c 2004-03-15 09:28:56.000000000 +0100
++++ busybox/networking/libiproute/ipaddress.c 2005-06-04 08:20:04.000000000 +0200
+@@ -48,7 +48,7 @@
+ struct rtnl_handle *rth;
+ } filter;
+
+-void print_link_flags(FILE *fp, unsigned flags, unsigned mdown)
++static void print_link_flags(FILE *fp, unsigned flags, unsigned mdown)
+ {
+ fprintf(fp, "<");
+ flags &= ~IFF_RUNNING;
+diff -Nur busybox-1.00/networking/libiproute/iptunnel.c busybox/networking/libiproute/iptunnel.c
+--- busybox-1.00/networking/libiproute/iptunnel.c 2004-04-26 21:32:49.000000000 +0200
++++ busybox/networking/libiproute/iptunnel.c 2005-06-04 08:20:04.000000000 +0200
+@@ -361,7 +361,7 @@
+ return -1;
+ }
+
+-int do_del(int argc, char **argv)
++static int do_del(int argc, char **argv)
+ {
+ struct ip_tunnel_parm p;
+
+@@ -381,7 +381,7 @@
+ return -1;
+ }
+
+-void print_tunnel(struct ip_tunnel_parm *p)
++static void print_tunnel(struct ip_tunnel_parm *p)
+ {
+ char s1[256];
+ char s2[256];
+diff -Nur busybox-1.00/networking/libiproute/ll_proto.c busybox/networking/libiproute/ll_proto.c
+--- busybox-1.00/networking/libiproute/ll_proto.c 2004-03-15 09:28:56.000000000 +0100
++++ busybox/networking/libiproute/ll_proto.c 2005-06-04 08:20:04.000000000 +0200
+@@ -90,7 +90,7 @@
+ #undef __PF
+
+
+-char * ll_proto_n2a(unsigned short id, char *buf, int len)
++const char * ll_proto_n2a(unsigned short id, char *buf, int len)
+ {
+ int i;
+
+diff -Nur busybox-1.00/networking/libiproute/ll_types.c busybox/networking/libiproute/ll_types.c
+--- busybox-1.00/networking/libiproute/ll_types.c 2003-06-20 11:05:00.000000000 +0200
++++ busybox/networking/libiproute/ll_types.c 2005-06-04 08:20:04.000000000 +0200
+@@ -13,7 +13,7 @@
+
+ #include <linux/if_arp.h>
+
+-char * ll_type_n2a(int type, char *buf, int len)
++const char * ll_type_n2a(int type, char *buf, int len)
+ {
+ #define __PF(f,n) { ARPHRD_##f, #n },
+ static struct {
+diff -Nur busybox-1.00/networking/libiproute/rt_names.c busybox/networking/libiproute/rt_names.c
+--- busybox-1.00/networking/libiproute/rt_names.c 2002-12-16 08:37:21.000000000 +0100
++++ busybox/networking/libiproute/rt_names.c 2005-06-04 08:20:04.000000000 +0200
+@@ -76,7 +76,7 @@
+ rtnl_rtprot_tab, 256);
+ }
+
+-char * rtnl_rtprot_n2a(int id, char *buf, int len)
++const char * rtnl_rtprot_n2a(int id, char *buf, int len)
+ {
+ if (id<0 || id>=256) {
+ snprintf(buf, len, "%d", id);
+@@ -143,7 +143,7 @@
+ rtnl_rtscope_tab, 256);
+ }
+
+-char * rtnl_rtscope_n2a(int id, char *buf, int len)
++const char * rtnl_rtscope_n2a(int id, char *buf, int len)
+ {
+ if (id<0 || id>=256) {
+ snprintf(buf, len, "%d", id);
+@@ -206,7 +206,7 @@
+ rtnl_rtrealm_tab, 256);
+ }
+
+-char * rtnl_rtrealm_n2a(int id, char *buf, int len)
++const char * rtnl_rtrealm_n2a(int id, char *buf, int len)
+ {
+ if (id<0 || id>=256) {
+ snprintf(buf, len, "%d", id);
+@@ -272,7 +272,7 @@
+ rtnl_rttable_tab, 256);
+ }
+
+-char * rtnl_rttable_n2a(int id, char *buf, int len)
++const char * rtnl_rttable_n2a(int id, char *buf, int len)
+ {
+ if (id<0 || id>=256) {
+ snprintf(buf, len, "%d", id);
+@@ -334,7 +334,7 @@
+ rtnl_rtdsfield_tab, 256);
+ }
+
+-char * rtnl_dsfield_n2a(int id, char *buf, int len)
++const char * rtnl_dsfield_n2a(int id, char *buf, int len)
+ {
+ if (id<0 || id>=256) {
+ snprintf(buf, len, "%d", id);
+diff -Nur busybox-1.00/networking/libiproute/utils.c busybox/networking/libiproute/utils.c
+--- busybox-1.00/networking/libiproute/utils.c 2003-03-19 10:12:42.000000000 +0100
++++ busybox/networking/libiproute/utils.c 2005-06-04 08:20:04.000000000 +0200
+@@ -238,7 +238,7 @@
+ return addr.data[0];
+ }
+
+-void incomplete_command()
++void incomplete_command(void)
+ {
+ bb_error_msg("Command line is not complete. Try option \"help\"");
+ exit(-1);
+diff -Nur busybox-1.00/networking/nameif.c busybox/networking/nameif.c
+--- busybox-1.00/networking/nameif.c 2004-04-25 07:11:17.000000000 +0200
++++ busybox/networking/nameif.c 2005-06-04 08:20:05.000000000 +0200
+@@ -86,7 +86,7 @@
+ }
+
+ /* Check ascii str_macaddr, convert and copy to *mac */
+-struct ether_addr *cc_macaddr(char *str_macaddr)
++static struct ether_addr *cc_macaddr(char *str_macaddr)
+ {
+ struct ether_addr *lmac, *mac;
+
+diff -Nur busybox-1.00/networking/nc.c busybox/networking/nc.c
+--- busybox-1.00/networking/nc.c 2004-03-27 11:02:43.000000000 +0100
++++ busybox/networking/nc.c 2005-06-04 08:20:05.000000000 +0200
+@@ -4,7 +4,7 @@
+
+ 0.0.1 6K It works.
+ 0.0.2 5K Smaller and you can also check the exit condition if you wish.
+- 0.0.3 Uses select()
++ 0.0.3 Uses select()
+
+ 19980918 Busy Boxed! Dave Cinege
+ 19990512 Uses Select. Charles P. Wright
+@@ -23,13 +23,13 @@
+ You 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 <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
+ #include <unistd.h>
++#include <signal.h>
+
+ #include <sys/types.h>
+ #include <sys/socket.h>
+@@ -40,14 +40,17 @@
+ #include <sys/ioctl.h>
+ #include "busybox.h"
+
+-#define GAPING_SECURITY_HOLE
++static void timeout(int signum)
++{
++ bb_error_msg_and_die("Timed out");
++}
+
+ int nc_main(int argc, char **argv)
+ {
+- int do_listen = 0, lport = 0, delay = 0, tmpfd, opt, sfd, x;
++ int do_listen = 0, lport = 0, delay = 0, wsecs = 0, tmpfd, opt, sfd, x;
+ char buf[BUFSIZ];
+-#ifdef GAPING_SECURITY_HOLE
+- char * pr00gie = NULL;
++#ifdef CONFIG_NC_GAPING_SECURITY_HOLE
++ char *pr00gie = NULL;
+ #endif
+
+ struct sockaddr_in address;
+@@ -55,7 +58,7 @@
+
+ fd_set readfds, testfds;
+
+- while ((opt = getopt(argc, argv, "lp:i:e:")) > 0) {
++ while ((opt = getopt(argc, argv, "lp:i:e:w:")) > 0) {
+ switch (opt) {
+ case 'l':
+ do_listen++;
+@@ -66,23 +69,25 @@
+ case 'i':
+ delay = atoi(optarg);
+ break;
+-#ifdef GAPING_SECURITY_HOLE
++#ifdef CONFIG_NC_GAPING_SECURITY_HOLE
+ case 'e':
+ pr00gie = optarg;
+ break;
+ #endif
++ case 'w':
++ wsecs = atoi(optarg);
++ break;
+ default:
+ bb_show_usage();
+ }
+ }
+
+-#ifdef GAPING_SECURITY_HOLE
++#ifdef CONFIG_NC_GAPING_SECURITY_HOLE
+ if (pr00gie) {
+ /* won't need stdin */
+- close (STDIN_FILENO);
++ close(STDIN_FILENO);
+ }
+-#endif /* GAPING_SECURITY_HOLE */
+-
++#endif /* CONFIG_NC_GAPING_SECURITY_HOLE */
+
+ if ((do_listen && optind != argc) || (!do_listen && optind + 2 != argc))
+ bb_show_usage();
+@@ -90,10 +95,15 @@
+ if ((sfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
+ bb_perror_msg_and_die("socket");
+ x = 1;
+- if (setsockopt (sfd, SOL_SOCKET, SO_REUSEADDR, &x, sizeof (x)) == -1)
+- bb_perror_msg_and_die ("reuseaddr failed");
++ if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &x, sizeof (x)) == -1)
++ bb_perror_msg_and_die("reuseaddr");
+ address.sin_family = AF_INET;
+
++ if (wsecs) {
++ signal(SIGALRM, timeout);
++ alarm(wsecs);
++ }
++
+ if (lport != 0) {
+ memset(&address.sin_addr, 0, sizeof(address.sin_addr));
+ address.sin_port = lport;
+@@ -123,19 +133,23 @@
+ bb_perror_msg_and_die("connect");
+ }
+
+-#ifdef GAPING_SECURITY_HOLE
++ if (wsecs) {
++ alarm(0);
++ signal(SIGALRM, SIG_DFL);
++ }
++
++#ifdef CONFIG_NC_GAPING_SECURITY_HOLE
+ /* -e given? */
+ if (pr00gie) {
+ dup2(sfd, 0);
+ close(sfd);
+- dup2 (0, 1);
+- dup2 (0, 2);
+- execl (pr00gie, pr00gie, NULL);
++ dup2(0, 1);
++ dup2(0, 2);
++ execl(pr00gie, pr00gie, NULL);
+ /* Don't print stuff or it will go over the wire.... */
+ _exit(-1);
+ }
+-#endif /* GAPING_SECURITY_HOLE */
+-
++#endif /* CONFIG_NC_GAPING_SECURITY_HOLE */
+
+ FD_ZERO(&readfds);
+ FD_SET(sfd, &readfds);
+@@ -154,7 +168,7 @@
+ for (fd = 0; fd < FD_SETSIZE; fd++) {
+ if (FD_ISSET(fd, &testfds)) {
+ if ((nread = safe_read(fd, buf, sizeof(buf))) < 0)
+- bb_perror_msg_and_die("read");
++ bb_perror_msg_and_die(bb_msg_read_error);
+
+ if (fd == sfd) {
+ if (nread == 0)
+@@ -167,7 +181,7 @@
+ }
+
+ if (bb_full_write(ofd, buf, nread) < 0)
+- bb_perror_msg_and_die("write");
++ bb_perror_msg_and_die(bb_msg_write_error);
+ if (delay > 0) {
+ sleep(delay);
+ }
+diff -Nur busybox-1.00/networking/nslookup.c busybox/networking/nslookup.c
+--- busybox-1.00/networking/nslookup.c 2004-10-13 09:25:01.000000000 +0200
++++ busybox/networking/nslookup.c 2005-06-04 08:20:05.000000000 +0200
+@@ -203,4 +203,4 @@
+ return EXIT_FAILURE;
+ }
+
+-/* $Id$ */
++/* $Id$ */
+diff -Nur busybox-1.00/networking/ping.c busybox/networking/ping.c
+--- busybox-1.00/networking/ping.c 2004-03-15 09:28:48.000000000 +0100
++++ busybox/networking/ping.c 2005-06-04 08:20:05.000000000 +0200
+@@ -1,6 +1,6 @@
+ /* vi: set sw=4 ts=4: */
+ /*
+- * $Id$
++ * $Id$
+ * Mini ping implementation for busybox
+ *
+ * Copyright (C) 1999 by Randolph Chung <tausq@debian.org>
+@@ -178,7 +178,10 @@
+ static unsigned long tmin = ULONG_MAX, tmax, tsum;
+ static char rcvd_tbl[MAX_DUP_CHK / 8];
+
+-struct hostent *hostent;
++#ifndef CONFIG_FEATURE_FANCY_PING6
++static
++#endif
++ struct hostent *hostent;
+
+ static void sendping(int);
+ static void pingstats(int);
+diff -Nur busybox-1.00/networking/ping6.c busybox/networking/ping6.c
+--- busybox-1.00/networking/ping6.c 2004-03-15 09:28:48.000000000 +0100
++++ busybox/networking/ping6.c 2005-06-04 08:20:05.000000000 +0200
+@@ -1,6 +1,6 @@
+ /* vi: set sw=4 ts=4: */
+ /*
+- * $Id$
++ * $Id$
+ * Mini ping implementation for busybox
+ *
+ * Copyright (C) 1999 by Randolph Chung <tausq@debian.org>
+diff -Nur busybox-1.00/networking/route.c busybox/networking/route.c
+--- busybox-1.00/networking/route.c 2004-03-20 00:27:08.000000000 +0100
++++ busybox/networking/route.c 2005-06-04 08:20:05.000000000 +0200
+@@ -15,7 +15,7 @@
+ * Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+- * $Id$
++ * $Id$
+ *
+ * displayroute() code added by Vladimir N. Oleynik <dzo@simtreas.ru>
+ * adjustments by Larry Doolittle <LRDoolittle@lbl.gov>
+@@ -485,6 +485,7 @@
+ }
+ }
+
++/* also used in netstat */
+ void displayroutes(int noresolve, int netstatfmt)
+ {
+ char devname[64], flags[16], sdest[16], sgw[16];
+diff -Nur busybox-1.00/networking/telnetd.c busybox/networking/telnetd.c
+--- busybox-1.00/networking/telnetd.c 2004-09-14 19:24:58.000000000 +0200
++++ busybox/networking/telnetd.c 2005-06-04 08:20:05.000000000 +0200
+@@ -1,4 +1,4 @@
+-/* $Id$
++/* $Id$
+ *
+ * Simple telnet server
+ * Bjorn Wesen, Axis Communications AB (bjornw@axis.com)
+@@ -49,6 +49,15 @@
+
+ #define BUFSIZE 4000
+
++#ifdef CONFIG_FEATURE_IPV6
++#define SOCKET_TYPE AF_INET6
++typedef struct sockaddr_in6 sockaddr_type;
++#else
++#define SOCKET_TYPE AF_INET
++typedef struct sockaddr_in sockaddr_type;
++#endif
++
++
+ #ifdef CONFIG_LOGIN
+ static const char *loginpath = "/bin/login";
+ #else
+@@ -373,7 +382,7 @@
+ telnetd_main(int argc, char **argv)
+ {
+ #ifndef CONFIG_FEATURE_TELNETD_INETD
+- struct sockaddr_in sa;
++ sockaddr_type sa;
+ int master_fd;
+ #endif /* CONFIG_FEATURE_TELNETD_INETD */
+ fd_set rdfdset, wrfdset;
+@@ -431,7 +440,7 @@
+
+ /* Grab a TCP socket. */
+
+- master_fd = socket(AF_INET, SOCK_STREAM, 0);
++ master_fd = socket(SOCKET_TYPE, SOCK_STREAM, 0);
+ if (master_fd < 0) {
+ bb_perror_msg_and_die("socket");
+ }
+@@ -440,8 +449,13 @@
+ /* Set it to listen to specified port. */
+
+ memset((void *)&sa, 0, sizeof(sa));
++#ifdef CONFIG_FEATURE_IPV6
++ sa.sin6_family = AF_INET6;
++ sa.sin6_port = htons(portnbr);
++#else
+ sa.sin_family = AF_INET;
+ sa.sin_port = htons(portnbr);
++#endif
+
+ if (bind(master_fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
+ bb_perror_msg_and_die("bind");
+diff -Nur busybox-1.00/networking/tftp.c busybox/networking/tftp.c
+--- busybox-1.00/networking/tftp.c 2004-09-14 19:24:58.000000000 +0200
++++ busybox/networking/tftp.c 2005-06-04 08:20:05.000000000 +0200
+@@ -71,8 +71,8 @@
+ "No such user"
+ };
+
+-const int tftp_cmd_get = 1;
+-const int tftp_cmd_put = 2;
++static const int tftp_cmd_get = 1;
++static const int tftp_cmd_put = 2;
+
+ #ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
+
+diff -Nur busybox-1.00/networking/udhcp/README.udhcpc busybox/networking/udhcp/README.udhcpc
+--- busybox-1.00/networking/udhcp/README.udhcpc 2004-03-15 09:29:00.000000000 +0100
++++ busybox/networking/udhcp/README.udhcpc 2005-06-04 08:20:05.000000000 +0200
+@@ -12,7 +12,8 @@
+
+ -c, --clientid=CLIENTID Client identifier
+ -H, --hostname=HOSTNAME Client hostname
+--h, Alias for -H
++-h, Alias for -H
++-F, --fqdn=FQDN Client fully qualified domain name
+ -f, --foreground Do not fork after getting lease
+ -b, --background Fork to background if lease cannot be
+ immediately negotiated.
+diff -Nur busybox-1.00/networking/udhcp/clientpacket.c busybox/networking/udhcp/clientpacket.c
+--- busybox-1.00/networking/udhcp/clientpacket.c 2004-04-14 19:51:25.000000000 +0200
++++ busybox/networking/udhcp/clientpacket.c 2005-06-04 08:20:05.000000000 +0200
+@@ -78,6 +78,7 @@
+ memcpy(packet->chaddr, client_config.arp, 6);
+ add_option_string(packet->options, client_config.clientid);
+ if (client_config.hostname) add_option_string(packet->options, client_config.hostname);
++ if (client_config.fqdn) add_option_string(packet->options, client_config.fqdn);
+ add_option_string(packet->options, (uint8_t *) &vendor_id);
+ }
+
+diff -Nur busybox-1.00/networking/udhcp/dhcpc.c busybox/networking/udhcp/dhcpc.c
+--- busybox-1.00/networking/udhcp/dhcpc.c 2004-05-19 10:29:05.000000000 +0200
++++ busybox/networking/udhcp/dhcpc.c 2005-06-04 08:20:05.000000000 +0200
+@@ -58,17 +58,18 @@
+
+ struct client_config_t client_config = {
+ /* Default options. */
+- abort_if_no_lease: 0,
+- foreground: 0,
+- quit_after_lease: 0,
+- background_if_no_lease: 0,
+- interface: "eth0",
+- pidfile: NULL,
+- script: DEFAULT_SCRIPT,
+- clientid: NULL,
+- hostname: NULL,
+- ifindex: 0,
+- arp: "\0\0\0\0\0\0", /* appease gcc-3.0 */
++ .abort_if_no_lease = 0,
++ .foreground = 0,
++ .quit_after_lease = 0,
++ .background_if_no_lease = 0,
++ .interface = "eth0",
++ .pidfile = NULL,
++ .script = DEFAULT_SCRIPT,
++ .clientid = NULL,
++ .hostname = NULL,
++ .fqdn = NULL,
++ .ifindex = 0,
++ .arp = "\0\0\0\0\0\0", /* appease gcc-3.0 */
+ };
+
+ #ifndef IN_BUSYBOX
+@@ -79,6 +80,7 @@
+ " -c, --clientid=CLIENTID Client identifier\n"
+ " -H, --hostname=HOSTNAME Client hostname\n"
+ " -h Alias for -H\n"
++" -F, --fqdn=FQDN Client fully qualified domain name\n"
+ " -f, --foreground Do not fork after getting lease\n"
+ " -b, --background Fork to background if lease cannot be\n"
+ " immediately negotiated.\n"
+@@ -197,7 +199,8 @@
+ {"foreground", no_argument, 0, 'f'},
+ {"background", no_argument, 0, 'b'},
+ {"hostname", required_argument, 0, 'H'},
+- {"hostname", required_argument, 0, 'h'},
++ {"hostname", required_argument, 0, 'h'},
++ {"fqdn", required_argument, 0, 'F'},
+ {"interface", required_argument, 0, 'i'},
+ {"now", no_argument, 0, 'n'},
+ {"pidfile", required_argument, 0, 'p'},
+@@ -211,7 +214,7 @@
+ /* get options */
+ while (1) {
+ int option_index = 0;
+- c = getopt_long(argc, argv, "c:fbH:h:i:np:qr:s:v", arg_options, &option_index);
++ c = getopt_long(argc, argv, "c:fbH:h:F:i:np:qr:s:v", arg_options, &option_index);
+ if (c == -1) break;
+
+ switch (c) {
+@@ -239,6 +242,23 @@
+ client_config.hostname[OPT_LEN] = len;
+ strncpy(client_config.hostname + 2, optarg, len);
+ break;
++ case 'F':
++ len = strlen(optarg) > 255 ? 255 : strlen(optarg);
++ if (client_config.fqdn) free(client_config.fqdn);
++ client_config.fqdn = xmalloc(len + 5);
++ client_config.fqdn[OPT_CODE] = DHCP_FQDN;
++ client_config.fqdn[OPT_LEN] = len + 3;
++ /* Flags: 0000NEOS
++ S: 1 => Client requests Server to update A RR in DNS as well as PTR
++ O: 1 => Server indicates to client that DNS has been updated regardless
++ E: 1 => Name data is DNS format, i.e. <4>host<6>domain<4>com<0> not "host.domain.com"
++ N: 1 => Client requests Server to not update DNS
++ */
++ client_config.fqdn[OPT_LEN + 1] = 0x1;
++ client_config.fqdn[OPT_LEN + 2] = 0;
++ client_config.fqdn[OPT_LEN + 3] = 0;
++ strncpy(client_config.fqdn + 5, optarg, len);
++ break;
+ case 'i':
+ client_config.interface = optarg;
+ break;
+@@ -419,6 +439,9 @@
+ (unsigned long) packet.xid, xid);
+ continue;
+ }
++ /* Ignore packets that aren't for us */
++ if (memcmp(client_config.arp,packet.chaddr,6))
++ continue;
+
+ if ((message = get_option(&packet, DHCP_MESSAGE_TYPE)) == NULL) {
+ DEBUG(LOG_ERR, "couldnt get option from packet -- ignoring");
+diff -Nur busybox-1.00/networking/udhcp/dhcpc.h busybox/networking/udhcp/dhcpc.h
+--- busybox-1.00/networking/udhcp/dhcpc.h 2004-01-31 00:45:12.000000000 +0100
++++ busybox/networking/udhcp/dhcpc.h 2005-06-04 08:20:05.000000000 +0200
+@@ -27,6 +27,7 @@
+ char *script; /* User script to run at dhcp events */
+ uint8_t *clientid; /* Optional client id to use */
+ uint8_t *hostname; /* Optional hostname to use */
++ uint8_t *fqdn; /* Optional fully qualified domain name to use */
+ int ifindex; /* Index number of the interface to use */
+ uint8_t arp[6]; /* Our arp address */
+ };
+diff -Nur busybox-1.00/networking/udhcp/dhcpd.h busybox/networking/udhcp/dhcpd.h
+--- busybox-1.00/networking/udhcp/dhcpd.h 2004-10-08 10:49:26.000000000 +0200
++++ busybox/networking/udhcp/dhcpd.h 2005-06-04 08:20:05.000000000 +0200
+@@ -63,6 +63,7 @@
+ #define DHCP_T2 0x3b
+ #define DHCP_VENDOR 0x3c
+ #define DHCP_CLIENT_ID 0x3d
++#define DHCP_FQDN 0x51
+
+ #define DHCP_END 0xFF
+
+diff -Nur busybox-1.00/networking/udhcp/options.c busybox/networking/udhcp/options.c
+--- busybox-1.00/networking/udhcp/options.c 2004-03-15 09:29:01.000000000 +0100
++++ busybox/networking/udhcp/options.c 2005-06-04 08:20:05.000000000 +0200
+@@ -32,7 +32,9 @@
+ {"ipttl", OPTION_U8, 0x17},
+ {"mtu", OPTION_U16, 0x1a},
+ {"broadcast", OPTION_IP | OPTION_REQ, 0x1c},
+- {"ntpsrv", OPTION_IP | OPTION_LIST, 0x2a},
++ {"nisdomain", OPTION_STRING | OPTION_REQ, 0x28},
++ {"nissrv", OPTION_IP | OPTION_LIST | OPTION_REQ, 0x29},
++ {"ntpsrv", OPTION_IP | OPTION_LIST | OPTION_REQ, 0x2a},
+ {"wins", OPTION_IP | OPTION_LIST, 0x2c},
+ {"requestip", OPTION_IP, 0x32},
+ {"lease", OPTION_U32, 0x33},
+diff -Nur busybox-1.00/networking/udhcp/script.h busybox/networking/udhcp/script.h
+--- busybox-1.00/networking/udhcp/script.h 2002-10-14 23:41:27.000000000 +0200
++++ busybox/networking/udhcp/script.h 2005-06-04 08:20:05.000000000 +0200
+@@ -1,6 +1,6 @@
+ #ifndef _SCRIPT_H
+ #define _SCRIPT_H
+
+-void run_script(struct dhcpMessage *packet, const char *name);
++extern void run_script(struct dhcpMessage *packet, const char *name);
+
+ #endif
+diff -Nur busybox-1.00/networking/wget.c busybox/networking/wget.c
+--- busybox-1.00/networking/wget.c 2004-10-08 10:27:40.000000000 +0200
++++ busybox/networking/wget.c 2005-06-04 08:20:05.000000000 +0200
+@@ -117,9 +117,9 @@
+ /*
+ * Base64-encode character string
+ * oops... isn't something similar in uuencode.c?
+- * It would be better to use already existing code
++ * XXX: It would be better to use already existing code
+ */
+-char *base64enc(unsigned char *p, char *buf, int len) {
++static char *base64enc(unsigned char *p, char *buf, int len) {
+
+ char al[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
+ "0123456789+/";
+@@ -854,7 +854,7 @@
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+- * $Id$
++ * $Id$
+ */
+
+
+diff -Nur busybox-1.00/networking/zcip.c busybox/networking/zcip.c
+--- busybox-1.00/networking/zcip.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/networking/zcip.c 2005-06-04 08:20:05.000000000 +0200
+@@ -0,0 +1,550 @@
++/*
++ * RFC3927 ZeroConf IPv4 Link-Local addressing
++ * (see <http://www.zeroconf.org/>)
++ *
++ * Copyright (C) 2003 by Arthur van Hoff (avh@strangeberry.com)
++ * Copyright (C) 2004 by David Brownell
++ *
++ * 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
++ */
++
++/*
++ * This can build as part of BusyBox or by itself:
++ *
++ * $(CROSS_COMPILE)cc -Os -Wall -DNO_BUSYBOX -DDEBUG -o zcip zcip.c
++ *
++ * ZCIP just manages the 169.254.*.* addresses. That network is not
++ * routed at the IP level, though various proxies or bridges can
++ * certainly be used. Its naming is built over multicast DNS.
++ */
++
++// #define DEBUG
++
++// TODO:
++// - more real-world usage/testing, especially daemon mode
++// - kernel packet filters to reduce scheduling noise
++// - avoid silent script failures, especially under load...
++// - link status monitoring (restart on link-up; stop on link-down)
++
++#include <errno.h>
++#include <stdlib.h>
++#include <stdio.h>
++#include <string.h>
++#include <syslog.h>
++#include <poll.h>
++#include <time.h>
++#include <unistd.h>
++
++#include <sys/ioctl.h>
++#include <sys/types.h>
++#include <sys/wait.h>
++#include <sys/time.h>
++#include <sys/socket.h>
++
++#include <arpa/inet.h>
++#include <netinet/in.h>
++#include <netinet/ether.h>
++#include <net/ethernet.h>
++#include <net/if.h>
++#include <net/if_arp.h>
++
++#include <linux/if_packet.h>
++#include <linux/sockios.h>
++
++
++struct arp_packet {
++ struct ether_header hdr;
++ // FIXME this part is netinet/if_ether.h "struct ether_arp"
++ struct arphdr arp;
++ struct ether_addr source_addr;
++ struct in_addr source_ip;
++ struct ether_addr target_addr;
++ struct in_addr target_ip;
++} __attribute__ ((__packed__));
++
++/* 169.254.0.0 */
++static const uint32_t LINKLOCAL_ADDR = 0xa9fe0000;
++
++/* protocol timeout parameters, specified in seconds */
++static const unsigned PROBE_WAIT = 1;
++static const unsigned PROBE_MIN = 1;
++static const unsigned PROBE_MAX = 2;
++static const unsigned PROBE_NUM = 3;
++static const unsigned MAX_CONFLICTS = 10;
++static const unsigned RATE_LIMIT_INTERVAL = 60;
++static const unsigned ANNOUNCE_WAIT = 2;
++static const unsigned ANNOUNCE_NUM = 2;
++static const unsigned ANNOUNCE_INTERVAL = 2;
++static const time_t DEFEND_INTERVAL = 10;
++
++static const unsigned char ZCIP_VERSION[] = "0.75 (18 April 2005)";
++static char *prog;
++
++static const struct in_addr null_ip = { 0 };
++static const struct ether_addr null_addr = { {0, 0, 0, 0, 0, 0} };
++
++static int verbose = 0;
++
++#ifdef DEBUG
++
++#define DBG(fmt,args...) \
++ fprintf(stderr, "%s: " fmt , prog , ## args)
++#define VDBG(fmt,args...) do { \
++ if (verbose) fprintf(stderr, "%s: " fmt , prog ,## args); \
++ } while (0)
++#else
++
++#define DBG(fmt,args...) \
++ do { } while (0)
++#define VDBG DBG
++#endif /* DEBUG */
++
++/**
++ * Pick a random link local IP address on 169.254/16, except that
++ * the first and last 256 addresses are reserved.
++ */
++static void
++pick(struct in_addr *ip)
++{
++ unsigned tmp;
++
++ /* use cheaper math than lrand48() mod N */
++ do {
++ tmp = (lrand48() >> 16) & IN_CLASSB_HOST;
++ } while (tmp > (IN_CLASSB_HOST - 0x0200));
++ ip->s_addr = htonl((LINKLOCAL_ADDR + 0x0100) + tmp);
++}
++
++/**
++ * Broadcast an ARP packet.
++ */
++static int
++arp(int fd, struct sockaddr *saddr, int op,
++ const struct ether_addr *source_addr, struct in_addr source_ip,
++ const struct ether_addr *target_addr, struct in_addr target_ip)
++{
++ struct arp_packet p;
++
++ // ether header
++ p.hdr.ether_type = htons(ETHERTYPE_ARP);
++ memcpy(p.hdr.ether_shost, source_addr, ETH_ALEN);
++ memset(p.hdr.ether_dhost, 0xff, ETH_ALEN);
++
++ // arp request
++ p.arp.ar_hrd = htons(ARPHRD_ETHER);
++ p.arp.ar_pro = htons(ETHERTYPE_IP);
++ p.arp.ar_hln = ETH_ALEN;
++ p.arp.ar_pln = 4;
++ p.arp.ar_op = htons(op);
++ memcpy(&p.source_addr, source_addr, ETH_ALEN);
++ memcpy(&p.source_ip, &source_ip, sizeof (p.source_ip));
++ memcpy(&p.target_addr, target_addr, ETH_ALEN);
++ memcpy(&p.target_ip, &target_ip, sizeof (p.target_ip));
++
++ // send it
++ if (sendto(fd, &p, sizeof (p), 0, saddr, sizeof (*saddr)) < 0) {
++ perror("sendto");
++ return -errno;
++ }
++ return 0;
++}
++
++/**
++ * Run a script.
++ */
++static int
++run(char *script, char *arg, char *intf, struct in_addr *ip)
++{
++ int pid, status;
++ char *why;
++
++ if (script != NULL) {
++ VDBG("%s run %s %s\n", intf, script, arg);
++ if (ip != NULL) {
++ char *addr = inet_ntoa(*ip);
++ setenv("ip", addr, 1);
++ syslog(LOG_INFO, "%s %s %s", arg, intf, addr);
++ }
++
++ pid = vfork();
++ if (pid < 0) { // error
++ why = "vfork";
++ goto bad;
++ } else if (pid == 0) { // child
++ execl(script, script, arg, NULL);
++ perror("execl");
++ _exit(EXIT_FAILURE);
++ }
++
++ if (waitpid(pid, &status, 0) <= 0) {
++ why = "waitpid";
++ goto bad;
++ }
++ if (WEXITSTATUS(status) != 0) {
++ fprintf(stderr, "%s: script %s failed, exit=%d\n",
++ prog, script, WEXITSTATUS(status));
++ return -errno;
++ }
++ }
++ return 0;
++bad:
++ status = -errno;
++ syslog(LOG_ERR, "%s %s, %s error: %s",
++ arg, intf, why, strerror(errno));
++ return status;
++}
++
++#ifndef NO_BUSYBOX
++#include "busybox.h"
++#endif
++
++/**
++ * Print usage information.
++ */
++static void __attribute__ ((noreturn))
++usage(const char *msg)
++{
++ fprintf(stderr, "%s: %s\n", prog, msg);
++#ifdef NO_BUSYBOX
++ fprintf(stderr, "Usage: %s [OPTIONS] ifname script\n"
++ "\t-f foreground mode (implied by -v)\n"
++ "\t-q quit after address (no daemon)\n"
++ "\t-r 169.254.x.x request this address first\n"
++ "\t-v verbose; show version\n",
++ prog);
++ exit(0);
++#else
++ bb_show_usage();
++#endif
++}
++
++/**
++ * Return milliseconds of random delay, up to "secs" seconds.
++ */
++static inline unsigned
++ms_rdelay(unsigned secs)
++{
++ return lrand48() % (secs * 1000);
++}
++
++/**
++ * main program
++ */
++int
++main(int argc, char *argv[])
++ __attribute__ ((weak, alias ("zcip_main")));
++
++int zcip_main(int argc, char *argv[])
++{
++ char *intf = NULL;
++ char *script = NULL;
++ int quit = 0;
++ int foreground = 0;
++
++ char *why;
++ struct sockaddr saddr;
++ struct ether_addr addr;
++ struct in_addr ip = { 0 };
++ int fd;
++ int ready = 0;
++ suseconds_t timeout = 0; // milliseconds
++ time_t defend = 0;
++ unsigned conflicts = 0;
++ unsigned nprobes = 0;
++ unsigned nclaims = 0;
++ int t;
++
++ // parse commandline: prog [options] ifname script
++ prog = argv[0];
++ while ((t = getopt(argc, argv, "fqr:v")) != EOF) {
++ switch (t) {
++ case 'f':
++ foreground = 1;
++ continue;
++ case 'q':
++ quit = 1;
++ continue;
++ case 'r':
++ if (inet_aton(optarg, &ip) == 0
++ || (ntohl(ip.s_addr) & IN_CLASSB_NET)
++ != LINKLOCAL_ADDR) {
++ usage("invalid link address");
++ }
++ continue;
++ case 'v':
++ if (!verbose)
++ printf("%s: version %s\n", prog, ZCIP_VERSION);
++ verbose++;
++ foreground = 1;
++ continue;
++ default:
++ usage("bad option");
++ }
++ }
++ if (optind < argc - 1) {
++ intf = argv[optind++];
++ setenv("interface", intf, 1);
++ script = argv[optind++];
++ }
++ if (optind != argc || !intf)
++ usage("wrong number of arguments");
++ openlog(prog, 0, LOG_DAEMON);
++
++ // initialize the interface (modprobe, ifup, etc)
++ if (run(script, "init", intf, NULL) < 0)
++ return EXIT_FAILURE;
++
++ // initialize saddr
++ memset(&saddr, 0, sizeof (saddr));
++ strncpy(saddr.sa_data, intf, sizeof (saddr.sa_data));
++
++ // open an ARP socket
++ if ((fd = socket(PF_PACKET, SOCK_PACKET, htons(ETH_P_ARP))) < 0) {
++ why = "open";
++fail:
++ foreground = 1;
++ goto bad;
++ }
++ // bind to the interface's ARP socket
++ if (bind(fd, &saddr, sizeof (saddr)) < 0) {
++ why = "bind";
++ goto fail;
++ } else {
++ struct ifreq ifr;
++ short seed[3];
++
++ // get the interface's ethernet address
++ memset(&ifr, 0, sizeof (ifr));
++ strncpy(ifr.ifr_name, intf, sizeof (ifr.ifr_name));
++ if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) {
++ why = "get ethernet address";
++ goto fail;
++ }
++ memcpy(&addr, &ifr.ifr_hwaddr.sa_data, ETH_ALEN);
++
++ // start with some stable ip address, either a function of
++ // the hardware address or else the last address we used.
++ // NOTE: the sequence of addresses we try changes only
++ // depending on when we detect conflicts.
++ memcpy(seed, &ifr.ifr_hwaddr.sa_data, ETH_ALEN);
++ seed48(seed);
++ if (ip.s_addr == 0)
++ pick(&ip);
++ }
++
++ // FIXME cases to handle:
++ // - zcip already running!
++ // - link already has local address... just defend/update
++
++ // daemonize now; don't delay system startup
++ if (!foreground) {
++ if (daemon(0, verbose) < 0) {
++ why = "daemon";
++ goto bad;
++ }
++ syslog(LOG_INFO, "start, interface %s", intf);
++ }
++
++ // run the dynamic address negotiation protocol,
++ // restarting after address conflicts:
++ // - start with some address we want to try
++ // - short random delay
++ // - arp probes to see if another host else uses it
++ // - arp announcements that we're claiming it
++ // - use it
++ // - defend it, within limits
++ while (1) {
++ struct pollfd fds[1];
++ struct timeval tv1;
++ struct arp_packet p;
++
++ fds[0].fd = fd;
++ fds[0].events = POLLIN;
++ fds[0].revents = 0;
++
++ // poll, being ready to adjust current timeout
++ if (timeout > 0) {
++ gettimeofday(&tv1, NULL);
++ tv1.tv_usec += (timeout % 1000) * 1000;
++ while (tv1.tv_usec > 1000000) {
++ tv1.tv_usec -= 1000000;
++ tv1.tv_sec++;
++ }
++ tv1.tv_sec += timeout / 1000;
++ } else if (timeout == 0) {
++ timeout = ms_rdelay(PROBE_WAIT);
++ // FIXME setsockopt(fd, SO_ATTACH_FILTER, ...) to
++ // make the kernel filter out all packets except
++ // ones we'd care about.
++ }
++ VDBG("...wait %ld %s nprobes=%d, nclaims=%d\n",
++ timeout, intf, nprobes, nclaims);
++ switch (poll(fds, 1, timeout)) {
++
++ // timeouts trigger protocol transitions
++ case 0:
++ // probes
++ if (nprobes < PROBE_NUM) {
++ nprobes++;
++ VDBG("probe/%d %s@%s\n",
++ nprobes, intf, inet_ntoa(ip));
++ (void)arp(fd, &saddr, ARPOP_REQUEST,
++ &addr, null_ip,
++ &null_addr, ip);
++ if (nprobes < PROBE_NUM) {
++ timeout = PROBE_MIN * 1000;
++ timeout += ms_rdelay(PROBE_MAX
++ - PROBE_MIN);
++ } else
++ timeout = ANNOUNCE_WAIT * 1000;
++ }
++ // then announcements
++ else if (nclaims < ANNOUNCE_NUM) {
++ nclaims++;
++ VDBG("announce/%d %s@%s\n",
++ nclaims, intf, inet_ntoa(ip));
++ (void)arp(fd, &saddr, ARPOP_REQUEST,
++ &addr, ip,
++ &addr, ip);
++ if (nclaims < ANNOUNCE_NUM) {
++ timeout = ANNOUNCE_INTERVAL * 1000;
++ } else {
++ // link is ok to use earlier
++ run(script, "config", intf, &ip);
++ ready = 1;
++ conflicts = 0;
++ timeout = -1;
++
++ // NOTE: all other exit paths
++ // should deconfig ...
++ if (quit)
++ return EXIT_SUCCESS;
++ // FIXME update filters
++ }
++ }
++ break;
++
++ // packets arriving
++ case 1:
++ // maybe adjust timeout
++ if (timeout > 0) {
++ struct timeval tv2;
++
++ gettimeofday(&tv2, NULL);
++ if (timercmp(&tv1, &tv2, <)) {
++ timeout = -1;
++ } else {
++ timersub(&tv1, &tv2, &tv1);
++ timeout = 1000 * tv1.tv_sec
++ + tv1.tv_usec / 1000;
++ }
++ }
++ if ((fds[0].revents & POLLIN) == 0) {
++ if (fds[0].revents & POLLERR) {
++ // FIXME: links routinely go down;
++ // this shouldn't necessarily exit.
++ fprintf(stderr, "%s %s: poll error\n",
++ prog, intf);
++ if (ready) {
++ run(script, "deconfig",
++ intf, &ip);
++ }
++ return EXIT_FAILURE;
++ }
++ continue;
++ }
++ // read ARP packet
++ if (recv(fd, &p, sizeof (p), 0) < 0) {
++ why = "recv";
++ goto bad;
++ }
++ if (p.hdr.ether_type != htons(ETHERTYPE_ARP))
++ continue;
++
++ VDBG("%s recv arp type=%d, op=%d,\n",
++ intf, ntohs(p.hdr.ether_type),
++ ntohs(p.arp.ar_op));
++ VDBG("\tsource=%s %s\n",
++ ether_ntoa(&p.source_addr),
++ inet_ntoa(p.source_ip));
++ VDBG("\ttarget=%s %s\n",
++ ether_ntoa(&p.target_addr),
++ inet_ntoa(p.target_ip));
++ if (p.arp.ar_op != htons(ARPOP_REQUEST)
++ && p.arp.ar_op != htons(ARPOP_REPLY))
++ continue;
++
++ // some cases are always conflicts
++ if ((p.source_ip.s_addr == ip.s_addr)
++ && (memcmp(&addr, &p.source_addr,
++ ETH_ALEN) != 0)) {
++collision:
++ VDBG("%s ARP conflict from %s\n", intf,
++ ether_ntoa(&p.source_addr));
++ if (ready) {
++ time_t now = time(0);
++
++ if ((defend + DEFEND_INTERVAL)
++ < now) {
++ defend = now;
++ (void)arp(fd, &saddr,
++ ARPOP_REQUEST,
++ &addr, ip,
++ &addr, ip);
++ VDBG("%s defend\n", intf);
++ timeout = -1;
++ continue;
++ }
++ defend = now;
++ ready = 0;
++ run(script, "deconfig", intf, &ip);
++ // FIXME rm filters: setsockopt(fd,
++ // SO_DETACH_FILTER, ...)
++ }
++ conflicts++;
++ if (conflicts >= MAX_CONFLICTS) {
++ VDBG("%s ratelimit\n", intf);
++ sleep(RATE_LIMIT_INTERVAL);
++ }
++ // restart the whole protocol
++ pick(&ip);
++ timeout = 0;
++ nprobes = 0;
++ nclaims = 0;
++ }
++ // two hosts probing one address is a collision too
++ else if (p.target_ip.s_addr == ip.s_addr
++ && nclaims == 0
++ && p.arp.ar_op == htons(ARPOP_REQUEST)
++ && memcmp(&addr, &p.target_addr,
++ ETH_ALEN) != 0) {
++ goto collision;
++ }
++ break;
++
++ default:
++ why = "poll";
++ goto bad;
++ }
++ }
++bad:
++ if (foreground)
++ perror(why);
++ else
++ syslog(LOG_ERR, "%s %s, %s error: %s",
++ prog, intf, why, strerror(errno));
++ return EXIT_FAILURE;
++}
+diff -Nur busybox-1.00/patches/ed.patch busybox/patches/ed.patch
+--- busybox-1.00/patches/ed.patch 1970-01-01 01:00:00.000000000 +0100
++++ busybox/patches/ed.patch 2005-06-04 08:20:03.000000000 +0200
+@@ -0,0 +1,1489 @@
++Index: editors/Makefile.in
++===================================================================
++--- editors/Makefile.in (revision 10144)
+++++ editors/Makefile.in (working copy)
++@@ -24,8 +24,9 @@
++ srcdir=$(top_srcdir)/editors
++
++ EDITOR-y:=
++-EDITOR-$(CONFIG_AWK) += awk.o
++-EDITOR-$(CONFIG_PATCH) += patch.o
+++EDITOR-$(CONFIG_AWK) += awk.o
+++EDITOR-$(CONFIG_ED) += ed.o
+++EDITOR-$(CONFIG_PATCH) += patch.o
++ EDITOR-$(CONFIG_SED) += sed.o
++ EDITOR-$(CONFIG_VI) += vi.o
++ EDITOR_SRC:= $(EDITOR-y)
++Index: editors/Config.in
++===================================================================
++--- editors/Config.in (revision 10144)
+++++ editors/Config.in (working copy)
++@@ -20,6 +20,12 @@
++ Enable math functions of the Awk programming language.
++ NOTE: This will require libm to be present for linking.
++
+++config CONFIG_ED
+++ bool "ed"
+++ default n
+++ help
+++ ed
+++
++ config CONFIG_PATCH
++ bool "patch"
++ default n
++Index: include/usage.h
++===================================================================
++--- include/usage.h (revision 10151)
+++++ include/usage.h (working copy)
++@@ -556,6 +561,9 @@
++ "$ echo \"Erik\\nis\\ncool\"\n" \
++ "Erik\\nis\\ncool\n")
++
+++#define ed_trivial_usage ""
+++#define ed_full_usage ""
+++
++ #define env_trivial_usage \
++ "[-iu] [-] [name=value]... [command]"
++ #define env_full_usage \
++Index: include/applets.h
++===================================================================
++--- include/applets.h (revision 10151)
+++++ include/applets.h (working copy)
++@@ -179,6 +179,9 @@
++ #ifdef CONFIG_ECHO
++ APPLET(echo, echo_main, _BB_DIR_BIN, _BB_SUID_NEVER)
++ #endif
+++#ifdef CONFIG_ED
+++ APPLET(ed, ed_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+++#endif
++ #if defined(CONFIG_FEATURE_GREP_EGREP_ALIAS)
++ APPLET_NOUSAGE("egrep", grep_main, _BB_DIR_BIN, _BB_SUID_NEVER)
++ #endif
++--- /dev/null 2005-04-24 01:00:01.350003056 -0400
+++++ ed.c 2005-04-24 01:38:51.000000000 -0400
++@@ -0,0 +1,1425 @@
+++/*
+++ * Copyright (c) 2002 by David I. Bell
+++ * Permission is granted to use, distribute, or modify this source,
+++ * provided that this copyright notice remains intact.
+++ *
+++ * The "ed" built-in command (much simplified)
+++ */
+++
+++#include <stdio.h>
+++#include <stdlib.h>
+++#include <unistd.h>
+++#include <fcntl.h>
+++#include <string.h>
+++#include <memory.h>
+++#include <time.h>
+++#include <ctype.h>
+++#include <sys/param.h>
+++#include <malloc.h>
+++#include "busybox.h"
+++
+++#define USERSIZE 1024 /* max line length typed in by user */
+++#define INITBUF_SIZE 1024 /* initial buffer size */
+++
+++typedef int BOOL;
+++typedef int NUM;
+++typedef int LEN;
+++
+++typedef struct LINE LINE;
+++struct LINE {
+++ LINE *next;
+++ LINE *prev;
+++ LEN len;
+++ char data[1];
+++};
+++
+++static LINE lines;
+++static LINE *curLine;
+++static NUM curNum;
+++static NUM lastNum;
+++static NUM marks[26];
+++static BOOL dirty;
+++static char *fileName;
+++static char searchString[USERSIZE];
+++
+++static char *bufBase;
+++static char *bufPtr;
+++static LEN bufUsed;
+++static LEN bufSize;
+++
+++static void doCommands(void);
+++static void subCommand(const char * cmd, NUM num1, NUM num2);
+++static BOOL getNum(const char ** retcp, BOOL * retHaveNum, NUM * retNum);
+++static BOOL setCurNum(NUM num);
+++static BOOL initEdit(void);
+++static void termEdit(void);
+++static void addLines(NUM num);
+++static BOOL insertLine(NUM num, const char * data, LEN len);
+++static BOOL deleteLines(NUM num1, NUM num2);
+++static BOOL printLines(NUM num1, NUM num2, BOOL expandFlag);
+++static BOOL writeLines(const char * file, NUM num1, NUM num2);
+++static BOOL readLines(const char * file, NUM num);
+++static NUM searchLines(const char * str, NUM num1, NUM num2);
+++static LINE * findLine(NUM num);
+++
+++static LEN findString(const LINE * lp, const char * str, LEN len, LEN offset);
+++
+++int ed_main(int argc, char **argv)
+++{
+++ if (!initEdit())
+++ return EXIT_FAILURE;
+++
+++ if (argc > 1) {
+++ fileName = strdup(argv[1]);
+++
+++ if (fileName == NULL) {
+++ bb_error_msg("No memory");
+++ termEdit();
+++ return EXIT_SUCCESS;
+++ }
+++
+++ if (!readLines(fileName, 1)) {
+++ termEdit();
+++ return EXIT_SUCCESS;
+++ }
+++
+++ if (lastNum)
+++ setCurNum(1);
+++
+++ dirty = FALSE;
+++ }
+++
+++ doCommands();
+++
+++ termEdit();
+++ return EXIT_SUCCESS;
+++}
+++
+++/*
+++ * Read commands until we are told to stop.
+++ */
+++static void doCommands(void)
+++{
+++ const char * cp;
+++ char * endbuf;
+++ char * newname;
+++ int len;
+++ NUM num1;
+++ NUM num2;
+++ BOOL have1;
+++ BOOL have2;
+++ char buf[USERSIZE];
+++
+++ while (TRUE)
+++ {
+++ printf(": ");
+++ fflush(stdout);
+++
+++ if (fgets(buf, sizeof(buf), stdin) == NULL)
+++ return;
+++
+++ len = strlen(buf);
+++
+++ if (len == 0)
+++ return;
+++
+++ endbuf = &buf[len - 1];
+++
+++ if (*endbuf != '\n')
+++ {
+++ bb_error_msg("Command line too long");
+++
+++ do
+++ {
+++ len = fgetc(stdin);
+++ }
+++ while ((len != EOF) && (len != '\n'));
+++
+++ continue;
+++ }
+++
+++ while ((endbuf > buf) && isblank(endbuf[-1]))
+++ endbuf--;
+++
+++ *endbuf = '\0';
+++
+++ cp = buf;
+++
+++ while (isblank(*cp))
+++ cp++;
+++
+++ have1 = FALSE;
+++ have2 = FALSE;
+++
+++ if ((curNum == 0) && (lastNum > 0))
+++ {
+++ curNum = 1;
+++ curLine = lines.next;
+++ }
+++
+++ if (!getNum(&cp, &have1, &num1))
+++ continue;
+++
+++ while (isblank(*cp))
+++ cp++;
+++
+++ if (*cp == ',')
+++ {
+++ cp++;
+++
+++ if (!getNum(&cp, &have2, &num2))
+++ continue;
+++
+++ if (!have1)
+++ num1 = 1;
+++
+++ if (!have2)
+++ num2 = lastNum;
+++
+++ have1 = TRUE;
+++ have2 = TRUE;
+++ }
+++
+++ if (!have1)
+++ num1 = curNum;
+++
+++ if (!have2)
+++ num2 = num1;
+++
+++ switch (*cp++)
+++ {
+++ case 'a':
+++ addLines(num1 + 1);
+++ break;
+++
+++ case 'c':
+++ deleteLines(num1, num2);
+++ addLines(num1);
+++ break;
+++
+++ case 'd':
+++ deleteLines(num1, num2);
+++ break;
+++
+++ case 'f':
+++ if (*cp && !isblank(*cp))
+++ {
+++ bb_error_msg("Bad file command");
+++ break;
+++ }
+++
+++ while (isblank(*cp))
+++ cp++;
+++
+++ if (*cp == '\0')
+++ {
+++ if (fileName)
+++ printf("\"%s\"\n", fileName);
+++ else
+++ printf("No file name\n");
+++
+++ break;
+++ }
+++
+++ newname = strdup(cp);
+++
+++ if (newname == NULL)
+++ {
+++ bb_error_msg("No memory for file name");
+++ break;
+++ }
+++
+++ if (fileName)
+++ free(fileName);
+++
+++ fileName = newname;
+++ break;
+++
+++ case 'i':
+++ addLines(num1);
+++ break;
+++
+++ case 'k':
+++ while (isblank(*cp))
+++ cp++;
+++
+++ if ((*cp < 'a') || (*cp > 'a') || cp[1])
+++ {
+++ bb_error_msg("Bad mark name");
+++ break;
+++ }
+++
+++ marks[*cp - 'a'] = num2;
+++ break;
+++
+++ case 'l':
+++ printLines(num1, num2, TRUE);
+++ break;
+++
+++ case 'p':
+++ printLines(num1, num2, FALSE);
+++ break;
+++
+++ case 'q':
+++ while (isblank(*cp))
+++ cp++;
+++
+++ if (have1 || *cp)
+++ {
+++ bb_error_msg("Bad quit command");
+++ break;
+++ }
+++
+++ if (!dirty)
+++ return;
+++
+++ printf("Really quit? ");
+++ fflush(stdout);
+++
+++ buf[0] = '\0';
+++ fgets(buf, sizeof(buf), stdin);
+++ cp = buf;
+++
+++ while (isblank(*cp))
+++ cp++;
+++
+++ if ((*cp == 'y') || (*cp == 'Y'))
+++ return;
+++
+++ break;
+++
+++ case 'r':
+++ if (*cp && !isblank(*cp))
+++ {
+++ bb_error_msg("Bad read command");
+++ break;
+++ }
+++
+++ while (isblank(*cp))
+++ cp++;
+++
+++ if (*cp == '\0')
+++ {
+++ bb_error_msg("No file name");
+++ break;
+++ }
+++
+++ if (!have1)
+++ num1 = lastNum;
+++
+++ if (readLines(cp, num1 + 1))
+++ break;
+++
+++ if (fileName == NULL)
+++ fileName = strdup(cp);
+++
+++ break;
+++
+++ case 's':
+++ subCommand(cp, num1, num2);
+++ break;
+++
+++ case 'w':
+++ if (*cp && !isblank(*cp))
+++ {
+++ bb_error_msg("Bad write command");
+++ break;
+++ }
+++
+++ while (isblank(*cp))
+++ cp++;
+++
+++ if (!have1) {
+++ num1 = 1;
+++ num2 = lastNum;
+++ }
+++
+++ if (*cp == '\0')
+++ cp = fileName;
+++
+++ if (cp == NULL)
+++ {
+++ bb_error_msg("No file name specified");
+++ break;
+++ }
+++
+++ writeLines(cp, num1, num2);
+++ break;
+++
+++ case 'z':
+++ switch (*cp)
+++ {
+++ case '-':
+++ printLines(curNum-21, curNum, FALSE);
+++ break;
+++ case '.':
+++ printLines(curNum-11, curNum+10, FALSE);
+++ break;
+++ default:
+++ printLines(curNum, curNum+21, FALSE);
+++ break;
+++ }
+++ break;
+++
+++ case '.':
+++ if (have1)
+++ {
+++ bb_error_msg("No arguments allowed");
+++ break;
+++ }
+++
+++ printLines(curNum, curNum, FALSE);
+++ break;
+++
+++ case '-':
+++ if (setCurNum(curNum - 1))
+++ printLines(curNum, curNum, FALSE);
+++
+++ break;
+++
+++ case '=':
+++ printf("%d\n", num1);
+++ break;
+++
+++ case '\0':
+++ if (have1)
+++ {
+++ printLines(num2, num2, FALSE);
+++ break;
+++ }
+++
+++ if (setCurNum(curNum + 1))
+++ printLines(curNum, curNum, FALSE);
+++
+++ break;
+++
+++ default:
+++ bb_error_msg("Unimplemented command");
+++ break;
+++ }
+++ }
+++}
+++
+++
+++/*
+++ * Do the substitute command.
+++ * The current line is set to the last substitution done.
+++ */
+++static void
+++subCommand(const char * cmd, NUM num1, NUM num2)
+++{
+++ int delim;
+++ char * cp;
+++ char * oldStr;
+++ char * newStr;
+++ LEN oldLen;
+++ LEN newLen;
+++ LEN deltaLen;
+++ LEN offset;
+++ LINE * lp;
+++ LINE * nlp;
+++ BOOL globalFlag;
+++ BOOL printFlag;
+++ BOOL didSub;
+++ BOOL needPrint;
+++ char buf[USERSIZE];
+++
+++ if ((num1 < 1) || (num2 > lastNum) || (num1 > num2))
+++ {
+++ bb_error_msg("Bad line range for substitute");
+++
+++ return;
+++ }
+++
+++ globalFlag = FALSE;
+++ printFlag = FALSE;
+++ didSub = FALSE;
+++ needPrint = FALSE;
+++
+++ /*
+++ * Copy the command so we can modify it.
+++ */
+++ strcpy(buf, cmd);
+++ cp = buf;
+++
+++ if (isblank(*cp) || (*cp == '\0'))
+++ {
+++ bb_error_msg("Bad delimiter for substitute");
+++
+++ return;
+++ }
+++
+++ delim = *cp++;
+++ oldStr = cp;
+++
+++ cp = strchr(cp, delim);
+++
+++ if (cp == NULL)
+++ {
+++ bb_error_msg("Missing 2nd delimiter for substitute");
+++
+++ return;
+++ }
+++
+++ *cp++ = '\0';
+++
+++ newStr = cp;
+++ cp = strchr(cp, delim);
+++
+++ if (cp)
+++ *cp++ = '\0';
+++ else
+++ cp = "";
+++
+++ while (*cp) switch (*cp++)
+++ {
+++ case 'g':
+++ globalFlag = TRUE;
+++ break;
+++
+++ case 'p':
+++ printFlag = TRUE;
+++ break;
+++
+++ default:
+++ bb_error_msg("Unknown option for substitute");
+++
+++ return;
+++ }
+++
+++ if (*oldStr == '\0')
+++ {
+++ if (searchString[0] == '\0')
+++ {
+++ bb_error_msg("No previous search string");
+++
+++ return;
+++ }
+++
+++ oldStr = searchString;
+++ }
+++
+++ if (oldStr != searchString)
+++ strcpy(searchString, oldStr);
+++
+++ lp = findLine(num1);
+++
+++ if (lp == NULL)
+++ return;
+++
+++ oldLen = strlen(oldStr);
+++ newLen = strlen(newStr);
+++ deltaLen = newLen - oldLen;
+++ offset = 0;
+++ nlp = NULL;
+++
+++ while (num1 <= num2)
+++ {
+++ offset = findString(lp, oldStr, oldLen, offset);
+++
+++ if (offset < 0)
+++ {
+++ if (needPrint)
+++ {
+++ printLines(num1, num1, FALSE);
+++ needPrint = FALSE;
+++ }
+++
+++ offset = 0;
+++ lp = lp->next;
+++ num1++;
+++
+++ continue;
+++ }
+++
+++ needPrint = printFlag;
+++ didSub = TRUE;
+++ dirty = TRUE;
+++
+++ /*
+++ * If the replacement string is the same size or shorter
+++ * than the old string, then the substitution is easy.
+++ */
+++ if (deltaLen <= 0)
+++ {
+++ memcpy(&lp->data[offset], newStr, newLen);
+++
+++ if (deltaLen)
+++ {
+++ memcpy(&lp->data[offset + newLen],
+++ &lp->data[offset + oldLen],
+++ lp->len - offset - oldLen);
+++
+++ lp->len += deltaLen;
+++ }
+++
+++ offset += newLen;
+++
+++ if (globalFlag)
+++ continue;
+++
+++ if (needPrint)
+++ {
+++ printLines(num1, num1, FALSE);
+++ needPrint = FALSE;
+++ }
+++
+++ lp = lp->next;
+++ num1++;
+++
+++ continue;
+++ }
+++
+++ /*
+++ * The new string is larger, so allocate a new line
+++ * structure and use that. Link it in in place of
+++ * the old line structure.
+++ */
+++ nlp = (LINE *) malloc(sizeof(LINE) + lp->len + deltaLen);
+++
+++ if (nlp == NULL)
+++ {
+++ bb_error_msg("Cannot get memory for line");
+++
+++ return;
+++ }
+++
+++ nlp->len = lp->len + deltaLen;
+++
+++ memcpy(nlp->data, lp->data, offset);
+++
+++ memcpy(&nlp->data[offset], newStr, newLen);
+++
+++ memcpy(&nlp->data[offset + newLen],
+++ &lp->data[offset + oldLen],
+++ lp->len - offset - oldLen);
+++
+++ nlp->next = lp->next;
+++ nlp->prev = lp->prev;
+++ nlp->prev->next = nlp;
+++ nlp->next->prev = nlp;
+++
+++ if (curLine == lp)
+++ curLine = nlp;
+++
+++ free(lp);
+++ lp = nlp;
+++
+++ offset += newLen;
+++
+++ if (globalFlag)
+++ continue;
+++
+++ if (needPrint)
+++ {
+++ printLines(num1, num1, FALSE);
+++ needPrint = FALSE;
+++ }
+++
+++ lp = lp->next;
+++ num1++;
+++ }
+++
+++ if (!didSub)
+++ bb_error_msg("No substitutions found for \"%s\"", oldStr);
+++}
+++
+++
+++/*
+++ * Search a line for the specified string starting at the specified
+++ * offset in the line. Returns the offset of the found string, or -1.
+++ */
+++static LEN
+++findString( const LINE * lp, const char * str, LEN len, LEN offset)
+++{
+++ LEN left;
+++ const char * cp;
+++ const char * ncp;
+++
+++ cp = &lp->data[offset];
+++ left = lp->len - offset;
+++
+++ while (left >= len)
+++ {
+++ ncp = memchr(cp, *str, left);
+++
+++ if (ncp == NULL)
+++ return -1;
+++
+++ left -= (ncp - cp);
+++
+++ if (left < len)
+++ return -1;
+++
+++ cp = ncp;
+++
+++ if (memcmp(cp, str, len) == 0)
+++ return (cp - lp->data);
+++
+++ cp++;
+++ left--;
+++ }
+++
+++ return -1;
+++}
+++
+++
+++/*
+++ * Add lines which are typed in by the user.
+++ * The lines are inserted just before the specified line number.
+++ * The lines are terminated by a line containing a single dot (ugly!),
+++ * or by an end of file.
+++ */
+++static void
+++addLines(NUM num)
+++{
+++ int len;
+++ char buf[USERSIZE + 1];
+++
+++ while (fgets(buf, sizeof(buf), stdin))
+++ {
+++ if ((buf[0] == '.') && (buf[1] == '\n') && (buf[2] == '\0'))
+++ return;
+++
+++ len = strlen(buf);
+++
+++ if (len == 0)
+++ return;
+++
+++ if (buf[len - 1] != '\n')
+++ {
+++ bb_error_msg("Line too long");
+++
+++ do
+++ {
+++ len = fgetc(stdin);
+++ }
+++ while ((len != EOF) && (len != '\n'));
+++
+++ return;
+++ }
+++
+++ if (!insertLine(num++, buf, len))
+++ return;
+++ }
+++}
+++
+++
+++/*
+++ * Parse a line number argument if it is present. This is a sum
+++ * or difference of numbers, '.', '$', 'x, or a search string.
+++ * Returns TRUE if successful (whether or not there was a number).
+++ * Returns FALSE if there was a parsing error, with a message output.
+++ * Whether there was a number is returned indirectly, as is the number.
+++ * The character pointer which stopped the scan is also returned.
+++ */
+++static BOOL
+++getNum(const char ** retcp, BOOL * retHaveNum, NUM * retNum)
+++{
+++ const char * cp;
+++ char * endStr;
+++ char str[USERSIZE];
+++ BOOL haveNum;
+++ NUM value;
+++ NUM num;
+++ NUM sign;
+++
+++ cp = *retcp;
+++ haveNum = FALSE;
+++ value = 0;
+++ sign = 1;
+++
+++ while (TRUE)
+++ {
+++ while (isblank(*cp))
+++ cp++;
+++
+++ switch (*cp)
+++ {
+++ case '.':
+++ haveNum = TRUE;
+++ num = curNum;
+++ cp++;
+++ break;
+++
+++ case '$':
+++ haveNum = TRUE;
+++ num = lastNum;
+++ cp++;
+++ break;
+++
+++ case '\'':
+++ cp++;
+++
+++ if ((*cp < 'a') || (*cp > 'z'))
+++ {
+++ bb_error_msg("Bad mark name");
+++
+++ return FALSE;
+++ }
+++
+++ haveNum = TRUE;
+++ num = marks[*cp++ - 'a'];
+++ break;
+++
+++ case '/':
+++ strcpy(str, ++cp);
+++ endStr = strchr(str, '/');
+++
+++ if (endStr)
+++ {
+++ *endStr++ = '\0';
+++ cp += (endStr - str);
+++ }
+++ else
+++ cp = "";
+++
+++ num = searchLines(str, curNum, lastNum);
+++
+++ if (num == 0)
+++ return FALSE;
+++
+++ haveNum = TRUE;
+++ break;
+++
+++ default:
+++ if (!isdigit(*cp))
+++ {
+++ *retcp = cp;
+++ *retHaveNum = haveNum;
+++ *retNum = value;
+++
+++ return TRUE;
+++ }
+++
+++ num = 0;
+++
+++ while (isdigit(*cp))
+++ num = num * 10 + *cp++ - '0';
+++
+++ haveNum = TRUE;
+++ break;
+++ }
+++
+++ value += num * sign;
+++
+++ while (isblank(*cp))
+++ cp++;
+++
+++ switch (*cp)
+++ {
+++ case '-':
+++ sign = -1;
+++ cp++;
+++ break;
+++
+++ case '+':
+++ sign = 1;
+++ cp++;
+++ break;
+++
+++ default:
+++ *retcp = cp;
+++ *retHaveNum = haveNum;
+++ *retNum = value;
+++
+++ return TRUE;
+++ }
+++ }
+++}
+++
+++
+++/*
+++ * Initialize everything for editing.
+++ */
+++static BOOL
+++initEdit(void)
+++{
+++ int i;
+++
+++ bufSize = INITBUF_SIZE;
+++ bufBase = malloc(bufSize);
+++
+++ if (bufBase == NULL)
+++ {
+++ bb_error_msg("No memory for buffer");
+++
+++ return FALSE;
+++ }
+++
+++ bufPtr = bufBase;
+++ bufUsed = 0;
+++
+++ lines.next = &lines;
+++ lines.prev = &lines;
+++
+++ curLine = NULL;
+++ curNum = 0;
+++ lastNum = 0;
+++ dirty = FALSE;
+++ fileName = NULL;
+++ searchString[0] = '\0';
+++
+++ for (i = 0; i < 26; i++)
+++ marks[i] = 0;
+++
+++ return TRUE;
+++}
+++
+++
+++/*
+++ * Finish editing.
+++ */
+++static void
+++termEdit(void)
+++{
+++ if (bufBase)
+++ free(bufBase);
+++
+++ bufBase = NULL;
+++ bufPtr = NULL;
+++ bufSize = 0;
+++ bufUsed = 0;
+++
+++ if (fileName)
+++ free(fileName);
+++
+++ fileName = NULL;
+++
+++ searchString[0] = '\0';
+++
+++ if (lastNum)
+++ deleteLines(1, lastNum);
+++
+++ lastNum = 0;
+++ curNum = 0;
+++ curLine = NULL;
+++}
+++
+++
+++/*
+++ * Read lines from a file at the specified line number.
+++ * Returns TRUE if the file was successfully read.
+++ */
+++static BOOL
+++readLines(const char * file, NUM num)
+++{
+++ int fd;
+++ int cc;
+++ LEN len;
+++ LEN lineCount;
+++ LEN charCount;
+++ char * cp;
+++
+++ if ((num < 1) || (num > lastNum + 1))
+++ {
+++ bb_error_msg("Bad line for read");
+++
+++ return FALSE;
+++ }
+++
+++ fd = open(file, 0);
+++
+++ if (fd < 0)
+++ {
+++ perror(file);
+++
+++ return FALSE;
+++ }
+++
+++ bufPtr = bufBase;
+++ bufUsed = 0;
+++ lineCount = 0;
+++ charCount = 0;
+++ cc = 0;
+++
+++ printf("\"%s\", ", file);
+++ fflush(stdout);
+++
+++ do
+++ {
+++ cp = memchr(bufPtr, '\n', bufUsed);
+++
+++ if (cp)
+++ {
+++ len = (cp - bufPtr) + 1;
+++
+++ if (!insertLine(num, bufPtr, len))
+++ {
+++ close(fd);
+++
+++ return FALSE;
+++ }
+++
+++ bufPtr += len;
+++ bufUsed -= len;
+++ charCount += len;
+++ lineCount++;
+++ num++;
+++
+++ continue;
+++ }
+++
+++ if (bufPtr != bufBase)
+++ {
+++ memcpy(bufBase, bufPtr, bufUsed);
+++ bufPtr = bufBase + bufUsed;
+++ }
+++
+++ if (bufUsed >= bufSize)
+++ {
+++ len = (bufSize * 3) / 2;
+++ cp = realloc(bufBase, len);
+++
+++ if (cp == NULL)
+++ {
+++ bb_error_msg("No memory for buffer");
+++ close(fd);
+++
+++ return FALSE;
+++ }
+++
+++ bufBase = cp;
+++ bufPtr = bufBase + bufUsed;
+++ bufSize = len;
+++ }
+++
+++ cc = read(fd, bufPtr, bufSize - bufUsed);
+++ bufUsed += cc;
+++ bufPtr = bufBase;
+++
+++ }
+++ while (cc > 0);
+++
+++ if (cc < 0)
+++ {
+++ perror(file);
+++ close(fd);
+++
+++ return FALSE;
+++ }
+++
+++ if (bufUsed)
+++ {
+++ if (!insertLine(num, bufPtr, bufUsed))
+++ {
+++ close(fd);
+++
+++ return -1;
+++ }
+++
+++ lineCount++;
+++ charCount += bufUsed;
+++ }
+++
+++ close(fd);
+++
+++ printf("%d lines%s, %d chars\n", lineCount,
+++ (bufUsed ? " (incomplete)" : ""), charCount);
+++
+++ return TRUE;
+++}
+++
+++
+++/*
+++ * Write the specified lines out to the specified file.
+++ * Returns TRUE if successful, or FALSE on an error with a message output.
+++ */
+++static BOOL
+++writeLines(const char * file, NUM num1, NUM num2)
+++{
+++ int fd;
+++ LINE * lp;
+++ LEN lineCount;
+++ LEN charCount;
+++
+++ if ((num1 < 1) || (num2 > lastNum) || (num1 > num2))
+++ {
+++ bb_error_msg("Bad line range for write");
+++
+++ return FALSE;
+++ }
+++
+++ lineCount = 0;
+++ charCount = 0;
+++
+++ fd = creat(file, 0666);
+++
+++ if (fd < 0) {
+++ perror(file);
+++
+++ return FALSE;
+++ }
+++
+++ printf("\"%s\", ", file);
+++ fflush(stdout);
+++
+++ lp = findLine(num1);
+++
+++ if (lp == NULL)
+++ {
+++ close(fd);
+++
+++ return FALSE;
+++ }
+++
+++ while (num1++ <= num2)
+++ {
+++ if (write(fd, lp->data, lp->len) != lp->len)
+++ {
+++ perror(file);
+++ close(fd);
+++
+++ return FALSE;
+++ }
+++
+++ charCount += lp->len;
+++ lineCount++;
+++ lp = lp->next;
+++ }
+++
+++ if (close(fd) < 0)
+++ {
+++ perror(file);
+++
+++ return FALSE;
+++ }
+++
+++ printf("%d lines, %d chars\n", lineCount, charCount);
+++
+++ return TRUE;
+++}
+++
+++
+++/*
+++ * Print lines in a specified range.
+++ * The last line printed becomes the current line.
+++ * If expandFlag is TRUE, then the line is printed specially to
+++ * show magic characters.
+++ */
+++static BOOL
+++printLines(NUM num1, NUM num2, BOOL expandFlag)
+++{
+++ const LINE * lp;
+++ const unsigned char * cp;
+++ int ch;
+++ LEN count;
+++
+++ if ((num1 < 1) || (num2 > lastNum) || (num1 > num2))
+++ {
+++ bb_error_msg("Bad line range for print");
+++
+++ return FALSE;
+++ }
+++
+++ lp = findLine(num1);
+++
+++ if (lp == NULL)
+++ return FALSE;
+++
+++ while (num1 <= num2)
+++ {
+++ if (!expandFlag)
+++ {
+++ write(1, lp->data, lp->len);
+++ setCurNum(num1++);
+++ lp = lp->next;
+++
+++ continue;
+++ }
+++
+++ /*
+++ * Show control characters and characters with the
+++ * high bit set specially.
+++ */
+++ cp = lp->data;
+++ count = lp->len;
+++
+++ if ((count > 0) && (cp[count - 1] == '\n'))
+++ count--;
+++
+++ while (count-- > 0)
+++ {
+++ ch = *cp++;
+++
+++ if (ch & 0x80)
+++ {
+++ fputs("M-", stdout);
+++ ch &= 0x7f;
+++ }
+++
+++ if (ch < ' ')
+++ {
+++ fputc('^', stdout);
+++ ch += '@';
+++ }
+++
+++ if (ch == 0x7f)
+++ {
+++ fputc('^', stdout);
+++ ch = '?';
+++ }
+++
+++ fputc(ch, stdout);
+++ }
+++
+++ fputs("$\n", stdout);
+++
+++ setCurNum(num1++);
+++ lp = lp->next;
+++ }
+++
+++ return TRUE;
+++}
+++
+++
+++/*
+++ * Insert a new line with the specified text.
+++ * The line is inserted so as to become the specified line,
+++ * thus pushing any existing and further lines down one.
+++ * The inserted line is also set to become the current line.
+++ * Returns TRUE if successful.
+++ */
+++static BOOL
+++insertLine(NUM num, const char * data, LEN len)
+++{
+++ LINE * newLp;
+++ LINE * lp;
+++
+++ if ((num < 1) || (num > lastNum + 1))
+++ {
+++ bb_error_msg("Inserting at bad line number");
+++
+++ return FALSE;
+++ }
+++
+++ newLp = (LINE *) malloc(sizeof(LINE) + len - 1);
+++
+++ if (newLp == NULL)
+++ {
+++ bb_error_msg("Failed to allocate memory for line");
+++
+++ return FALSE;
+++ }
+++
+++ memcpy(newLp->data, data, len);
+++ newLp->len = len;
+++
+++ if (num > lastNum)
+++ lp = &lines;
+++ else
+++ {
+++ lp = findLine(num);
+++
+++ if (lp == NULL)
+++ {
+++ free((char *) newLp);
+++
+++ return FALSE;
+++ }
+++ }
+++
+++ newLp->next = lp;
+++ newLp->prev = lp->prev;
+++ lp->prev->next = newLp;
+++ lp->prev = newLp;
+++
+++ lastNum++;
+++ dirty = TRUE;
+++
+++ return setCurNum(num);
+++}
+++
+++
+++/*
+++ * Delete lines from the given range.
+++ */
+++static BOOL
+++deleteLines(NUM num1, NUM num2)
+++{
+++ LINE * lp;
+++ LINE * nlp;
+++ LINE * plp;
+++ NUM count;
+++
+++ if ((num1 < 1) || (num2 > lastNum) || (num1 > num2))
+++ {
+++ bb_error_msg("Bad line numbers for delete");
+++
+++ return FALSE;
+++ }
+++
+++ lp = findLine(num1);
+++
+++ if (lp == NULL)
+++ return FALSE;
+++
+++ if ((curNum >= num1) && (curNum <= num2))
+++ {
+++ if (num2 < lastNum)
+++ setCurNum(num2 + 1);
+++ else if (num1 > 1)
+++ setCurNum(num1 - 1);
+++ else
+++ curNum = 0;
+++ }
+++
+++ count = num2 - num1 + 1;
+++
+++ if (curNum > num2)
+++ curNum -= count;
+++
+++ lastNum -= count;
+++
+++ while (count-- > 0)
+++ {
+++ nlp = lp->next;
+++ plp = lp->prev;
+++ plp->next = nlp;
+++ nlp->prev = plp;
+++ lp->next = NULL;
+++ lp->prev = NULL;
+++ lp->len = 0;
+++ free(lp);
+++ lp = nlp;
+++ }
+++
+++ dirty = TRUE;
+++
+++ return TRUE;
+++}
+++
+++
+++/*
+++ * Search for a line which contains the specified string.
+++ * If the string is NULL, then the previously searched for string
+++ * is used. The currently searched for string is saved for future use.
+++ * Returns the line number which matches, or 0 if there was no match
+++ * with an error printed.
+++ */
+++static NUM
+++searchLines(const char * str, NUM num1, NUM num2)
+++{
+++ const LINE * lp;
+++ int len;
+++
+++ if ((num1 < 1) || (num2 > lastNum) || (num1 > num2))
+++ {
+++ bb_error_msg("Bad line numbers for search");
+++
+++ return 0;
+++ }
+++
+++ if (*str == '\0')
+++ {
+++ if (searchString[0] == '\0')
+++ {
+++ bb_error_msg("No previous search string");
+++
+++ return 0;
+++ }
+++
+++ str = searchString;
+++ }
+++
+++ if (str != searchString)
+++ strcpy(searchString, str);
+++
+++ len = strlen(str);
+++
+++ lp = findLine(num1);
+++
+++ if (lp == NULL)
+++ return 0;
+++
+++ while (num1 <= num2)
+++ {
+++ if (findString(lp, str, len, 0) >= 0)
+++ return num1;
+++
+++ num1++;
+++ lp = lp->next;
+++ }
+++
+++ bb_error_msg("Cannot find string \"%s\"", str);
+++
+++ return 0;
+++}
+++
+++
+++/*
+++ * Return a pointer to the specified line number.
+++ */
+++static LINE *
+++findLine(NUM num)
+++{
+++ LINE * lp;
+++ NUM lnum;
+++
+++ if ((num < 1) || (num > lastNum))
+++ {
+++ bb_error_msg("Line number %d does not exist", num);
+++
+++ return NULL;
+++ }
+++
+++ if (curNum <= 0)
+++ {
+++ curNum = 1;
+++ curLine = lines.next;
+++ }
+++
+++ if (num == curNum)
+++ return curLine;
+++
+++ lp = curLine;
+++ lnum = curNum;
+++
+++ if (num < (curNum / 2))
+++ {
+++ lp = lines.next;
+++ lnum = 1;
+++ }
+++ else if (num > ((curNum + lastNum) / 2))
+++ {
+++ lp = lines.prev;
+++ lnum = lastNum;
+++ }
+++
+++ while (lnum < num)
+++ {
+++ lp = lp->next;
+++ lnum++;
+++ }
+++
+++ while (lnum > num)
+++ {
+++ lp = lp->prev;
+++ lnum--;
+++ }
+++
+++ return lp;
+++}
+++
+++
+++/*
+++ * Set the current line number.
+++ * Returns TRUE if successful.
+++ */
+++static BOOL
+++setCurNum(NUM num)
+++{
+++ LINE * lp;
+++
+++ lp = findLine(num);
+++
+++ if (lp == NULL)
+++ return FALSE;
+++
+++ curNum = num;
+++ curLine = lp;
+++
+++ return TRUE;
+++}
+++
+++/* END CODE */
+diff -Nur busybox-1.00/patches/eject.diff busybox/patches/eject.diff
+--- busybox-1.00/patches/eject.diff 2004-03-15 09:29:02.000000000 +0100
++++ busybox/patches/eject.diff 1970-01-01 01:00:00.000000000 +0100
+@@ -1,164 +0,0 @@
+-Index: AUTHORS
+-===================================================================
+-RCS file: /var/cvs/busybox/AUTHORS,v
+-retrieving revision 1.40
+-diff -u -r1.40 AUTHORS
+---- a/AUTHORS 9 Oct 2003 21:19:21 -0000 1.40
+-+++ b/AUTHORS 5 Mar 2004 07:23:17 -0000
+-@@ -8,6 +8,9 @@
+-
+- -----------
+-
+-+Peter Willis <psyphreak@phreaker.net>
+-+ eject
+-+
+- Emanuele Aina <emanuele.aina@tiscali.it>
+- run-parts
+-
+-Index: coreutils/Config.in
+-===================================================================
+-RCS file: /var/cvs/busybox/coreutils/Config.in,v
+-retrieving revision 1.23
+-diff -u -r1.23 Config.in
+---- a/coreutils/Config.in 5 Mar 2004 06:47:25 -0000 1.23
+-+++ b/coreutils/Config.in 5 Mar 2004 07:23:18 -0000
+-@@ -164,6 +164,13 @@
+- a command; without options it displays the current
+- environment.
+-
+-+config CONFIG_EJECT
+-+ bool "eject"
+-+ default n
+-+ help
+-+ ejects a cdrom drive.
+-+ defaults to /dev/cdrom
+-+
+- config CONFIG_EXPR
+- bool "expr"
+- default n
+-Index: coreutils/Makefile.in
+-===================================================================
+-RCS file: /var/cvs/busybox/coreutils/Makefile.in,v
+-retrieving revision 1.8
+-diff -u -r1.8 Makefile.in
+---- a/coreutils/Makefile.in 27 Jan 2004 09:22:20 -0000 1.8
+-+++ b/coreutils/Makefile.in 5 Mar 2004 07:23:18 -0000
+-@@ -41,6 +41,7 @@
+- COREUTILS-$(CONFIG_DU) += du.o
+- COREUTILS-$(CONFIG_ECHO) += echo.o
+- COREUTILS-$(CONFIG_ENV) += env.o
+-+COREUTILS-$(CONFIG_EJECT) += eject.o
+- COREUTILS-$(CONFIG_EXPR) += expr.o
+- COREUTILS-$(CONFIG_FALSE) += false.o
+- COREUTILS-$(CONFIG_FOLD) += fold.o
+-Index: coreutils/eject.c
+-===================================================================
+-RCS file: coreutils/eject.c
+-diff -N coreutils/eject.c
+---- /dev/null 1 Jan 1970 00:00:00 -0000
+-+++ b/coreutils/eject.c 5 Mar 2004 07:23:21 -0000
+-@@ -0,0 +1,66 @@
+-+/*
+-+ * eject implementation for busybox
+-+ *
+-+ * Copyright (C) 2004 Peter Willis <psyphreak@phreaker.net>
+-+ *
+-+ * 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
+-+ *
+-+ */
+-+
+-+/*
+-+ * This is a simple hack of eject based on something Erik posted in #uclibc.
+-+ * Most of the dirty work blatantly ripped off from cat.c =)
+-+ */
+-+
+-+#include <stdio.h>
+-+#include <string.h>
+-+#include <sys/types.h>
+-+#include <sys/stat.h>
+-+#include <fcntl.h>
+-+#include <sys/ioctl.h>
+-+#include "busybox.h"
+-+#include <linux/cdrom.h> // needs to be after busybox.h or compile problems arise
+-+
+-+#define DEFAULT_CDROM "/dev/cdrom"
+-+
+-+extern int eject_main(int argc, char **argv)
+-+{
+-+ int fd;
+-+ int flag = CDROMEJECT;
+-+ int i = 1;
+-+ char *device = NULL;
+-+
+-+ /*
+-+ * i'm too lazy to learn bb_getopt_ulflags and this is obscenely large
+-+ * for just some argument parsing so mjn3 can clean it up later.
+-+ * sorry, but PlumpOS 7.0-pre2 needs this asap :-/
+-+ */
+-+ while (++i <= argc) {
+-+ if ( (! strncmp(argv[i-1],"-t",2)) || (! strncmp(argv[i-1],"--trayclose",11)) ) {
+-+ flag = CDROMCLOSETRAY;
+-+ } else {
+-+ device = argv[i-1];
+-+ }
+-+ }
+-+ if ( (fd = open(device == NULL ? DEFAULT_CDROM : device, O_RDONLY | O_NONBLOCK) ) < 0 ) {
+-+ perror("eject: Can't open device");
+-+ return(EXIT_FAILURE);
+-+ }
+-+ if (ioctl(fd, flag)) {
+-+ perror("eject: Can't eject cdrom");
+-+ return(EXIT_FAILURE);
+-+ }
+-+ return EXIT_SUCCESS;
+-+}
+-Index: include/applets.h
+-===================================================================
+-RCS file: /var/cvs/busybox/include/applets.h,v
+-retrieving revision 1.111
+-diff -u -r1.111 applets.h
+---- a/include/applets.h 27 Jan 2004 09:22:20 -0000 1.111
+-+++ b/include/applets.h 5 Mar 2004 07:23:21 -0000
+-@@ -178,6 +178,9 @@
+- #if defined(CONFIG_FEATURE_GREP_EGREP_ALIAS)
+- APPLET_NOUSAGE("egrep", grep_main, _BB_DIR_BIN, _BB_SUID_NEVER)
+- #endif
+-+#ifdef CONFIG_EJECT
+-+ APPLET(eject, eject_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+-+#endif
+- #ifdef CONFIG_ENV
+- APPLET(env, env_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
+- #endif
+-Index: include/usage.h
+-===================================================================
+-RCS file: /var/cvs/busybox/include/usage.h,v
+-retrieving revision 1.191
+-diff -u -r1.191 usage.h
+---- a/include/usage.h 25 Feb 2004 10:35:55 -0000 1.191
+-+++ b/include/usage.h 5 Mar 2004 07:23:29 -0000
+-@@ -537,6 +537,13 @@
+- "\t-, -i\tstart with an empty environment\n" \
+- "\t-u\tremove variable from the environment\n"
+-
+-+#define eject_trivial_usage \
+-+ "[-t] [FILE]"
+-+#define eject_full_usage \
+-+ "Ejects the specified FILE or /dev/cdrom if FILE is unspecified.\n\n" \
+-+ "Options:\n" \
+-+ "\t-t, --trayclose \tclose tray\n"
+-+
+- #define expr_trivial_usage \
+- "EXPRESSION"
+- #define expr_full_usage \
+diff -Nur busybox-1.00/procps/ps.c busybox/procps/ps.c
+--- busybox-1.00/procps/ps.c 2004-03-15 09:29:03.000000000 +0100
++++ busybox/procps/ps.c 2005-06-04 08:20:20.000000000 +0200
+@@ -31,9 +31,7 @@
+ #include <sys/ioctl.h>
+ #include "busybox.h"
+ #ifdef CONFIG_SELINUX
+-#include <fs_secure.h>
+-#include <ss.h>
+-#include <flask_util.h> /* for is_flask_enabled() */
++#include <selinux/selinux.h> /* for is_selinux_enabled() */
+ #endif
+
+ static const int TERMINAL_WIDTH = 79; /* not 80 in case terminal has linefold bug */
+@@ -48,8 +46,8 @@
+
+ #ifdef CONFIG_SELINUX
+ int use_selinux = 0;
+- security_id_t sid;
+- if(is_flask_enabled() && argv[1] && !strcmp(argv[1], "-c") )
++ security_context_t sid=NULL;
++ if(is_selinux_enabled() && argv[1] && !strcmp(argv[1], "-c") )
+ use_selinux = 1;
+ #endif
+
+@@ -58,34 +56,42 @@
+ terminal_width--;
+
+ #ifdef CONFIG_SELINUX
+- if(use_selinux)
+- printf(" PID Context Stat Command\n");
++ if (use_selinux)
++ printf(" PID Context Stat Command\n");
+ else
+ #endif
+- printf(" PID Uid VmSize Stat Command\n");
+-#ifdef CONFIG_SELINUX
+- while ((p = procps_scan(1, use_selinux, &sid)) != 0) {
+-#else
+- while ((p = procps_scan(1)) != 0) {
+-#endif
+- char *namecmd = p->cmd;
++ printf(" PID Uid VmSize Stat Command\n");
+
++ while ((p = procps_scan(1)) != 0) {
++ char *namecmd = p->cmd;
+ #ifdef CONFIG_SELINUX
+- if(use_selinux)
+- {
++ if ( use_selinux )
++ {
+ char sbuf[128];
+ len = sizeof(sbuf);
+- if(security_sid_to_context(sid, (security_context_t)&sbuf, &len))
+- strcpy(sbuf, "unknown");
+
++ if (is_selinux_enabled()) {
++ if (getpidcon(p->pid,&sid)<0)
++ sid=NULL;
++ }
++
++ if (sid) {
++ /* I assume sid initilized with NULL */
++ len = strlen(sid)+1;
++ safe_strncpy(sbuf, sid, len);
++ freecon(sid);
++ sid=NULL;
++ }else {
++ safe_strncpy(sbuf, "unknown",7);
++ }
+ len = printf("%5d %-32s %s ", p->pid, sbuf, p->state);
+- }
++ }
+ else
+ #endif
+- if(p->rss == 0)
+- len = printf("%5d %-8s %s ", p->pid, p->user, p->state);
+- else
+- len = printf("%5d %-8s %6ld %s ", p->pid, p->user, p->rss, p->state);
++ if(p->rss == 0)
++ len = printf("%5d %-8s %s ", p->pid, p->user, p->state);
++ else
++ len = printf("%5d %-8s %6ld %s ", p->pid, p->user, p->rss, p->state);
+ i = terminal_width-len;
+
+ if(namecmd != 0 && namecmd[0] != 0) {
+diff -Nur busybox-1.00/procps/renice.c busybox/procps/renice.c
+--- busybox-1.00/procps/renice.c 2004-03-15 09:29:03.000000000 +0100
++++ busybox/procps/renice.c 2005-06-04 08:20:20.000000000 +0200
+@@ -1,8 +1,8 @@
++/* vi: set sw=4 ts=4: */
+ /*
+- * Mini renice implementation for busybox
++ * renice implementation for busybox
+ *
+- *
+- * Copyright (C) 2000 Dave 'Kill a Cop' Cinege <dcinege@psychosis.com>
++ * Copyright (C) 2005 Manuel Novoa III <mjn3@codepoet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -20,35 +20,133 @@
+ *
+ */
+
++/* Notes:
++ * Setting an absolute priority was obsoleted in SUSv2 and removed
++ * in SUSv3. However, the common linux version of renice does
++ * absolute and not relative. So we'll continue supporting absolute,
++ * although the stdout logging has been removed since both SUSv2 and
++ * SUSv3 specify that stdout isn't used.
++ *
++ * This version is lenient in that it doesn't require any IDs. The
++ * options -p, -g, and -u are treated as mode switches for the
++ * following IDs (if any). Multiple switches are allowed.
++ */
++
+ #include <stdio.h>
+-#include <errno.h>
+ #include <stdlib.h>
++#include <string.h>
++#include <limits.h>
++#include <errno.h>
++#include <unistd.h>
+ #include <sys/time.h>
+ #include <sys/resource.h>
+ #include "busybox.h"
+
++#if (PRIO_PROCESS < CHAR_MIN) || (PRIO_PROCESS > CHAR_MAX)
++#error Assumption violated : PRIO_PROCESS value
++#endif
++#if (PRIO_PGRP < CHAR_MIN) || (PRIO_PGRP > CHAR_MAX)
++#error Assumption violated : PRIO_PGRP value
++#endif
++#if (PRIO_USER < CHAR_MIN) || (PRIO_USER > CHAR_MAX)
++#error Assumption violated : PRIO_USER value
++#endif
+
+-extern int renice_main(int argc, char **argv)
++static inline int int_add_no_wrap(int a, int b)
+ {
+- int prio, status = EXIT_SUCCESS;
++ int s = a + b;
++
++ if (b < 0) {
++ if (s > a) s = INT_MIN;
++ } else {
++ if (s < a) s = INT_MAX;
++ }
++
++ return s;
++}
+
+- if (argc < 3) bb_show_usage();
++int renice_main(int argc, char **argv)
++{
++ static const char Xetpriority_msg[] = "%d : %cetpriority";
+
+- prio = atoi(*++argv);
+- if (prio > 20) prio = 20;
+- if (prio < -20) prio = -20;
++ int retval = EXIT_SUCCESS;
++ int which = PRIO_PROCESS; /* Default 'which' value. */
++ int use_relative = 0;
++ int adjustment, new_priority;
++ id_t who;
++
++ ++argv;
++
++ /* Check if we are using a relative adjustment. */
++ if (argv[0] && (argv[0][0] == '-') && (argv[0][1] == 'n') && !argv[0][2]) {
++ use_relative = 1;
++ ++argv;
++ }
++
++ if (!*argv) { /* No args? Then show usage. */
++ bb_show_usage();
++ }
++
++ /* Get the priority adjustment (absolute or relative). */
++ adjustment = bb_xgetlarg(*argv, 10, INT_MIN, INT_MAX);
+
+ while (*++argv) {
+- int ps = atoi(*argv);
+- int oldp = getpriority(PRIO_PROCESS, ps);
++ /* Check for a mode switch. */
++ if ((argv[0][0] == '-') && argv[0][1] && !argv[0][2]) {
++ static const char opts[]
++ = { 'p', 'g', 'u', 0, PRIO_PROCESS, PRIO_PGRP, PRIO_USER };
++ const char *p;
++ if ((p = strchr(opts, argv[0][1]))) {
++ which = p[4];
++ continue;
++ }
++ }
+
+- if (setpriority(PRIO_PROCESS, ps, prio) == 0) {
+- printf("%d: old priority %d, new priority %d\n", ps, oldp, prio );
++ /* Process an ID arg. */
++ if (which == PRIO_USER) {
++ struct passwd *p;
++ if (!(p = getpwnam(*argv))) {
++ bb_error_msg("unknown user: %s", *argv);
++ goto HAD_ERROR;
++ }
++ who = p->pw_uid;
+ } else {
+- bb_perror_msg("%d: setpriority", ps);
+- status = EXIT_FAILURE;
++ char *e;
++ errno = 0;
++ who = strtoul(*argv, &e, 10);
++ if (*e || (*argv == e) || errno) {
++ bb_error_msg("bad value: %s", *argv);
++ goto HAD_ERROR;
++ }
+ }
++
++ /* Get priority to use, and set it. */
++ if (use_relative) {
++ int old_priority;
++
++ errno = 0; /* Needed for getpriority error detection. */
++ old_priority = getpriority(which, who);
++ if (errno) {
++ bb_perror_msg(Xetpriority_msg, who, 'g');
++ goto HAD_ERROR;
++ }
++
++ new_priority = int_add_no_wrap(old_priority, adjustment);
++ } else {
++ new_priority = adjustment;
++ }
++
++ if (setpriority(which, who, new_priority) == 0) {
++ continue;
++ }
++
++ bb_perror_msg(Xetpriority_msg, who, 's');
++ HAD_ERROR:
++ retval = EXIT_FAILURE;
+ }
+
+- return status;
++ /* No need to check for errors outputing to stderr since, if it
++ * was used, the HAD_ERROR label was reached and retval was set. */
++
++ return retval;
+ }
+diff -Nur busybox-1.00/procps/top.c busybox/procps/top.c
+--- busybox-1.00/procps/top.c 2004-09-14 21:14:00.000000000 +0200
++++ busybox/procps/top.c 2005-06-04 08:20:20.000000000 +0200
+@@ -78,7 +78,7 @@
+ return (int)((Q->stime + Q->utime) - (P->stime + P->utime));
+ }
+
+-int mult_lvl_cmp(void* a, void* b) {
++static int mult_lvl_cmp(void* a, void* b) {
+ int i, cmp_val;
+
+ for(i = 0; i < sort_depth; i++) {
+@@ -510,11 +510,7 @@
+ /* read process IDs & status for all the processes */
+ procps_status_t * p;
+
+-#ifdef CONFIG_SELINUX
+- while ((p = procps_scan(0, 0, NULL) ) != 0) {
+-#else
+ while ((p = procps_scan(0)) != 0) {
+-#endif
+ int n = ntop;
+
+ top = xrealloc(top, (++ntop)*sizeof(procps_status_t));
+diff -Nur busybox-1.00/scripts/config/Makefile busybox/scripts/config/Makefile
+--- busybox-1.00/scripts/config/Makefile 2004-10-08 09:45:49.000000000 +0200
++++ busybox/scripts/config/Makefile 2005-06-04 08:20:03.000000000 +0200
+@@ -9,7 +9,11 @@
+
+ all: ncurses conf mconf
+
++ifeq ($(shell uname),SunOS)
++LIBS = -lcurses
++else
+ LIBS = -lncurses
++endif
+ ifeq (/usr/include/ncurses/ncurses.h, $(wildcard /usr/include/ncurses/ncurses.h))
+ HOSTNCURSES += -I/usr/include/ncurses -DCURSES_LOC="<ncurses.h>"
+ else
+@@ -32,14 +36,17 @@
+ endif
+ endif
+
+-CONF_SRC =conf.c
+-MCONF_SRC =mconf.c checklist.c menubox.c textbox.c yesno.c inputbox.c util.c msgbox.c
+-SHARED_SRC=zconf.tab.c
+-SHARED_DEPS:=$(srcdir)/lkc.h $(srcdir)/lkc_proto.h \
+- lkc_defs.h $(srcdir)/expr.h zconf.tab.h
+-CONF_OBJS =$(patsubst %.c,%.o, $(CONF_SRC))
+-MCONF_OBJS=$(patsubst %.c,%.o, $(MCONF_SRC))
+-SHARED_OBJS=$(patsubst %.c,%.o, $(SHARED_SRC))
++CONF_SRC = conf.c
++MCONF_SRC = mconf.c
++LXD_SRC = lxdialog/checklist.c lxdialog/menubox.c lxdialog/textbox.c \
++ lxdialog/yesno.c lxdialog/inputbox.c lxdialog/util.c \
++ lxdialog/msgbox.c
++SHARED_SRC = zconf.tab.c
++SHARED_DEPS := $(srcdir)/lkc.h $(srcdir)/lkc_proto.h \
++ lkc_defs.h $(srcdir)/expr.h zconf.tab.h
++CONF_OBJS = $(patsubst %.c,%.o, $(CONF_SRC))
++MCONF_OBJS = $(patsubst %.c,%.o, $(MCONF_SRC) $(LXD_SRC))
++SHARED_OBJS = $(patsubst %.c,%.o, $(SHARED_SRC))
+
+ conf: $(CONF_OBJS) $(SHARED_OBJS)
+ $(HOSTCC) $(NATIVE_LDFLAGS) $^ -o $@
+diff -Nur busybox-1.00/scripts/config/checklist.c busybox/scripts/config/checklist.c
+--- busybox-1.00/scripts/config/checklist.c 2004-07-15 08:01:05.000000000 +0200
++++ busybox/scripts/config/checklist.c 1970-01-01 01:00:00.000000000 +0100
+@@ -1,372 +0,0 @@
+-/*
+- * checklist.c -- implements the checklist box
+- *
+- * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
+- * Stuart Herbert - S.Herbert@sheffield.ac.uk: radiolist extension
+- * Alessandro Rubini - rubini@ipvvis.unipv.it: merged the two
+- * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com)
+- *
+- * This program is free software; you can redistribute it and/or
+- * modify it under the terms of the GNU General Public License
+- * as published by the Free Software Foundation; either version 2
+- * of the License, or (at your option) any later version.
+- *
+- * This program is distributed in the hope that it will be useful,
+- * but WITHOUT ANY WARRANTY; without even the implied warranty of
+- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+- * GNU General Public License for more details.
+- *
+- * You should have received a copy of the GNU General Public License
+- * along with this program; if not, write to the Free Software
+- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+- */
+-
+-#include "dialog.h"
+-
+-static int list_width, check_x, item_x, checkflag;
+-
+-/*
+- * Print list item
+- */
+-static void
+-print_item (WINDOW * win, const char *item, int status,
+- int choice, int selected)
+-{
+- int i;
+-
+- /* Clear 'residue' of last item */
+- wattrset (win, menubox_attr);
+- wmove (win, choice, 0);
+- for (i = 0; i < list_width; i++)
+- waddch (win, ' ');
+-
+- wmove (win, choice, check_x);
+- wattrset (win, selected ? check_selected_attr : check_attr);
+- if (checkflag == FLAG_CHECK)
+- wprintw (win, "[%c]", status ? 'X' : ' ');
+- else
+- wprintw (win, "(%c)", status ? 'X' : ' ');
+-
+- wattrset (win, selected ? tag_selected_attr : tag_attr);
+- mvwaddch(win, choice, item_x, item[0]);
+- wattrset (win, selected ? item_selected_attr : item_attr);
+- waddstr (win, (char *)item+1);
+- if (selected) {
+- wmove (win, choice, check_x+1);
+- wrefresh (win);
+- }
+-}
+-
+-/*
+- * Print the scroll indicators.
+- */
+-static void
+-print_arrows (WINDOW * win, int choice, int item_no, int scroll,
+- int y, int x, int height)
+-{
+- wmove(win, y, x);
+-
+- if (scroll > 0) {
+- wattrset (win, uarrow_attr);
+- waddch (win, ACS_UARROW);
+- waddstr (win, "(-)");
+- }
+- else {
+- wattrset (win, menubox_attr);
+- waddch (win, ACS_HLINE);
+- waddch (win, ACS_HLINE);
+- waddch (win, ACS_HLINE);
+- waddch (win, ACS_HLINE);
+- }
+-
+- y = y + height + 1;
+- wmove(win, y, x);
+-
+- if ((height < item_no) && (scroll + choice < item_no - 1)) {
+- wattrset (win, darrow_attr);
+- waddch (win, ACS_DARROW);
+- waddstr (win, "(+)");
+- }
+- else {
+- wattrset (win, menubox_border_attr);
+- waddch (win, ACS_HLINE);
+- waddch (win, ACS_HLINE);
+- waddch (win, ACS_HLINE);
+- waddch (win, ACS_HLINE);
+- }
+-}
+-
+-/*
+- * Display the termination buttons
+- */
+-static void
+-print_buttons( WINDOW *dialog, int height, int width, int selected)
+-{
+- int x = width / 2 - 11;
+- int y = height - 2;
+-
+- print_button (dialog, "Select", y, x, selected == 0);
+- print_button (dialog, " Help ", y, x + 14, selected == 1);
+-
+- wmove(dialog, y, x+1 + 14*selected);
+- wrefresh (dialog);
+-}
+-
+-/*
+- * Display a dialog box with a list of options that can be turned on or off
+- * The `flag' parameter is used to select between radiolist and checklist.
+- */
+-int
+-dialog_checklist (const char *title, const char *prompt, int height, int width,
+- int list_height, int item_no, struct dialog_list_item ** items,
+- int flag)
+-
+-{
+- int i, x, y, box_x, box_y;
+- int key = 0, button = 0, choice = 0, scroll = 0, max_choice, *status;
+- WINDOW *dialog, *list;
+-
+- checkflag = flag;
+-
+- /* Allocate space for storing item on/off status */
+- if ((status = malloc (sizeof (int) * item_no)) == NULL) {
+- endwin ();
+- fprintf (stderr,
+- "\nCan't allocate memory in dialog_checklist().\n");
+- exit (-1);
+- }
+-
+- /* Initializes status */
+- for (i = 0; i < item_no; i++) {
+- status[i] = (items[i]->selected == 1); /* ON */
+- if ((!choice && status[i]) || items[i]->selected == 2) /* SELECTED */
+- choice = i + 1;
+- }
+- if (choice)
+- choice--;
+-
+- max_choice = MIN (list_height, item_no);
+-
+- /* center dialog box on screen */
+- x = (COLS - width) / 2;
+- y = (LINES - height) / 2;
+-
+- draw_shadow (stdscr, y, x, height, width);
+-
+- dialog = newwin (height, width, y, x);
+- keypad (dialog, TRUE);
+-
+- draw_box (dialog, 0, 0, height, width, dialog_attr, border_attr);
+- wattrset (dialog, border_attr);
+- mvwaddch (dialog, height-3, 0, ACS_LTEE);
+- for (i = 0; i < width - 2; i++)
+- waddch (dialog, ACS_HLINE);
+- wattrset (dialog, dialog_attr);
+- waddch (dialog, ACS_RTEE);
+-
+- if (title != NULL && strlen(title) >= width-2 ) {
+- /* truncate long title -- mec */
+- char * title2 = malloc(width-2+1);
+- memcpy( title2, title, width-2 );
+- title2[width-2] = '\0';
+- title = title2;
+- }
+-
+- if (title != NULL) {
+- wattrset (dialog, title_attr);
+- mvwaddch (dialog, 0, (width - strlen(title))/2 - 1, ' ');
+- waddstr (dialog, (char *)title);
+- waddch (dialog, ' ');
+- }
+-
+- wattrset (dialog, dialog_attr);
+- print_autowrap (dialog, prompt, width - 2, 1, 3);
+-
+- list_width = width - 6;
+- box_y = height - list_height - 5;
+- box_x = (width - list_width) / 2 - 1;
+-
+- /* create new window for the list */
+- list = subwin (dialog, list_height, list_width, y+box_y+1, x+box_x+1);
+-
+- keypad (list, TRUE);
+-
+- /* draw a box around the list items */
+- draw_box (dialog, box_y, box_x, list_height + 2, list_width + 2,
+- menubox_border_attr, menubox_attr);
+-
+- /* Find length of longest item in order to center checklist */
+- check_x = 0;
+- for (i = 0; i < item_no; i++)
+- check_x = MAX (check_x, + strlen (items[i]->name) + 4);
+-
+- check_x = (list_width - check_x) / 2;
+- item_x = check_x + 4;
+-
+- if (choice >= list_height) {
+- scroll = choice - list_height + 1;
+- choice -= scroll;
+- }
+-
+- /* Print the list */
+- for (i = 0; i < max_choice; i++) {
+- print_item (list, items[scroll + i]->name,
+- status[i+scroll], i, i == choice);
+- }
+-
+- print_arrows(dialog, choice, item_no, scroll,
+- box_y, box_x + check_x + 5, list_height);
+-
+- print_buttons(dialog, height, width, 0);
+-
+- wnoutrefresh (list);
+- wnoutrefresh (dialog);
+- doupdate ();
+-
+- while (key != ESC) {
+- key = wgetch (dialog);
+-
+- for (i = 0; i < max_choice; i++)
+- if (toupper(key) == toupper(items[scroll + i]->name[0]))
+- break;
+-
+-
+- if ( i < max_choice || key == KEY_UP || key == KEY_DOWN ||
+- key == '+' || key == '-' ) {
+- if (key == KEY_UP || key == '-') {
+- if (!choice) {
+- if (!scroll)
+- continue;
+- /* Scroll list down */
+- if (list_height > 1) {
+- /* De-highlight current first item */
+- print_item (list, items[scroll]->name,
+- status[scroll], 0, FALSE);
+- scrollok (list, TRUE);
+- wscrl (list, -1);
+- scrollok (list, FALSE);
+- }
+- scroll--;
+- print_item (list, items[scroll]->name,
+- status[scroll], 0, TRUE);
+- wnoutrefresh (list);
+-
+- print_arrows(dialog, choice, item_no, scroll,
+- box_y, box_x + check_x + 5, list_height);
+-
+- wrefresh (dialog);
+-
+- continue; /* wait for another key press */
+- } else
+- i = choice - 1;
+- } else if (key == KEY_DOWN || key == '+') {
+- if (choice == max_choice - 1) {
+- if (scroll + choice >= item_no - 1)
+- continue;
+- /* Scroll list up */
+- if (list_height > 1) {
+- /* De-highlight current last item before scrolling up */
+- print_item (list, items[scroll + max_choice - 1]->name,
+- status[scroll + max_choice - 1],
+- max_choice - 1, FALSE);
+- scrollok (list, TRUE);
+- scroll (list);
+- scrollok (list, FALSE);
+- }
+- scroll++;
+- print_item (list, items[scroll + max_choice - 1]->name,
+- status[scroll + max_choice - 1],
+- max_choice - 1, TRUE);
+- wnoutrefresh (list);
+-
+- print_arrows(dialog, choice, item_no, scroll,
+- box_y, box_x + check_x + 5, list_height);
+-
+- wrefresh (dialog);
+-
+- continue; /* wait for another key press */
+- } else
+- i = choice + 1;
+- }
+- if (i != choice) {
+- /* De-highlight current item */
+- print_item (list, items[scroll + choice]->name,
+- status[scroll + choice], choice, FALSE);
+- /* Highlight new item */
+- choice = i;
+- print_item (list, items[scroll + choice]->name,
+- status[scroll + choice], choice, TRUE);
+- wnoutrefresh (list);
+- wrefresh (dialog);
+- }
+- continue; /* wait for another key press */
+- }
+- switch (key) {
+- case 'H':
+- case 'h':
+- case '?':
+- for (i = 0; i < item_no; i++)
+- items[i]->selected = 0;
+- items[scroll + choice]->selected = 1;
+- delwin (dialog);
+- free (status);
+- return 1;
+- case TAB:
+- case KEY_LEFT:
+- case KEY_RIGHT:
+- button = ((key == KEY_LEFT ? --button : ++button) < 0)
+- ? 1 : (button > 1 ? 0 : button);
+-
+- print_buttons(dialog, height, width, button);
+- wrefresh (dialog);
+- break;
+- case 'S':
+- case 's':
+- case ' ':
+- case '\n':
+- if (!button) {
+- if (flag == FLAG_CHECK) {
+- status[scroll + choice] = !status[scroll + choice];
+- wmove (list, choice, check_x);
+- wattrset (list, check_selected_attr);
+- wprintw (list, "[%c]", status[scroll + choice] ? 'X' : ' ');
+- } else {
+- if (!status[scroll + choice]) {
+- for (i = 0; i < item_no; i++)
+- status[i] = 0;
+- status[scroll + choice] = 1;
+- for (i = 0; i < max_choice; i++)
+- print_item (list, items[scroll + i]->name,
+- status[scroll + i], i, i == choice);
+- }
+- }
+- wnoutrefresh (list);
+- wrefresh (dialog);
+-
+- for (i = 0; i < item_no; i++) {
+- items[i]->selected = status[i];
+- }
+- } else {
+- for (i = 0; i < item_no; i++)
+- items[i]->selected = 0;
+- items[scroll + choice]->selected = 1;
+- }
+- delwin (dialog);
+- free (status);
+- return button;
+- case 'X':
+- case 'x':
+- key = ESC;
+- case ESC:
+- break;
+- }
+-
+- /* Now, update everything... */
+- doupdate ();
+- }
+-
+-
+- delwin (dialog);
+- free (status);
+- return -1; /* ESC pressed */
+-}
+diff -Nur busybox-1.00/scripts/config/colors.h busybox/scripts/config/colors.h
+--- busybox-1.00/scripts/config/colors.h 2002-12-05 09:41:06.000000000 +0100
++++ busybox/scripts/config/colors.h 1970-01-01 01:00:00.000000000 +0100
+@@ -1,161 +0,0 @@
+-/*
+- * colors.h -- color attribute definitions
+- *
+- * AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
+- *
+- * This program is free software; you can redistribute it and/or
+- * modify it under the terms of the GNU General Public License
+- * as published by the Free Software Foundation; either version 2
+- * of the License, or (at your option) any later version.
+- *
+- * This program is distributed in the hope that it will be useful,
+- * but WITHOUT ANY WARRANTY; without even the implied warranty of
+- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+- * GNU General Public License for more details.
+- *
+- * You should have received a copy of the GNU General Public License
+- * along with this program; if not, write to the Free Software
+- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+- */
+-
+-
+-/*
+- * Default color definitions
+- *
+- * *_FG = foreground
+- * *_BG = background
+- * *_HL = highlight?
+- */
+-#define SCREEN_FG COLOR_CYAN
+-#define SCREEN_BG COLOR_BLUE
+-#define SCREEN_HL TRUE
+-
+-#define SHADOW_FG COLOR_BLACK
+-#define SHADOW_BG COLOR_BLACK
+-#define SHADOW_HL TRUE
+-
+-#define DIALOG_FG COLOR_BLACK
+-#define DIALOG_BG COLOR_WHITE
+-#define DIALOG_HL FALSE
+-
+-#define TITLE_FG COLOR_YELLOW
+-#define TITLE_BG COLOR_WHITE
+-#define TITLE_HL TRUE
+-
+-#define BORDER_FG COLOR_WHITE
+-#define BORDER_BG COLOR_WHITE
+-#define BORDER_HL TRUE
+-
+-#define BUTTON_ACTIVE_FG COLOR_WHITE
+-#define BUTTON_ACTIVE_BG COLOR_BLUE
+-#define BUTTON_ACTIVE_HL TRUE
+-
+-#define BUTTON_INACTIVE_FG COLOR_BLACK
+-#define BUTTON_INACTIVE_BG COLOR_WHITE
+-#define BUTTON_INACTIVE_HL FALSE
+-
+-#define BUTTON_KEY_ACTIVE_FG COLOR_WHITE
+-#define BUTTON_KEY_ACTIVE_BG COLOR_BLUE
+-#define BUTTON_KEY_ACTIVE_HL TRUE
+-
+-#define BUTTON_KEY_INACTIVE_FG COLOR_RED
+-#define BUTTON_KEY_INACTIVE_BG COLOR_WHITE
+-#define BUTTON_KEY_INACTIVE_HL FALSE
+-
+-#define BUTTON_LABEL_ACTIVE_FG COLOR_YELLOW
+-#define BUTTON_LABEL_ACTIVE_BG COLOR_BLUE
+-#define BUTTON_LABEL_ACTIVE_HL TRUE
+-
+-#define BUTTON_LABEL_INACTIVE_FG COLOR_BLACK
+-#define BUTTON_LABEL_INACTIVE_BG COLOR_WHITE
+-#define BUTTON_LABEL_INACTIVE_HL TRUE
+-
+-#define INPUTBOX_FG COLOR_BLACK
+-#define INPUTBOX_BG COLOR_WHITE
+-#define INPUTBOX_HL FALSE
+-
+-#define INPUTBOX_BORDER_FG COLOR_BLACK
+-#define INPUTBOX_BORDER_BG COLOR_WHITE
+-#define INPUTBOX_BORDER_HL FALSE
+-
+-#define SEARCHBOX_FG COLOR_BLACK
+-#define SEARCHBOX_BG COLOR_WHITE
+-#define SEARCHBOX_HL FALSE
+-
+-#define SEARCHBOX_TITLE_FG COLOR_YELLOW
+-#define SEARCHBOX_TITLE_BG COLOR_WHITE
+-#define SEARCHBOX_TITLE_HL TRUE
+-
+-#define SEARCHBOX_BORDER_FG COLOR_WHITE
+-#define SEARCHBOX_BORDER_BG COLOR_WHITE
+-#define SEARCHBOX_BORDER_HL TRUE
+-
+-#define POSITION_INDICATOR_FG COLOR_YELLOW
+-#define POSITION_INDICATOR_BG COLOR_WHITE
+-#define POSITION_INDICATOR_HL TRUE
+-
+-#define MENUBOX_FG COLOR_BLACK
+-#define MENUBOX_BG COLOR_WHITE
+-#define MENUBOX_HL FALSE
+-
+-#define MENUBOX_BORDER_FG COLOR_WHITE
+-#define MENUBOX_BORDER_BG COLOR_WHITE
+-#define MENUBOX_BORDER_HL TRUE
+-
+-#define ITEM_FG COLOR_BLACK
+-#define ITEM_BG COLOR_WHITE
+-#define ITEM_HL FALSE
+-
+-#define ITEM_SELECTED_FG COLOR_WHITE
+-#define ITEM_SELECTED_BG COLOR_BLUE
+-#define ITEM_SELECTED_HL TRUE
+-
+-#define TAG_FG COLOR_YELLOW
+-#define TAG_BG COLOR_WHITE
+-#define TAG_HL TRUE
+-
+-#define TAG_SELECTED_FG COLOR_YELLOW
+-#define TAG_SELECTED_BG COLOR_BLUE
+-#define TAG_SELECTED_HL TRUE
+-
+-#define TAG_KEY_FG COLOR_YELLOW
+-#define TAG_KEY_BG COLOR_WHITE
+-#define TAG_KEY_HL TRUE
+-
+-#define TAG_KEY_SELECTED_FG COLOR_YELLOW
+-#define TAG_KEY_SELECTED_BG COLOR_BLUE
+-#define TAG_KEY_SELECTED_HL TRUE
+-
+-#define CHECK_FG COLOR_BLACK
+-#define CHECK_BG COLOR_WHITE
+-#define CHECK_HL FALSE
+-
+-#define CHECK_SELECTED_FG COLOR_WHITE
+-#define CHECK_SELECTED_BG COLOR_BLUE
+-#define CHECK_SELECTED_HL TRUE
+-
+-#define UARROW_FG COLOR_GREEN
+-#define UARROW_BG COLOR_WHITE
+-#define UARROW_HL TRUE
+-
+-#define DARROW_FG COLOR_GREEN
+-#define DARROW_BG COLOR_WHITE
+-#define DARROW_HL TRUE
+-
+-/* End of default color definitions */
+-
+-#define C_ATTR(x,y) ((x ? A_BOLD : 0) | COLOR_PAIR((y)))
+-#define COLOR_NAME_LEN 10
+-#define COLOR_COUNT 8
+-
+-/*
+- * Global variables
+- */
+-
+-typedef struct {
+- char name[COLOR_NAME_LEN];
+- int value;
+-} color_names_st;
+-
+-extern color_names_st color_names[];
+-extern int color_table[][3];
+diff -Nur busybox-1.00/scripts/config/conf.c busybox/scripts/config/conf.c
+--- busybox-1.00/scripts/config/conf.c 2004-01-16 13:48:53.000000000 +0100
++++ busybox/scripts/config/conf.c 2005-06-04 08:20:03.000000000 +0200
+@@ -31,14 +31,14 @@
+ static int indent = 1;
+ static int valid_stdin = 1;
+ static int conf_cnt;
+-static char line[128];
++static signed char line[128];
+ static struct menu *rootEntry;
+
+ static char nohelp_text[] = "Sorry, no help available for this option yet.\n";
+
+-static void strip(char *str)
++static void strip(signed char *str)
+ {
+- char *p = str;
++ signed char *p = str;
+ int l;
+
+ while ((isspace(*p)))
+diff -Nur busybox-1.00/scripts/config/confdata.c busybox/scripts/config/confdata.c
+--- busybox-1.00/scripts/config/confdata.c 2004-07-15 08:01:05.000000000 +0200
++++ busybox/scripts/config/confdata.c 2005-06-04 08:20:03.000000000 +0200
+@@ -23,10 +23,10 @@
+ NULL,
+ };
+
+-static char *conf_expand_value(const char *in)
++static char *conf_expand_value(const signed char *in)
+ {
+ struct symbol *sym;
+- const char *src;
++ const signed char *src;
+ static char res_value[SYMBOL_MAXLENGTH];
+ char *dst, name[SYMBOL_MAXLENGTH];
+
+@@ -287,7 +287,7 @@
+ } else
+ basename = conf_def_filename;
+
+- sprintf(newname, "%s.tmpconfig.%d", dirname, getpid());
++ sprintf(newname, "%s.tmpconfig.%d", dirname, (int)getpid());
+ out = fopen(newname, "w");
+ if (!out)
+ return 1;
+diff -Nur busybox-1.00/scripts/config/dialog.h busybox/scripts/config/dialog.h
+--- busybox-1.00/scripts/config/dialog.h 2004-03-15 09:29:08.000000000 +0100
++++ busybox/scripts/config/dialog.h 1970-01-01 01:00:00.000000000 +0100
+@@ -1,196 +0,0 @@
+-
+-/*
+- * dialog.h -- common declarations for all dialog modules
+- *
+- * AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
+- *
+- * This program is free software; you can redistribute it and/or
+- * modify it under the terms of the GNU General Public License
+- * as published by the Free Software Foundation; either version 2
+- * of the License, or (at your option) any later version.
+- *
+- * This program is distributed in the hope that it will be useful,
+- * but WITHOUT ANY WARRANTY; without even the implied warranty of
+- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+- * GNU General Public License for more details.
+- *
+- * You should have received a copy of the GNU General Public License
+- * along with this program; if not, write to the Free Software
+- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+- */
+-
+-#include <sys/types.h>
+-#include <fcntl.h>
+-#include <unistd.h>
+-#include <ctype.h>
+-#include <stdlib.h>
+-#include <string.h>
+-
+-#ifdef CURSES_LOC
+-#include CURSES_LOC
+-
+-/*
+- * Colors in ncurses 1.9.9e do not work properly since foreground and
+- * background colors are OR'd rather than separately masked. This version
+- * of dialog was hacked to work with ncurses 1.9.9e, making it incompatible
+- * with standard curses. The simplest fix (to make this work with standard
+- * curses) uses the wbkgdset() function, not used in the original hack.
+- * Turn it off if we're building with 1.9.9e, since it just confuses things.
+- */
+-#if defined(NCURSES_VERSION) && defined(_NEED_WRAP) && !defined(GCC_PRINTFLIKE)
+-#define OLD_NCURSES 1
+-#undef wbkgdset
+-#define wbkgdset(w,p) /*nothing*/
+-#else
+-#define OLD_NCURSES 0
+-#endif
+-
+-#define TR(params) _tracef params
+-
+-#define ESC 27
+-#define TAB 9
+-#define MAX_LEN 2048
+-#define BUF_SIZE (10*1024)
+-#define MIN(x,y) (x < y ? x : y)
+-#define MAX(x,y) (x > y ? x : y)
+-
+-
+-#ifndef ACS_ULCORNER
+-#define ACS_ULCORNER '+'
+-#endif
+-#ifndef ACS_LLCORNER
+-#define ACS_LLCORNER '+'
+-#endif
+-#ifndef ACS_URCORNER
+-#define ACS_URCORNER '+'
+-#endif
+-#ifndef ACS_LRCORNER
+-#define ACS_LRCORNER '+'
+-#endif
+-#ifndef ACS_HLINE
+-#define ACS_HLINE '-'
+-#endif
+-#ifndef ACS_VLINE
+-#define ACS_VLINE '|'
+-#endif
+-#ifndef ACS_LTEE
+-#define ACS_LTEE '+'
+-#endif
+-#ifndef ACS_RTEE
+-#define ACS_RTEE '+'
+-#endif
+-#ifndef ACS_UARROW
+-#define ACS_UARROW '^'
+-#endif
+-#ifndef ACS_DARROW
+-#define ACS_DARROW 'v'
+-#endif
+-
+-/*
+- * Attribute names
+- */
+-#define screen_attr attributes[0]
+-#define shadow_attr attributes[1]
+-#define dialog_attr attributes[2]
+-#define title_attr attributes[3]
+-#define border_attr attributes[4]
+-#define button_active_attr attributes[5]
+-#define button_inactive_attr attributes[6]
+-#define button_key_active_attr attributes[7]
+-#define button_key_inactive_attr attributes[8]
+-#define button_label_active_attr attributes[9]
+-#define button_label_inactive_attr attributes[10]
+-#define inputbox_attr attributes[11]
+-#define inputbox_border_attr attributes[12]
+-#define searchbox_attr attributes[13]
+-#define searchbox_title_attr attributes[14]
+-#define searchbox_border_attr attributes[15]
+-#define position_indicator_attr attributes[16]
+-#define menubox_attr attributes[17]
+-#define menubox_border_attr attributes[18]
+-#define item_attr attributes[19]
+-#define item_selected_attr attributes[20]
+-#define tag_attr attributes[21]
+-#define tag_selected_attr attributes[22]
+-#define tag_key_attr attributes[23]
+-#define tag_key_selected_attr attributes[24]
+-#define check_attr attributes[25]
+-#define check_selected_attr attributes[26]
+-#define uarrow_attr attributes[27]
+-#define darrow_attr attributes[28]
+-
+-/* number of attributes */
+-#define ATTRIBUTE_COUNT 29
+-
+-/*
+- * Global variables
+- */
+-extern bool use_colors;
+-
+-extern chtype attributes[];
+-#endif
+-
+-extern char *backtitle;
+-
+-struct dialog_list_item {
+- char *name;
+- int namelen;
+- char *tag;
+- int selected; /* Set to 1 by dialog_*() function. */
+-};
+-
+-/*
+- * Function prototypes
+- */
+-
+-void init_dialog (void);
+-void end_dialog (void);
+-void dialog_clear (void);
+-#ifdef CURSES_LOC
+-void attr_clear (WINDOW * win, int height, int width, chtype attr);
+-void color_setup (void);
+-void print_autowrap (WINDOW * win, const char *prompt, int width, int y, int x);
+-void print_button (WINDOW * win, const char *label, int y, int x, int selected);
+-void draw_box (WINDOW * win, int y, int x, int height, int width, chtype box,
+- chtype border);
+-void draw_shadow (WINDOW * win, int y, int x, int height, int width);
+-#endif
+-
+-int first_alpha (const char *string, const char *exempt);
+-int dialog_yesno (const char *title, const char *prompt, int height, int width);
+-int dialog_msgbox (const char *title, const char *prompt, int height,
+- int width, int pause);
+-int dialog_textbox (const char *title, const char *file, int height, int width);
+-int dialog_menu (const char *title, const char *prompt, int height, int width,
+- int menu_height, const char *choice, int item_no,
+- struct dialog_list_item ** items);
+-int dialog_checklist (const char *title, const char *prompt, int height,
+- int width, int list_height, int item_no,
+- struct dialog_list_item ** items, int flag);
+-extern unsigned char dialog_input_result[];
+-int dialog_inputbox (const char *title, const char *prompt, int height,
+- int width, const char *init);
+-
+-struct dialog_list_item *first_sel_item(int item_no,
+- struct dialog_list_item ** items);
+-
+-/*
+- * This is the base for fictitious keys, which activate
+- * the buttons.
+- *
+- * Mouse-generated keys are the following:
+- * -- the first 32 are used as numbers, in addition to '0'-'9'
+- * -- the lowercase are used to signal mouse-enter events (M_EVENT + 'o')
+- * -- uppercase chars are used to invoke the button (M_EVENT + 'O')
+- */
+-#ifdef CURSES_LOC
+-#define M_EVENT (KEY_MAX+1)
+-#endif
+-
+-
+-/*
+- * The `flag' parameter in checklist is used to select between
+- * radiolist and checklist
+- */
+-#define FLAG_CHECK 1
+-#define FLAG_RADIO 0
+diff -Nur busybox-1.00/scripts/config/expr.c busybox/scripts/config/expr.c
+--- busybox-1.00/scripts/config/expr.c 2004-07-15 08:01:05.000000000 +0200
++++ busybox/scripts/config/expr.c 2005-06-04 08:20:03.000000000 +0200
+@@ -1087,3 +1087,13 @@
+ {
+ expr_print(e, expr_print_file_helper, out, E_NONE);
+ }
++
++static void expr_print_gstr_helper(void *data, const char *str)
++{
++ str_append((struct gstr*)data, str);
++}
++
++void expr_gstr_print(struct expr *e, struct gstr *gs)
++{
++ expr_print(e, expr_print_gstr_helper, gs, E_NONE);
++}
+diff -Nur busybox-1.00/scripts/config/expr.h busybox/scripts/config/expr.h
+--- busybox-1.00/scripts/config/expr.h 2004-07-15 08:01:05.000000000 +0200
++++ busybox/scripts/config/expr.h 2005-06-04 08:20:03.000000000 +0200
+@@ -174,6 +174,8 @@
+ struct expr *expr_trans_compare(struct expr *e, enum expr_type type, struct symbol *sym);
+
+ void expr_fprint(struct expr *e, FILE *out);
++struct gstr; /* forward */
++void expr_gstr_print(struct expr *e, struct gstr *gs);
+
+ static inline int expr_is_yes(struct expr *e)
+ {
+diff -Nur busybox-1.00/scripts/config/inputbox.c busybox/scripts/config/inputbox.c
+--- busybox-1.00/scripts/config/inputbox.c 2002-12-05 09:41:07.000000000 +0100
++++ busybox/scripts/config/inputbox.c 1970-01-01 01:00:00.000000000 +0100
+@@ -1,240 +0,0 @@
+-/*
+- * inputbox.c -- implements the input box
+- *
+- * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
+- * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com)
+- *
+- * This program is free software; you can redistribute it and/or
+- * modify it under the terms of the GNU General Public License
+- * as published by the Free Software Foundation; either version 2
+- * of the License, or (at your option) any later version.
+- *
+- * This program is distributed in the hope that it will be useful,
+- * but WITHOUT ANY WARRANTY; without even the implied warranty of
+- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+- * GNU General Public License for more details.
+- *
+- * You should have received a copy of the GNU General Public License
+- * along with this program; if not, write to the Free Software
+- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+- */
+-
+-#include "dialog.h"
+-
+-unsigned char dialog_input_result[MAX_LEN + 1];
+-
+-/*
+- * Print the termination buttons
+- */
+-static void
+-print_buttons(WINDOW *dialog, int height, int width, int selected)
+-{
+- int x = width / 2 - 11;
+- int y = height - 2;
+-
+- print_button (dialog, " Ok ", y, x, selected==0);
+- print_button (dialog, " Help ", y, x + 14, selected==1);
+-
+- wmove(dialog, y, x+1+14*selected);
+- wrefresh(dialog);
+-}
+-
+-/*
+- * Display a dialog box for inputing a string
+- */
+-int
+-dialog_inputbox (const char *title, const char *prompt, int height, int width,
+- const char *init)
+-{
+- int i, x, y, box_y, box_x, box_width;
+- int input_x = 0, scroll = 0, key = 0, button = -1;
+- unsigned char *instr = dialog_input_result;
+- WINDOW *dialog;
+-
+- /* center dialog box on screen */
+- x = (COLS - width) / 2;
+- y = (LINES - height) / 2;
+-
+-
+- draw_shadow (stdscr, y, x, height, width);
+-
+- dialog = newwin (height, width, y, x);
+- keypad (dialog, TRUE);
+-
+- draw_box (dialog, 0, 0, height, width, dialog_attr, border_attr);
+- wattrset (dialog, border_attr);
+- mvwaddch (dialog, height-3, 0, ACS_LTEE);
+- for (i = 0; i < width - 2; i++)
+- waddch (dialog, ACS_HLINE);
+- wattrset (dialog, dialog_attr);
+- waddch (dialog, ACS_RTEE);
+-
+- if (title != NULL && strlen(title) >= width-2 ) {
+- /* truncate long title -- mec */
+- char * title2 = malloc(width-2+1);
+- memcpy( title2, title, width-2 );
+- title2[width-2] = '\0';
+- title = title2;
+- }
+-
+- if (title != NULL) {
+- wattrset (dialog, title_attr);
+- mvwaddch (dialog, 0, (width - strlen(title))/2 - 1, ' ');
+- waddstr (dialog, (char *)title);
+- waddch (dialog, ' ');
+- }
+-
+- wattrset (dialog, dialog_attr);
+- print_autowrap (dialog, prompt, width - 2, 1, 3);
+-
+- /* Draw the input field box */
+- box_width = width - 6;
+- getyx (dialog, y, x);
+- box_y = y + 2;
+- box_x = (width - box_width) / 2;
+- draw_box (dialog, y + 1, box_x - 1, 3, box_width + 2,
+- border_attr, dialog_attr);
+-
+- print_buttons(dialog, height, width, 0);
+-
+- /* Set up the initial value */
+- wmove (dialog, box_y, box_x);
+- wattrset (dialog, inputbox_attr);
+-
+- if (!init)
+- instr[0] = '\0';
+- else
+- strcpy (instr, init);
+-
+- input_x = strlen (instr);
+-
+- if (input_x >= box_width) {
+- scroll = input_x - box_width + 1;
+- input_x = box_width - 1;
+- for (i = 0; i < box_width - 1; i++)
+- waddch (dialog, instr[scroll + i]);
+- } else
+- waddstr (dialog, instr);
+-
+- wmove (dialog, box_y, box_x + input_x);
+-
+- wrefresh (dialog);
+-
+- while (key != ESC) {
+- key = wgetch (dialog);
+-
+- if (button == -1) { /* Input box selected */
+- switch (key) {
+- case TAB:
+- case KEY_UP:
+- case KEY_DOWN:
+- break;
+- case KEY_LEFT:
+- continue;
+- case KEY_RIGHT:
+- continue;
+- case KEY_BACKSPACE:
+- case 127:
+- if (input_x || scroll) {
+- wattrset (dialog, inputbox_attr);
+- if (!input_x) {
+- scroll = scroll < box_width - 1 ?
+- 0 : scroll - (box_width - 1);
+- wmove (dialog, box_y, box_x);
+- for (i = 0; i < box_width; i++)
+- waddch (dialog, instr[scroll + input_x + i] ?
+- instr[scroll + input_x + i] : ' ');
+- input_x = strlen (instr) - scroll;
+- } else
+- input_x--;
+- instr[scroll + input_x] = '\0';
+- mvwaddch (dialog, box_y, input_x + box_x, ' ');
+- wmove (dialog, box_y, input_x + box_x);
+- wrefresh (dialog);
+- }
+- continue;
+- default:
+- if (key < 0x100 && isprint (key)) {
+- if (scroll + input_x < MAX_LEN) {
+- wattrset (dialog, inputbox_attr);
+- instr[scroll + input_x] = key;
+- instr[scroll + input_x + 1] = '\0';
+- if (input_x == box_width - 1) {
+- scroll++;
+- wmove (dialog, box_y, box_x);
+- for (i = 0; i < box_width - 1; i++)
+- waddch (dialog, instr[scroll + i]);
+- } else {
+- wmove (dialog, box_y, input_x++ + box_x);
+- waddch (dialog, key);
+- }
+- wrefresh (dialog);
+- } else
+- flash (); /* Alarm user about overflow */
+- continue;
+- }
+- }
+- }
+- switch (key) {
+- case 'O':
+- case 'o':
+- delwin (dialog);
+- return 0;
+- case 'H':
+- case 'h':
+- delwin (dialog);
+- return 1;
+- case KEY_UP:
+- case KEY_LEFT:
+- switch (button) {
+- case -1:
+- button = 1; /* Indicates "Cancel" button is selected */
+- print_buttons(dialog, height, width, 1);
+- break;
+- case 0:
+- button = -1; /* Indicates input box is selected */
+- print_buttons(dialog, height, width, 0);
+- wmove (dialog, box_y, box_x + input_x);
+- wrefresh (dialog);
+- break;
+- case 1:
+- button = 0; /* Indicates "OK" button is selected */
+- print_buttons(dialog, height, width, 0);
+- break;
+- }
+- break;
+- case TAB:
+- case KEY_DOWN:
+- case KEY_RIGHT:
+- switch (button) {
+- case -1:
+- button = 0; /* Indicates "OK" button is selected */
+- print_buttons(dialog, height, width, 0);
+- break;
+- case 0:
+- button = 1; /* Indicates "Cancel" button is selected */
+- print_buttons(dialog, height, width, 1);
+- break;
+- case 1:
+- button = -1; /* Indicates input box is selected */
+- print_buttons(dialog, height, width, 0);
+- wmove (dialog, box_y, box_x + input_x);
+- wrefresh (dialog);
+- break;
+- }
+- break;
+- case ' ':
+- case '\n':
+- delwin (dialog);
+- return (button == -1 ? 0 : button);
+- case 'X':
+- case 'x':
+- key = ESC;
+- case ESC:
+- break;
+- }
+- }
+-
+- delwin (dialog);
+- return -1; /* ESC pressed */
+-}
+diff -Nur busybox-1.00/scripts/config/lkc.h busybox/scripts/config/lkc.h
+--- busybox-1.00/scripts/config/lkc.h 2003-08-05 04:18:24.000000000 +0200
++++ busybox/scripts/config/lkc.h 2005-06-04 08:20:03.000000000 +0200
+@@ -56,11 +56,21 @@
+ void menu_add_symbol(enum prop_type type, struct symbol *sym, struct expr *dep);
+ void menu_finalize(struct menu *parent);
+ void menu_set_type(int type);
++
++/* util.c */
+ struct file *file_lookup(const char *name);
+ int file_write_dep(const char *name);
+
+-extern struct menu *current_entry;
+-extern struct menu *current_menu;
++struct gstr {
++ size_t len;
++ char *s;
++};
++struct gstr str_new(void);
++struct gstr str_assign(const char *s);
++void str_free(struct gstr *gs);
++void str_append(struct gstr *gs, const char *s);
++void str_printf(struct gstr *gs, const char *fmt, ...);
++const char *str_get(struct gstr *gs);
+
+ /* symbol.c */
+ void sym_init(void);
+diff -Nur busybox-1.00/scripts/config/lkc_proto.h busybox/scripts/config/lkc_proto.h
+--- busybox-1.00/scripts/config/lkc_proto.h 2003-08-05 04:18:25.000000000 +0200
++++ busybox/scripts/config/lkc_proto.h 2005-06-04 08:20:03.000000000 +0200
+@@ -18,6 +18,7 @@
+
+ P(sym_lookup,struct symbol *,(const char *name, int isconst));
+ P(sym_find,struct symbol *,(const char *name));
++P(sym_re_search,struct symbol **,(const char *pattern));
+ P(sym_type_name,const char *,(enum symbol_type type));
+ P(sym_calc_value,void,(struct symbol *sym));
+ P(sym_get_type,enum symbol_type,(struct symbol *sym));
+diff -Nur busybox-1.00/scripts/config/lxdialog/BIG.FAT.WARNING busybox/scripts/config/lxdialog/BIG.FAT.WARNING
+--- busybox-1.00/scripts/config/lxdialog/BIG.FAT.WARNING 1970-01-01 01:00:00.000000000 +0100
++++ busybox/scripts/config/lxdialog/BIG.FAT.WARNING 2005-06-04 08:20:02.000000000 +0200
+@@ -0,0 +1,4 @@
++This is NOT the official version of dialog. This version has been
++significantly modified from the original. It is for use by the Linux
++kernel configuration script. Please do not bother Savio Lam with
++questions about this program.
+diff -Nur busybox-1.00/scripts/config/lxdialog/checklist.c busybox/scripts/config/lxdialog/checklist.c
+--- busybox-1.00/scripts/config/lxdialog/checklist.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/scripts/config/lxdialog/checklist.c 2005-06-04 08:20:02.000000000 +0200
+@@ -0,0 +1,372 @@
++/*
++ * checklist.c -- implements the checklist box
++ *
++ * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
++ * Stuart Herbert - S.Herbert@sheffield.ac.uk: radiolist extension
++ * Alessandro Rubini - rubini@ipvvis.unipv.it: merged the two
++ * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com)
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version 2
++ * of the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
++ */
++
++#include "dialog.h"
++
++static int list_width, check_x, item_x, checkflag;
++
++/*
++ * Print list item
++ */
++static void
++print_item (WINDOW * win, const char *item, int status,
++ int choice, int selected)
++{
++ int i;
++
++ /* Clear 'residue' of last item */
++ wattrset (win, menubox_attr);
++ wmove (win, choice, 0);
++ for (i = 0; i < list_width; i++)
++ waddch (win, ' ');
++
++ wmove (win, choice, check_x);
++ wattrset (win, selected ? check_selected_attr : check_attr);
++ if (checkflag == FLAG_CHECK)
++ wprintw (win, "[%c]", status ? 'X' : ' ');
++ else
++ wprintw (win, "(%c)", status ? 'X' : ' ');
++
++ wattrset (win, selected ? tag_selected_attr : tag_attr);
++ mvwaddch(win, choice, item_x, item[0]);
++ wattrset (win, selected ? item_selected_attr : item_attr);
++ waddstr (win, (char *)item+1);
++ if (selected) {
++ wmove (win, choice, check_x+1);
++ wrefresh (win);
++ }
++}
++
++/*
++ * Print the scroll indicators.
++ */
++static void
++print_arrows (WINDOW * win, int choice, int item_no, int scroll,
++ int y, int x, int height)
++{
++ wmove(win, y, x);
++
++ if (scroll > 0) {
++ wattrset (win, uarrow_attr);
++ waddch (win, ACS_UARROW);
++ waddstr (win, "(-)");
++ }
++ else {
++ wattrset (win, menubox_attr);
++ waddch (win, ACS_HLINE);
++ waddch (win, ACS_HLINE);
++ waddch (win, ACS_HLINE);
++ waddch (win, ACS_HLINE);
++ }
++
++ y = y + height + 1;
++ wmove(win, y, x);
++
++ if ((height < item_no) && (scroll + choice < item_no - 1)) {
++ wattrset (win, darrow_attr);
++ waddch (win, ACS_DARROW);
++ waddstr (win, "(+)");
++ }
++ else {
++ wattrset (win, menubox_border_attr);
++ waddch (win, ACS_HLINE);
++ waddch (win, ACS_HLINE);
++ waddch (win, ACS_HLINE);
++ waddch (win, ACS_HLINE);
++ }
++}
++
++/*
++ * Display the termination buttons
++ */
++static void
++print_buttons( WINDOW *dialog, int height, int width, int selected)
++{
++ int x = width / 2 - 11;
++ int y = height - 2;
++
++ print_button (dialog, "Select", y, x, selected == 0);
++ print_button (dialog, " Help ", y, x + 14, selected == 1);
++
++ wmove(dialog, y, x+1 + 14*selected);
++ wrefresh (dialog);
++}
++
++/*
++ * Display a dialog box with a list of options that can be turned on or off
++ * The `flag' parameter is used to select between radiolist and checklist.
++ */
++int
++dialog_checklist (const char *title, const char *prompt, int height, int width,
++ int list_height, int item_no, struct dialog_list_item ** items,
++ int flag)
++
++{
++ int i, x, y, box_x, box_y;
++ int key = 0, button = 0, choice = 0, scroll = 0, max_choice, *status;
++ WINDOW *dialog, *list;
++
++ checkflag = flag;
++
++ /* Allocate space for storing item on/off status */
++ if ((status = malloc (sizeof (int) * item_no)) == NULL) {
++ endwin ();
++ fprintf (stderr,
++ "\nCan't allocate memory in dialog_checklist().\n");
++ exit (-1);
++ }
++
++ /* Initializes status */
++ for (i = 0; i < item_no; i++) {
++ status[i] = (items[i]->selected == 1); /* ON */
++ if ((!choice && status[i]) || items[i]->selected == 2) /* SELECTED */
++ choice = i + 1;
++ }
++ if (choice)
++ choice--;
++
++ max_choice = MIN (list_height, item_no);
++
++ /* center dialog box on screen */
++ x = (COLS - width) / 2;
++ y = (LINES - height) / 2;
++
++ draw_shadow (stdscr, y, x, height, width);
++
++ dialog = newwin (height, width, y, x);
++ keypad (dialog, TRUE);
++
++ draw_box (dialog, 0, 0, height, width, dialog_attr, border_attr);
++ wattrset (dialog, border_attr);
++ mvwaddch (dialog, height-3, 0, ACS_LTEE);
++ for (i = 0; i < width - 2; i++)
++ waddch (dialog, ACS_HLINE);
++ wattrset (dialog, dialog_attr);
++ waddch (dialog, ACS_RTEE);
++
++ if (title != NULL && strlen(title) >= width-2 ) {
++ /* truncate long title -- mec */
++ char * title2 = malloc(width-2+1);
++ memcpy( title2, title, width-2 );
++ title2[width-2] = '\0';
++ title = title2;
++ }
++
++ if (title != NULL) {
++ wattrset (dialog, title_attr);
++ mvwaddch (dialog, 0, (width - strlen(title))/2 - 1, ' ');
++ waddstr (dialog, (char *)title);
++ waddch (dialog, ' ');
++ }
++
++ wattrset (dialog, dialog_attr);
++ print_autowrap (dialog, prompt, width - 2, 1, 3);
++
++ list_width = width - 6;
++ box_y = height - list_height - 5;
++ box_x = (width - list_width) / 2 - 1;
++
++ /* create new window for the list */
++ list = subwin (dialog, list_height, list_width, y+box_y+1, x+box_x+1);
++
++ keypad (list, TRUE);
++
++ /* draw a box around the list items */
++ draw_box (dialog, box_y, box_x, list_height + 2, list_width + 2,
++ menubox_border_attr, menubox_attr);
++
++ /* Find length of longest item in order to center checklist */
++ check_x = 0;
++ for (i = 0; i < item_no; i++)
++ check_x = MAX (check_x, + strlen (items[i]->name) + 4);
++
++ check_x = (list_width - check_x) / 2;
++ item_x = check_x + 4;
++
++ if (choice >= list_height) {
++ scroll = choice - list_height + 1;
++ choice -= scroll;
++ }
++
++ /* Print the list */
++ for (i = 0; i < max_choice; i++) {
++ print_item (list, items[scroll + i]->name,
++ status[i+scroll], i, i == choice);
++ }
++
++ print_arrows(dialog, choice, item_no, scroll,
++ box_y, box_x + check_x + 5, list_height);
++
++ print_buttons(dialog, height, width, 0);
++
++ wnoutrefresh (list);
++ wnoutrefresh (dialog);
++ doupdate ();
++
++ while (key != ESC) {
++ key = wgetch (dialog);
++
++ for (i = 0; i < max_choice; i++)
++ if (toupper(key) == toupper(items[scroll + i]->name[0]))
++ break;
++
++
++ if ( i < max_choice || key == KEY_UP || key == KEY_DOWN ||
++ key == '+' || key == '-' ) {
++ if (key == KEY_UP || key == '-') {
++ if (!choice) {
++ if (!scroll)
++ continue;
++ /* Scroll list down */
++ if (list_height > 1) {
++ /* De-highlight current first item */
++ print_item (list, items[scroll]->name,
++ status[scroll], 0, FALSE);
++ scrollok (list, TRUE);
++ wscrl (list, -1);
++ scrollok (list, FALSE);
++ }
++ scroll--;
++ print_item (list, items[scroll]->name,
++ status[scroll], 0, TRUE);
++ wnoutrefresh (list);
++
++ print_arrows(dialog, choice, item_no, scroll,
++ box_y, box_x + check_x + 5, list_height);
++
++ wrefresh (dialog);
++
++ continue; /* wait for another key press */
++ } else
++ i = choice - 1;
++ } else if (key == KEY_DOWN || key == '+') {
++ if (choice == max_choice - 1) {
++ if (scroll + choice >= item_no - 1)
++ continue;
++ /* Scroll list up */
++ if (list_height > 1) {
++ /* De-highlight current last item before scrolling up */
++ print_item (list, items[scroll + max_choice - 1]->name,
++ status[scroll + max_choice - 1],
++ max_choice - 1, FALSE);
++ scrollok (list, TRUE);
++ scroll (list);
++ scrollok (list, FALSE);
++ }
++ scroll++;
++ print_item (list, items[scroll + max_choice - 1]->name,
++ status[scroll + max_choice - 1],
++ max_choice - 1, TRUE);
++ wnoutrefresh (list);
++
++ print_arrows(dialog, choice, item_no, scroll,
++ box_y, box_x + check_x + 5, list_height);
++
++ wrefresh (dialog);
++
++ continue; /* wait for another key press */
++ } else
++ i = choice + 1;
++ }
++ if (i != choice) {
++ /* De-highlight current item */
++ print_item (list, items[scroll + choice]->name,
++ status[scroll + choice], choice, FALSE);
++ /* Highlight new item */
++ choice = i;
++ print_item (list, items[scroll + choice]->name,
++ status[scroll + choice], choice, TRUE);
++ wnoutrefresh (list);
++ wrefresh (dialog);
++ }
++ continue; /* wait for another key press */
++ }
++ switch (key) {
++ case 'H':
++ case 'h':
++ case '?':
++ for (i = 0; i < item_no; i++)
++ items[i]->selected = 0;
++ items[scroll + choice]->selected = 1;
++ delwin (dialog);
++ free (status);
++ return 1;
++ case TAB:
++ case KEY_LEFT:
++ case KEY_RIGHT:
++ button = ((key == KEY_LEFT ? --button : ++button) < 0)
++ ? 1 : (button > 1 ? 0 : button);
++
++ print_buttons(dialog, height, width, button);
++ wrefresh (dialog);
++ break;
++ case 'S':
++ case 's':
++ case ' ':
++ case '\n':
++ if (!button) {
++ if (flag == FLAG_CHECK) {
++ status[scroll + choice] = !status[scroll + choice];
++ wmove (list, choice, check_x);
++ wattrset (list, check_selected_attr);
++ wprintw (list, "[%c]", status[scroll + choice] ? 'X' : ' ');
++ } else {
++ if (!status[scroll + choice]) {
++ for (i = 0; i < item_no; i++)
++ status[i] = 0;
++ status[scroll + choice] = 1;
++ for (i = 0; i < max_choice; i++)
++ print_item (list, items[scroll + i]->name,
++ status[scroll + i], i, i == choice);
++ }
++ }
++ wnoutrefresh (list);
++ wrefresh (dialog);
++
++ for (i = 0; i < item_no; i++) {
++ items[i]->selected = status[i];
++ }
++ } else {
++ for (i = 0; i < item_no; i++)
++ items[i]->selected = 0;
++ items[scroll + choice]->selected = 1;
++ }
++ delwin (dialog);
++ free (status);
++ return button;
++ case 'X':
++ case 'x':
++ key = ESC;
++ case ESC:
++ break;
++ }
++
++ /* Now, update everything... */
++ doupdate ();
++ }
++
++
++ delwin (dialog);
++ free (status);
++ return -1; /* ESC pressed */
++}
+diff -Nur busybox-1.00/scripts/config/lxdialog/colors.h busybox/scripts/config/lxdialog/colors.h
+--- busybox-1.00/scripts/config/lxdialog/colors.h 1970-01-01 01:00:00.000000000 +0100
++++ busybox/scripts/config/lxdialog/colors.h 2005-06-04 08:20:02.000000000 +0200
+@@ -0,0 +1,161 @@
++/*
++ * colors.h -- color attribute definitions
++ *
++ * AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version 2
++ * of the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
++ */
++
++
++/*
++ * Default color definitions
++ *
++ * *_FG = foreground
++ * *_BG = background
++ * *_HL = highlight?
++ */
++#define SCREEN_FG COLOR_CYAN
++#define SCREEN_BG COLOR_BLUE
++#define SCREEN_HL TRUE
++
++#define SHADOW_FG COLOR_BLACK
++#define SHADOW_BG COLOR_BLACK
++#define SHADOW_HL TRUE
++
++#define DIALOG_FG COLOR_BLACK
++#define DIALOG_BG COLOR_WHITE
++#define DIALOG_HL FALSE
++
++#define TITLE_FG COLOR_YELLOW
++#define TITLE_BG COLOR_WHITE
++#define TITLE_HL TRUE
++
++#define BORDER_FG COLOR_WHITE
++#define BORDER_BG COLOR_WHITE
++#define BORDER_HL TRUE
++
++#define BUTTON_ACTIVE_FG COLOR_WHITE
++#define BUTTON_ACTIVE_BG COLOR_BLUE
++#define BUTTON_ACTIVE_HL TRUE
++
++#define BUTTON_INACTIVE_FG COLOR_BLACK
++#define BUTTON_INACTIVE_BG COLOR_WHITE
++#define BUTTON_INACTIVE_HL FALSE
++
++#define BUTTON_KEY_ACTIVE_FG COLOR_WHITE
++#define BUTTON_KEY_ACTIVE_BG COLOR_BLUE
++#define BUTTON_KEY_ACTIVE_HL TRUE
++
++#define BUTTON_KEY_INACTIVE_FG COLOR_RED
++#define BUTTON_KEY_INACTIVE_BG COLOR_WHITE
++#define BUTTON_KEY_INACTIVE_HL FALSE
++
++#define BUTTON_LABEL_ACTIVE_FG COLOR_YELLOW
++#define BUTTON_LABEL_ACTIVE_BG COLOR_BLUE
++#define BUTTON_LABEL_ACTIVE_HL TRUE
++
++#define BUTTON_LABEL_INACTIVE_FG COLOR_BLACK
++#define BUTTON_LABEL_INACTIVE_BG COLOR_WHITE
++#define BUTTON_LABEL_INACTIVE_HL TRUE
++
++#define INPUTBOX_FG COLOR_BLACK
++#define INPUTBOX_BG COLOR_WHITE
++#define INPUTBOX_HL FALSE
++
++#define INPUTBOX_BORDER_FG COLOR_BLACK
++#define INPUTBOX_BORDER_BG COLOR_WHITE
++#define INPUTBOX_BORDER_HL FALSE
++
++#define SEARCHBOX_FG COLOR_BLACK
++#define SEARCHBOX_BG COLOR_WHITE
++#define SEARCHBOX_HL FALSE
++
++#define SEARCHBOX_TITLE_FG COLOR_YELLOW
++#define SEARCHBOX_TITLE_BG COLOR_WHITE
++#define SEARCHBOX_TITLE_HL TRUE
++
++#define SEARCHBOX_BORDER_FG COLOR_WHITE
++#define SEARCHBOX_BORDER_BG COLOR_WHITE
++#define SEARCHBOX_BORDER_HL TRUE
++
++#define POSITION_INDICATOR_FG COLOR_YELLOW
++#define POSITION_INDICATOR_BG COLOR_WHITE
++#define POSITION_INDICATOR_HL TRUE
++
++#define MENUBOX_FG COLOR_BLACK
++#define MENUBOX_BG COLOR_WHITE
++#define MENUBOX_HL FALSE
++
++#define MENUBOX_BORDER_FG COLOR_WHITE
++#define MENUBOX_BORDER_BG COLOR_WHITE
++#define MENUBOX_BORDER_HL TRUE
++
++#define ITEM_FG COLOR_BLACK
++#define ITEM_BG COLOR_WHITE
++#define ITEM_HL FALSE
++
++#define ITEM_SELECTED_FG COLOR_WHITE
++#define ITEM_SELECTED_BG COLOR_BLUE
++#define ITEM_SELECTED_HL TRUE
++
++#define TAG_FG COLOR_YELLOW
++#define TAG_BG COLOR_WHITE
++#define TAG_HL TRUE
++
++#define TAG_SELECTED_FG COLOR_YELLOW
++#define TAG_SELECTED_BG COLOR_BLUE
++#define TAG_SELECTED_HL TRUE
++
++#define TAG_KEY_FG COLOR_YELLOW
++#define TAG_KEY_BG COLOR_WHITE
++#define TAG_KEY_HL TRUE
++
++#define TAG_KEY_SELECTED_FG COLOR_YELLOW
++#define TAG_KEY_SELECTED_BG COLOR_BLUE
++#define TAG_KEY_SELECTED_HL TRUE
++
++#define CHECK_FG COLOR_BLACK
++#define CHECK_BG COLOR_WHITE
++#define CHECK_HL FALSE
++
++#define CHECK_SELECTED_FG COLOR_WHITE
++#define CHECK_SELECTED_BG COLOR_BLUE
++#define CHECK_SELECTED_HL TRUE
++
++#define UARROW_FG COLOR_GREEN
++#define UARROW_BG COLOR_WHITE
++#define UARROW_HL TRUE
++
++#define DARROW_FG COLOR_GREEN
++#define DARROW_BG COLOR_WHITE
++#define DARROW_HL TRUE
++
++/* End of default color definitions */
++
++#define C_ATTR(x,y) ((x ? A_BOLD : 0) | COLOR_PAIR((y)))
++#define COLOR_NAME_LEN 10
++#define COLOR_COUNT 8
++
++/*
++ * Global variables
++ */
++
++typedef struct {
++ char name[COLOR_NAME_LEN];
++ int value;
++} color_names_st;
++
++extern color_names_st color_names[];
++extern int color_table[][3];
+diff -Nur busybox-1.00/scripts/config/lxdialog/dialog.h busybox/scripts/config/lxdialog/dialog.h
+--- busybox-1.00/scripts/config/lxdialog/dialog.h 1970-01-01 01:00:00.000000000 +0100
++++ busybox/scripts/config/lxdialog/dialog.h 2005-06-04 08:20:02.000000000 +0200
+@@ -0,0 +1,199 @@
++
++/*
++ * dialog.h -- common declarations for all dialog modules
++ *
++ * AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version 2
++ * of the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
++ */
++
++#include <sys/types.h>
++#include <fcntl.h>
++#include <unistd.h>
++#include <ctype.h>
++#include <stdlib.h>
++#include <string.h>
++
++#ifdef CURSES_LOC
++#ifdef __sun__
++#define CURS_MACROS
++#endif
++#include CURSES_LOC
++
++/*
++ * Colors in ncurses 1.9.9e do not work properly since foreground and
++ * background colors are OR'd rather than separately masked. This version
++ * of dialog was hacked to work with ncurses 1.9.9e, making it incompatible
++ * with standard curses. The simplest fix (to make this work with standard
++ * curses) uses the wbkgdset() function, not used in the original hack.
++ * Turn it off if we're building with 1.9.9e, since it just confuses things.
++ */
++#if defined(NCURSES_VERSION) && defined(_NEED_WRAP) && !defined(GCC_PRINTFLIKE)
++#define OLD_NCURSES 1
++#undef wbkgdset
++#define wbkgdset(w,p) /*nothing*/
++#else
++#define OLD_NCURSES 0
++#endif
++
++#define TR(params) _tracef params
++
++#define ESC 27
++#define TAB 9
++#define MAX_LEN 2048
++#define BUF_SIZE (10*1024)
++#define MIN(x,y) (x < y ? x : y)
++#define MAX(x,y) (x > y ? x : y)
++
++
++#ifndef ACS_ULCORNER
++#define ACS_ULCORNER '+'
++#endif
++#ifndef ACS_LLCORNER
++#define ACS_LLCORNER '+'
++#endif
++#ifndef ACS_URCORNER
++#define ACS_URCORNER '+'
++#endif
++#ifndef ACS_LRCORNER
++#define ACS_LRCORNER '+'
++#endif
++#ifndef ACS_HLINE
++#define ACS_HLINE '-'
++#endif
++#ifndef ACS_VLINE
++#define ACS_VLINE '|'
++#endif
++#ifndef ACS_LTEE
++#define ACS_LTEE '+'
++#endif
++#ifndef ACS_RTEE
++#define ACS_RTEE '+'
++#endif
++#ifndef ACS_UARROW
++#define ACS_UARROW '^'
++#endif
++#ifndef ACS_DARROW
++#define ACS_DARROW 'v'
++#endif
++
++/*
++ * Attribute names
++ */
++#define screen_attr attributes[0]
++#define shadow_attr attributes[1]
++#define dialog_attr attributes[2]
++#define title_attr attributes[3]
++#define border_attr attributes[4]
++#define button_active_attr attributes[5]
++#define button_inactive_attr attributes[6]
++#define button_key_active_attr attributes[7]
++#define button_key_inactive_attr attributes[8]
++#define button_label_active_attr attributes[9]
++#define button_label_inactive_attr attributes[10]
++#define inputbox_attr attributes[11]
++#define inputbox_border_attr attributes[12]
++#define searchbox_attr attributes[13]
++#define searchbox_title_attr attributes[14]
++#define searchbox_border_attr attributes[15]
++#define position_indicator_attr attributes[16]
++#define menubox_attr attributes[17]
++#define menubox_border_attr attributes[18]
++#define item_attr attributes[19]
++#define item_selected_attr attributes[20]
++#define tag_attr attributes[21]
++#define tag_selected_attr attributes[22]
++#define tag_key_attr attributes[23]
++#define tag_key_selected_attr attributes[24]
++#define check_attr attributes[25]
++#define check_selected_attr attributes[26]
++#define uarrow_attr attributes[27]
++#define darrow_attr attributes[28]
++
++/* number of attributes */
++#define ATTRIBUTE_COUNT 29
++
++/*
++ * Global variables
++ */
++extern bool use_colors;
++
++extern chtype attributes[];
++#endif
++
++extern const char *backtitle;
++
++struct dialog_list_item {
++ char *name;
++ int namelen;
++ char *tag;
++ int selected; /* Set to 1 by dialog_*() function. */
++};
++
++/*
++ * Function prototypes
++ */
++
++void init_dialog (void);
++void end_dialog (void);
++void dialog_clear (void);
++#ifdef CURSES_LOC
++void attr_clear (WINDOW * win, int height, int width, chtype attr);
++void color_setup (void);
++void print_autowrap (WINDOW * win, const char *prompt, int width, int y, int x);
++void print_button (WINDOW * win, const char *label, int y, int x, int selected);
++void draw_box (WINDOW * win, int y, int x, int height, int width, chtype box,
++ chtype border);
++void draw_shadow (WINDOW * win, int y, int x, int height, int width);
++#endif
++
++int first_alpha (const char *string, const char *exempt);
++int dialog_yesno (const char *title, const char *prompt, int height, int width);
++int dialog_msgbox (const char *title, const char *prompt, int height,
++ int width, int pause);
++int dialog_textbox (const char *title, const char *file, int height, int width);
++int dialog_menu (const char *title, const char *prompt, int height, int width,
++ int menu_height, const char *choice, int item_no,
++ struct dialog_list_item ** items);
++int dialog_checklist (const char *title, const char *prompt, int height,
++ int width, int list_height, int item_no,
++ struct dialog_list_item ** items, int flag);
++extern unsigned char dialog_input_result[];
++int dialog_inputbox (const char *title, const char *prompt, int height,
++ int width, const char *init);
++
++struct dialog_list_item *first_sel_item(int item_no,
++ struct dialog_list_item ** items);
++
++/*
++ * This is the base for fictitious keys, which activate
++ * the buttons.
++ *
++ * Mouse-generated keys are the following:
++ * -- the first 32 are used as numbers, in addition to '0'-'9'
++ * -- the lowercase are used to signal mouse-enter events (M_EVENT + 'o')
++ * -- uppercase chars are used to invoke the button (M_EVENT + 'O')
++ */
++#ifdef CURSES_LOC
++#define M_EVENT (KEY_MAX+1)
++#endif
++
++
++/*
++ * The `flag' parameter in checklist is used to select between
++ * radiolist and checklist
++ */
++#define FLAG_CHECK 1
++#define FLAG_RADIO 0
+diff -Nur busybox-1.00/scripts/config/lxdialog/inputbox.c busybox/scripts/config/lxdialog/inputbox.c
+--- busybox-1.00/scripts/config/lxdialog/inputbox.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/scripts/config/lxdialog/inputbox.c 2005-06-04 08:20:02.000000000 +0200
+@@ -0,0 +1,240 @@
++/*
++ * inputbox.c -- implements the input box
++ *
++ * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
++ * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com)
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version 2
++ * of the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
++ */
++
++#include "dialog.h"
++
++unsigned char dialog_input_result[MAX_LEN + 1];
++
++/*
++ * Print the termination buttons
++ */
++static void
++print_buttons(WINDOW *dialog, int height, int width, int selected)
++{
++ int x = width / 2 - 11;
++ int y = height - 2;
++
++ print_button (dialog, " Ok ", y, x, selected==0);
++ print_button (dialog, " Help ", y, x + 14, selected==1);
++
++ wmove(dialog, y, x+1+14*selected);
++ wrefresh(dialog);
++}
++
++/*
++ * Display a dialog box for inputing a string
++ */
++int
++dialog_inputbox (const char *title, const char *prompt, int height, int width,
++ const char *init)
++{
++ int i, x, y, box_y, box_x, box_width;
++ int input_x = 0, scroll = 0, key = 0, button = -1;
++ unsigned char *instr = dialog_input_result;
++ WINDOW *dialog;
++
++ /* center dialog box on screen */
++ x = (COLS - width) / 2;
++ y = (LINES - height) / 2;
++
++
++ draw_shadow (stdscr, y, x, height, width);
++
++ dialog = newwin (height, width, y, x);
++ keypad (dialog, TRUE);
++
++ draw_box (dialog, 0, 0, height, width, dialog_attr, border_attr);
++ wattrset (dialog, border_attr);
++ mvwaddch (dialog, height-3, 0, ACS_LTEE);
++ for (i = 0; i < width - 2; i++)
++ waddch (dialog, ACS_HLINE);
++ wattrset (dialog, dialog_attr);
++ waddch (dialog, ACS_RTEE);
++
++ if (title != NULL && strlen(title) >= width-2 ) {
++ /* truncate long title -- mec */
++ char * title2 = malloc(width-2+1);
++ memcpy( title2, title, width-2 );
++ title2[width-2] = '\0';
++ title = title2;
++ }
++
++ if (title != NULL) {
++ wattrset (dialog, title_attr);
++ mvwaddch (dialog, 0, (width - strlen(title))/2 - 1, ' ');
++ waddstr (dialog, (char *)title);
++ waddch (dialog, ' ');
++ }
++
++ wattrset (dialog, dialog_attr);
++ print_autowrap (dialog, prompt, width - 2, 1, 3);
++
++ /* Draw the input field box */
++ box_width = width - 6;
++ getyx (dialog, y, x);
++ box_y = y + 2;
++ box_x = (width - box_width) / 2;
++ draw_box (dialog, y + 1, box_x - 1, 3, box_width + 2,
++ border_attr, dialog_attr);
++
++ print_buttons(dialog, height, width, 0);
++
++ /* Set up the initial value */
++ wmove (dialog, box_y, box_x);
++ wattrset (dialog, inputbox_attr);
++
++ if (!init)
++ instr[0] = '\0';
++ else
++ strcpy (instr, init);
++
++ input_x = strlen (instr);
++
++ if (input_x >= box_width) {
++ scroll = input_x - box_width + 1;
++ input_x = box_width - 1;
++ for (i = 0; i < box_width - 1; i++)
++ waddch (dialog, instr[scroll + i]);
++ } else
++ waddstr (dialog, instr);
++
++ wmove (dialog, box_y, box_x + input_x);
++
++ wrefresh (dialog);
++
++ while (key != ESC) {
++ key = wgetch (dialog);
++
++ if (button == -1) { /* Input box selected */
++ switch (key) {
++ case TAB:
++ case KEY_UP:
++ case KEY_DOWN:
++ break;
++ case KEY_LEFT:
++ continue;
++ case KEY_RIGHT:
++ continue;
++ case KEY_BACKSPACE:
++ case 127:
++ if (input_x || scroll) {
++ wattrset (dialog, inputbox_attr);
++ if (!input_x) {
++ scroll = scroll < box_width - 1 ?
++ 0 : scroll - (box_width - 1);
++ wmove (dialog, box_y, box_x);
++ for (i = 0; i < box_width; i++)
++ waddch (dialog, instr[scroll + input_x + i] ?
++ instr[scroll + input_x + i] : ' ');
++ input_x = strlen (instr) - scroll;
++ } else
++ input_x--;
++ instr[scroll + input_x] = '\0';
++ mvwaddch (dialog, box_y, input_x + box_x, ' ');
++ wmove (dialog, box_y, input_x + box_x);
++ wrefresh (dialog);
++ }
++ continue;
++ default:
++ if (key < 0x100 && isprint (key)) {
++ if (scroll + input_x < MAX_LEN) {
++ wattrset (dialog, inputbox_attr);
++ instr[scroll + input_x] = key;
++ instr[scroll + input_x + 1] = '\0';
++ if (input_x == box_width - 1) {
++ scroll++;
++ wmove (dialog, box_y, box_x);
++ for (i = 0; i < box_width - 1; i++)
++ waddch (dialog, instr[scroll + i]);
++ } else {
++ wmove (dialog, box_y, input_x++ + box_x);
++ waddch (dialog, key);
++ }
++ wrefresh (dialog);
++ } else
++ flash (); /* Alarm user about overflow */
++ continue;
++ }
++ }
++ }
++ switch (key) {
++ case 'O':
++ case 'o':
++ delwin (dialog);
++ return 0;
++ case 'H':
++ case 'h':
++ delwin (dialog);
++ return 1;
++ case KEY_UP:
++ case KEY_LEFT:
++ switch (button) {
++ case -1:
++ button = 1; /* Indicates "Cancel" button is selected */
++ print_buttons(dialog, height, width, 1);
++ break;
++ case 0:
++ button = -1; /* Indicates input box is selected */
++ print_buttons(dialog, height, width, 0);
++ wmove (dialog, box_y, box_x + input_x);
++ wrefresh (dialog);
++ break;
++ case 1:
++ button = 0; /* Indicates "OK" button is selected */
++ print_buttons(dialog, height, width, 0);
++ break;
++ }
++ break;
++ case TAB:
++ case KEY_DOWN:
++ case KEY_RIGHT:
++ switch (button) {
++ case -1:
++ button = 0; /* Indicates "OK" button is selected */
++ print_buttons(dialog, height, width, 0);
++ break;
++ case 0:
++ button = 1; /* Indicates "Cancel" button is selected */
++ print_buttons(dialog, height, width, 1);
++ break;
++ case 1:
++ button = -1; /* Indicates input box is selected */
++ print_buttons(dialog, height, width, 0);
++ wmove (dialog, box_y, box_x + input_x);
++ wrefresh (dialog);
++ break;
++ }
++ break;
++ case ' ':
++ case '\n':
++ delwin (dialog);
++ return (button == -1 ? 0 : button);
++ case 'X':
++ case 'x':
++ key = ESC;
++ case ESC:
++ break;
++ }
++ }
++
++ delwin (dialog);
++ return -1; /* ESC pressed */
++}
+diff -Nur busybox-1.00/scripts/config/lxdialog/menubox.c busybox/scripts/config/lxdialog/menubox.c
+--- busybox-1.00/scripts/config/lxdialog/menubox.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/scripts/config/lxdialog/menubox.c 2005-06-04 08:20:02.000000000 +0200
+@@ -0,0 +1,438 @@
++/*
++ * menubox.c -- implements the menu box
++ *
++ * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
++ * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcapw@cfw.com)
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version 2
++ * of the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
++ */
++
++/*
++ * Changes by Clifford Wolf (god@clifford.at)
++ *
++ * [ 1998-06-13 ]
++ *
++ * *) A bugfix for the Page-Down problem
++ *
++ * *) Formerly when I used Page Down and Page Up, the cursor would be set
++ * to the first position in the menu box. Now lxdialog is a bit
++ * smarter and works more like other menu systems (just have a look at
++ * it).
++ *
++ * *) Formerly if I selected something my scrolling would be broken because
++ * lxdialog is re-invoked by the Menuconfig shell script, can't
++ * remember the last scrolling position, and just sets it so that the
++ * cursor is at the bottom of the box. Now it writes the temporary file
++ * lxdialog.scrltmp which contains this information. The file is
++ * deleted by lxdialog if the user leaves a submenu or enters a new
++ * one, but it would be nice if Menuconfig could make another "rm -f"
++ * just to be sure. Just try it out - you will recognise a difference!
++ *
++ * [ 1998-06-14 ]
++ *
++ * *) Now lxdialog is crash-safe against broken "lxdialog.scrltmp" files
++ * and menus change their size on the fly.
++ *
++ * *) If for some reason the last scrolling position is not saved by
++ * lxdialog, it sets the scrolling so that the selected item is in the
++ * middle of the menu box, not at the bottom.
++ *
++ * 02 January 1999, Michael Elizabeth Chastain (mec@shout.net)
++ * Reset 'scroll' to 0 if the value from lxdialog.scrltmp is bogus.
++ * This fixes a bug in Menuconfig where using ' ' to descend into menus
++ * would leave mis-synchronized lxdialog.scrltmp files lying around,
++ * fscanf would read in 'scroll', and eventually that value would get used.
++ */
++
++#include "dialog.h"
++
++static int menu_width, item_x;
++
++/*
++ * Print menu item
++ */
++static void
++print_item (WINDOW * win, const char *item, int choice, int selected, int hotkey)
++{
++ int j;
++ char menu_item[menu_width+1];
++
++ strncpy(menu_item, item, menu_width);
++ menu_item[menu_width] = 0;
++ j = first_alpha(menu_item, "YyNnMmHh");
++
++ /* Clear 'residue' of last item */
++ wattrset (win, menubox_attr);
++ wmove (win, choice, 0);
++#if OLD_NCURSES
++ {
++ int i;
++ for (i = 0; i < menu_width; i++)
++ waddch (win, ' ');
++ }
++#else
++ wclrtoeol(win);
++#endif
++ wattrset (win, selected ? item_selected_attr : item_attr);
++ mvwaddstr (win, choice, item_x, menu_item);
++ if (hotkey) {
++ wattrset (win, selected ? tag_key_selected_attr : tag_key_attr);
++ mvwaddch(win, choice, item_x+j, menu_item[j]);
++ }
++ if (selected) {
++ wmove (win, choice, item_x+1);
++ wrefresh (win);
++ }
++}
++
++/*
++ * Print the scroll indicators.
++ */
++static void
++print_arrows (WINDOW * win, int item_no, int scroll,
++ int y, int x, int height)
++{
++ int cur_y, cur_x;
++
++ getyx(win, cur_y, cur_x);
++
++ wmove(win, y, x);
++
++ if (scroll > 0) {
++ wattrset (win, uarrow_attr);
++ waddch (win, ACS_UARROW);
++ waddstr (win, "(-)");
++ }
++ else {
++ wattrset (win, menubox_attr);
++ waddch (win, ACS_HLINE);
++ waddch (win, ACS_HLINE);
++ waddch (win, ACS_HLINE);
++ waddch (win, ACS_HLINE);
++ }
++
++ y = y + height + 1;
++ wmove(win, y, x);
++
++ if ((height < item_no) && (scroll + height < item_no)) {
++ wattrset (win, darrow_attr);
++ waddch (win, ACS_DARROW);
++ waddstr (win, "(+)");
++ }
++ else {
++ wattrset (win, menubox_border_attr);
++ waddch (win, ACS_HLINE);
++ waddch (win, ACS_HLINE);
++ waddch (win, ACS_HLINE);
++ waddch (win, ACS_HLINE);
++ }
++
++ wmove(win, cur_y, cur_x);
++}
++
++/*
++ * Display the termination buttons.
++ */
++static void
++print_buttons (WINDOW *win, int height, int width, int selected)
++{
++ int x = width / 2 - 16;
++ int y = height - 2;
++
++ print_button (win, "Select", y, x, selected == 0);
++ print_button (win, " Exit ", y, x + 12, selected == 1);
++ print_button (win, " Help ", y, x + 24, selected == 2);
++
++ wmove(win, y, x+1+12*selected);
++ wrefresh (win);
++}
++
++/*
++ * Display a menu for choosing among a number of options
++ */
++int
++dialog_menu (const char *title, const char *prompt, int height, int width,
++ int menu_height, const char *current, int item_no,
++ struct dialog_list_item ** items)
++{
++ int i, j, x, y, box_x, box_y;
++ int key = 0, button = 0, scroll = 0, choice = 0, first_item = 0, max_choice;
++ WINDOW *dialog, *menu;
++ FILE *f;
++
++ max_choice = MIN (menu_height, item_no);
++
++ /* center dialog box on screen */
++ x = (COLS - width) / 2;
++ y = (LINES - height) / 2;
++
++ draw_shadow (stdscr, y, x, height, width);
++
++ dialog = newwin (height, width, y, x);
++ keypad (dialog, TRUE);
++
++ draw_box (dialog, 0, 0, height, width, dialog_attr, border_attr);
++ wattrset (dialog, border_attr);
++ mvwaddch (dialog, height - 3, 0, ACS_LTEE);
++ for (i = 0; i < width - 2; i++)
++ waddch (dialog, ACS_HLINE);
++ wattrset (dialog, dialog_attr);
++ wbkgdset (dialog, dialog_attr & A_COLOR);
++ waddch (dialog, ACS_RTEE);
++
++ if (title != NULL && strlen(title) >= width-2 ) {
++ /* truncate long title -- mec */
++ char * title2 = malloc(width-2+1);
++ memcpy( title2, title, width-2 );
++ title2[width-2] = '\0';
++ title = title2;
++ }
++
++ if (title != NULL) {
++ wattrset (dialog, title_attr);
++ mvwaddch (dialog, 0, (width - strlen(title))/2 - 1, ' ');
++ waddstr (dialog, (char *)title);
++ waddch (dialog, ' ');
++ }
++
++ wattrset (dialog, dialog_attr);
++ print_autowrap (dialog, prompt, width - 2, 1, 3);
++
++ menu_width = width - 6;
++ box_y = height - menu_height - 5;
++ box_x = (width - menu_width) / 2 - 1;
++
++ /* create new window for the menu */
++ menu = subwin (dialog, menu_height, menu_width,
++ y + box_y + 1, x + box_x + 1);
++ keypad (menu, TRUE);
++
++ /* draw a box around the menu items */
++ draw_box (dialog, box_y, box_x, menu_height + 2, menu_width + 2,
++ menubox_border_attr, menubox_attr);
++
++ /*
++ * Find length of longest item in order to center menu.
++ * Set 'choice' to default item.
++ */
++ item_x = 0;
++ for (i = 0; i < item_no; i++) {
++ item_x = MAX (item_x, MIN(menu_width, strlen (items[i]->name) + 2));
++ if (strcmp(current, items[i]->tag) == 0) choice = i;
++ }
++
++ item_x = (menu_width - item_x) / 2;
++
++ /* get the scroll info from the temp file */
++ if ( (f=fopen("lxdialog.scrltmp","r")) != NULL ) {
++ if ( (fscanf(f,"%d\n",&scroll) == 1) && (scroll <= choice) &&
++ (scroll+max_choice > choice) && (scroll >= 0) &&
++ (scroll+max_choice <= item_no) ) {
++ first_item = scroll;
++ choice = choice - scroll;
++ fclose(f);
++ } else {
++ scroll=0;
++ remove("lxdialog.scrltmp");
++ fclose(f);
++ f=NULL;
++ }
++ }
++ if ( (choice >= max_choice) || (f==NULL && choice >= max_choice/2) ) {
++ if (choice >= item_no-max_choice/2)
++ scroll = first_item = item_no-max_choice;
++ else
++ scroll = first_item = choice - max_choice/2;
++ choice = choice - scroll;
++ }
++
++ /* Print the menu */
++ for (i=0; i < max_choice; i++) {
++ print_item (menu, items[first_item + i]->name, i, i == choice,
++ (items[first_item + i]->tag[0] != ':'));
++ }
++
++ wnoutrefresh (menu);
++
++ print_arrows(dialog, item_no, scroll,
++ box_y, box_x+item_x+1, menu_height);
++
++ print_buttons (dialog, height, width, 0);
++ wmove (menu, choice, item_x+1);
++ wrefresh (menu);
++
++ while (key != ESC) {
++ key = wgetch(menu);
++
++ if (key < 256 && isalpha(key)) key = tolower(key);
++
++ if (strchr("ynmh", key))
++ i = max_choice;
++ else {
++ for (i = choice+1; i < max_choice; i++) {
++ j = first_alpha(items[scroll + i]->name, "YyNnMmHh");
++ if (key == tolower(items[scroll + i]->name[j]))
++ break;
++ }
++ if (i == max_choice)
++ for (i = 0; i < max_choice; i++) {
++ j = first_alpha(items[scroll + i]->name, "YyNnMmHh");
++ if (key == tolower(items[scroll + i]->name[j]))
++ break;
++ }
++ }
++
++ if (i < max_choice ||
++ key == KEY_UP || key == KEY_DOWN ||
++ key == '-' || key == '+' ||
++ key == KEY_PPAGE || key == KEY_NPAGE) {
++
++ print_item (menu, items[scroll + choice]->name, choice, FALSE,
++ (items[scroll + choice]->tag[0] != ':'));
++
++ if (key == KEY_UP || key == '-') {
++ if (choice < 2 && scroll) {
++ /* Scroll menu down */
++ scrollok (menu, TRUE);
++ wscrl (menu, -1);
++ scrollok (menu, FALSE);
++
++ scroll--;
++
++ print_item (menu, items[scroll]->name, 0, FALSE,
++ (items[scroll]->tag[0] != ':'));
++ } else
++ choice = MAX(choice - 1, 0);
++
++ } else if (key == KEY_DOWN || key == '+') {
++
++ print_item (menu, items[scroll + choice]->name, choice, FALSE,
++ (items[scroll + choice]->tag[0] != ':'));
++
++ if ((choice > max_choice-3) &&
++ (scroll + max_choice < item_no)
++ ) {
++ /* Scroll menu up */
++ scrollok (menu, TRUE);
++ scroll (menu);
++ scrollok (menu, FALSE);
++
++ scroll++;
++
++ print_item (menu, items[scroll + max_choice - 1]->name,
++ max_choice-1, FALSE,
++ (items[scroll + max_choice - 1]->tag[0] != ':'));
++ } else
++ choice = MIN(choice+1, max_choice-1);
++
++ } else if (key == KEY_PPAGE) {
++ scrollok (menu, TRUE);
++ for (i=0; (i < max_choice); i++) {
++ if (scroll > 0) {
++ wscrl (menu, -1);
++ scroll--;
++ print_item (menu, items[scroll]->name, 0, FALSE,
++ (items[scroll]->tag[0] != ':'));
++ } else {
++ if (choice > 0)
++ choice--;
++ }
++ }
++ scrollok (menu, FALSE);
++
++ } else if (key == KEY_NPAGE) {
++ for (i=0; (i < max_choice); i++) {
++ if (scroll+max_choice < item_no) {
++ scrollok (menu, TRUE);
++ scroll(menu);
++ scrollok (menu, FALSE);
++ scroll++;
++ print_item (menu, items[scroll + max_choice - 1]->name,
++ max_choice-1, FALSE,
++ (items[scroll + max_choice - 1]->tag[0] != ':'));
++ } else {
++ if (choice+1 < max_choice)
++ choice++;
++ }
++ }
++
++ } else
++ choice = i;
++
++ print_item (menu, items[scroll + choice]->name, choice, TRUE,
++ (items[scroll + choice]->tag[0] != ':'));
++
++ print_arrows(dialog, item_no, scroll,
++ box_y, box_x+item_x+1, menu_height);
++
++ wnoutrefresh (dialog);
++ wrefresh (menu);
++
++ continue; /* wait for another key press */
++ }
++
++ switch (key) {
++ case KEY_LEFT:
++ case TAB:
++ case KEY_RIGHT:
++ button = ((key == KEY_LEFT ? --button : ++button) < 0)
++ ? 2 : (button > 2 ? 0 : button);
++
++ print_buttons(dialog, height, width, button);
++ wrefresh (menu);
++ break;
++ case ' ':
++ case 's':
++ case 'y':
++ case 'n':
++ case 'm':
++ case '/':
++ /* save scroll info */
++ if ( (f=fopen("lxdialog.scrltmp","w")) != NULL ) {
++ fprintf(f,"%d\n",scroll);
++ fclose(f);
++ }
++ delwin (dialog);
++ items[scroll + choice]->selected = 1;
++ switch (key) {
++ case 's': return 3;
++ case 'y': return 3;
++ case 'n': return 4;
++ case 'm': return 5;
++ case ' ': return 6;
++ case '/': return 7;
++ }
++ return 0;
++ case 'h':
++ case '?':
++ button = 2;
++ case '\n':
++ delwin (dialog);
++ items[scroll + choice]->selected = 1;
++
++ remove("lxdialog.scrltmp");
++ return button;
++ case 'e':
++ case 'x':
++ key = ESC;
++ case ESC:
++ break;
++ }
++ }
++
++ delwin (dialog);
++ remove("lxdialog.scrltmp");
++ return -1; /* ESC pressed */
++}
+diff -Nur busybox-1.00/scripts/config/lxdialog/msgbox.c busybox/scripts/config/lxdialog/msgbox.c
+--- busybox-1.00/scripts/config/lxdialog/msgbox.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/scripts/config/lxdialog/msgbox.c 2005-06-04 08:20:02.000000000 +0200
+@@ -0,0 +1,85 @@
++/*
++ * msgbox.c -- implements the message box and info box
++ *
++ * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
++ * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcapw@cfw.com)
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version 2
++ * of the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
++ */
++
++#include "dialog.h"
++
++/*
++ * Display a message box. Program will pause and display an "OK" button
++ * if the parameter 'pause' is non-zero.
++ */
++int
++dialog_msgbox (const char *title, const char *prompt, int height, int width,
++ int pause)
++{
++ int i, x, y, key = 0;
++ WINDOW *dialog;
++
++ /* center dialog box on screen */
++ x = (COLS - width) / 2;
++ y = (LINES - height) / 2;
++
++ draw_shadow (stdscr, y, x, height, width);
++
++ dialog = newwin (height, width, y, x);
++ keypad (dialog, TRUE);
++
++ draw_box (dialog, 0, 0, height, width, dialog_attr, border_attr);
++
++ if (title != NULL && strlen(title) >= width-2 ) {
++ /* truncate long title -- mec */
++ char * title2 = malloc(width-2+1);
++ memcpy( title2, title, width-2 );
++ title2[width-2] = '\0';
++ title = title2;
++ }
++
++ if (title != NULL) {
++ wattrset (dialog, title_attr);
++ mvwaddch (dialog, 0, (width - strlen(title))/2 - 1, ' ');
++ waddstr (dialog, (char *)title);
++ waddch (dialog, ' ');
++ }
++ wattrset (dialog, dialog_attr);
++ print_autowrap (dialog, prompt, width - 2, 1, 2);
++
++ if (pause) {
++ wattrset (dialog, border_attr);
++ mvwaddch (dialog, height - 3, 0, ACS_LTEE);
++ for (i = 0; i < width - 2; i++)
++ waddch (dialog, ACS_HLINE);
++ wattrset (dialog, dialog_attr);
++ waddch (dialog, ACS_RTEE);
++
++ print_button (dialog, " Ok ",
++ height - 2, width / 2 - 4, TRUE);
++
++ wrefresh (dialog);
++ while (key != ESC && key != '\n' && key != ' ' &&
++ key != 'O' && key != 'o' && key != 'X' && key != 'x')
++ key = wgetch (dialog);
++ } else {
++ key = '\n';
++ wrefresh (dialog);
++ }
++
++ delwin (dialog);
++ return key == ESC ? -1 : 0;
++}
+diff -Nur busybox-1.00/scripts/config/lxdialog/textbox.c busybox/scripts/config/lxdialog/textbox.c
+--- busybox-1.00/scripts/config/lxdialog/textbox.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/scripts/config/lxdialog/textbox.c 2005-06-04 08:20:02.000000000 +0200
+@@ -0,0 +1,556 @@
++/*
++ * textbox.c -- implements the text box
++ *
++ * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
++ * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com)
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version 2
++ * of the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
++ */
++
++#include "dialog.h"
++
++static void back_lines (int n);
++static void print_page (WINDOW * win, int height, int width);
++static void print_line (WINDOW * win, int row, int width);
++static char *get_line (void);
++static void print_position (WINDOW * win, int height, int width);
++
++static int hscroll, fd, file_size, bytes_read;
++static int begin_reached = 1, end_reached, page_length;
++static char *buf, *page;
++
++/*
++ * Display text from a file in a dialog box.
++ */
++int
++dialog_textbox (const char *title, const char *file, int height, int width)
++{
++ int i, x, y, cur_x, cur_y, fpos, key = 0;
++ int passed_end;
++ char search_term[MAX_LEN + 1];
++ WINDOW *dialog, *text;
++
++ search_term[0] = '\0'; /* no search term entered yet */
++
++ /* Open input file for reading */
++ if ((fd = open (file, O_RDONLY)) == -1) {
++ endwin ();
++ fprintf (stderr,
++ "\nCan't open input file in dialog_textbox().\n");
++ exit (-1);
++ }
++ /* Get file size. Actually, 'file_size' is the real file size - 1,
++ since it's only the last byte offset from the beginning */
++ if ((file_size = lseek (fd, 0, SEEK_END)) == -1) {
++ endwin ();
++ fprintf (stderr, "\nError getting file size in dialog_textbox().\n");
++ exit (-1);
++ }
++ /* Restore file pointer to beginning of file after getting file size */
++ if (lseek (fd, 0, SEEK_SET) == -1) {
++ endwin ();
++ fprintf (stderr, "\nError moving file pointer in dialog_textbox().\n");
++ exit (-1);
++ }
++ /* Allocate space for read buffer */
++ if ((buf = malloc (BUF_SIZE + 1)) == NULL) {
++ endwin ();
++ fprintf (stderr, "\nCan't allocate memory in dialog_textbox().\n");
++ exit (-1);
++ }
++ if ((bytes_read = read (fd, buf, BUF_SIZE)) == -1) {
++ endwin ();
++ fprintf (stderr, "\nError reading file in dialog_textbox().\n");
++ exit (-1);
++ }
++ buf[bytes_read] = '\0'; /* mark end of valid data */
++ page = buf; /* page is pointer to start of page to be displayed */
++
++ /* center dialog box on screen */
++ x = (COLS - width) / 2;
++ y = (LINES - height) / 2;
++
++
++ draw_shadow (stdscr, y, x, height, width);
++
++ dialog = newwin (height, width, y, x);
++ keypad (dialog, TRUE);
++
++ /* Create window for text region, used for scrolling text */
++ text = subwin (dialog, height - 4, width - 2, y + 1, x + 1);
++ wattrset (text, dialog_attr);
++ wbkgdset (text, dialog_attr & A_COLOR);
++
++ keypad (text, TRUE);
++
++ /* register the new window, along with its borders */
++ draw_box (dialog, 0, 0, height, width, dialog_attr, border_attr);
++
++ wattrset (dialog, border_attr);
++ mvwaddch (dialog, height-3, 0, ACS_LTEE);
++ for (i = 0; i < width - 2; i++)
++ waddch (dialog, ACS_HLINE);
++ wattrset (dialog, dialog_attr);
++ wbkgdset (dialog, dialog_attr & A_COLOR);
++ waddch (dialog, ACS_RTEE);
++
++ if (title != NULL && strlen(title) >= width-2 ) {
++ /* truncate long title -- mec */
++ char * title2 = malloc(width-2+1);
++ memcpy( title2, title, width-2 );
++ title2[width-2] = '\0';
++ title = title2;
++ }
++
++ if (title != NULL) {
++ wattrset (dialog, title_attr);
++ mvwaddch (dialog, 0, (width - strlen(title))/2 - 1, ' ');
++ waddstr (dialog, (char *)title);
++ waddch (dialog, ' ');
++ }
++ print_button (dialog, " Exit ", height - 2, width / 2 - 4, TRUE);
++ wnoutrefresh (dialog);
++ getyx (dialog, cur_y, cur_x); /* Save cursor position */
++
++ /* Print first page of text */
++ attr_clear (text, height - 4, width - 2, dialog_attr);
++ print_page (text, height - 4, width - 2);
++ print_position (dialog, height, width);
++ wmove (dialog, cur_y, cur_x); /* Restore cursor position */
++ wrefresh (dialog);
++
++ while ((key != ESC) && (key != '\n')) {
++ key = wgetch (dialog);
++ switch (key) {
++ case 'E': /* Exit */
++ case 'e':
++ case 'X':
++ case 'x':
++ delwin (dialog);
++ free (buf);
++ close (fd);
++ return 0;
++ case 'g': /* First page */
++ case KEY_HOME:
++ if (!begin_reached) {
++ begin_reached = 1;
++ /* First page not in buffer? */
++ if ((fpos = lseek (fd, 0, SEEK_CUR)) == -1) {
++ endwin ();
++ fprintf (stderr,
++ "\nError moving file pointer in dialog_textbox().\n");
++ exit (-1);
++ }
++ if (fpos > bytes_read) { /* Yes, we have to read it in */
++ if (lseek (fd, 0, SEEK_SET) == -1) {
++ endwin ();
++ fprintf (stderr, "\nError moving file pointer in "
++ "dialog_textbox().\n");
++ exit (-1);
++ }
++ if ((bytes_read = read (fd, buf, BUF_SIZE)) == -1) {
++ endwin ();
++ fprintf (stderr,
++ "\nError reading file in dialog_textbox().\n");
++ exit (-1);
++ }
++ buf[bytes_read] = '\0';
++ }
++ page = buf;
++ print_page (text, height - 4, width - 2);
++ print_position (dialog, height, width);
++ wmove (dialog, cur_y, cur_x); /* Restore cursor position */
++ wrefresh (dialog);
++ }
++ break;
++ case 'G': /* Last page */
++ case KEY_END:
++
++ end_reached = 1;
++ /* Last page not in buffer? */
++ if ((fpos = lseek (fd, 0, SEEK_CUR)) == -1) {
++ endwin ();
++ fprintf (stderr,
++ "\nError moving file pointer in dialog_textbox().\n");
++ exit (-1);
++ }
++ if (fpos < file_size) { /* Yes, we have to read it in */
++ if (lseek (fd, -BUF_SIZE, SEEK_END) == -1) {
++ endwin ();
++ fprintf (stderr,
++ "\nError moving file pointer in dialog_textbox().\n");
++ exit (-1);
++ }
++ if ((bytes_read = read (fd, buf, BUF_SIZE)) == -1) {
++ endwin ();
++ fprintf (stderr,
++ "\nError reading file in dialog_textbox().\n");
++ exit (-1);
++ }
++ buf[bytes_read] = '\0';
++ }
++ page = buf + bytes_read;
++ back_lines (height - 4);
++ print_page (text, height - 4, width - 2);
++ print_position (dialog, height, width);
++ wmove (dialog, cur_y, cur_x); /* Restore cursor position */
++ wrefresh (dialog);
++ break;
++ case 'K': /* Previous line */
++ case 'k':
++ case KEY_UP:
++ if (!begin_reached) {
++ back_lines (page_length + 1);
++
++ /* We don't call print_page() here but use scrolling to ensure
++ faster screen update. However, 'end_reached' and
++ 'page_length' should still be updated, and 'page' should
++ point to start of next page. This is done by calling
++ get_line() in the following 'for' loop. */
++ scrollok (text, TRUE);
++ wscrl (text, -1); /* Scroll text region down one line */
++ scrollok (text, FALSE);
++ page_length = 0;
++ passed_end = 0;
++ for (i = 0; i < height - 4; i++) {
++ if (!i) {
++ /* print first line of page */
++ print_line (text, 0, width - 2);
++ wnoutrefresh (text);
++ } else
++ /* Called to update 'end_reached' and 'page' */
++ get_line ();
++ if (!passed_end)
++ page_length++;
++ if (end_reached && !passed_end)
++ passed_end = 1;
++ }
++
++ print_position (dialog, height, width);
++ wmove (dialog, cur_y, cur_x); /* Restore cursor position */
++ wrefresh (dialog);
++ }
++ break;
++ case 'B': /* Previous page */
++ case 'b':
++ case KEY_PPAGE:
++ if (begin_reached)
++ break;
++ back_lines (page_length + height - 4);
++ print_page (text, height - 4, width - 2);
++ print_position (dialog, height, width);
++ wmove (dialog, cur_y, cur_x);
++ wrefresh (dialog);
++ break;
++ case 'J': /* Next line */
++ case 'j':
++ case KEY_DOWN:
++ if (!end_reached) {
++ begin_reached = 0;
++ scrollok (text, TRUE);
++ scroll (text); /* Scroll text region up one line */
++ scrollok (text, FALSE);
++ print_line (text, height - 5, width - 2);
++ wnoutrefresh (text);
++ print_position (dialog, height, width);
++ wmove (dialog, cur_y, cur_x); /* Restore cursor position */
++ wrefresh (dialog);
++ }
++ break;
++ case KEY_NPAGE: /* Next page */
++ case ' ':
++ if (end_reached)
++ break;
++
++ begin_reached = 0;
++ print_page (text, height - 4, width - 2);
++ print_position (dialog, height, width);
++ wmove (dialog, cur_y, cur_x);
++ wrefresh (dialog);
++ break;
++ case '0': /* Beginning of line */
++ case 'H': /* Scroll left */
++ case 'h':
++ case KEY_LEFT:
++ if (hscroll <= 0)
++ break;
++
++ if (key == '0')
++ hscroll = 0;
++ else
++ hscroll--;
++ /* Reprint current page to scroll horizontally */
++ back_lines (page_length);
++ print_page (text, height - 4, width - 2);
++ wmove (dialog, cur_y, cur_x);
++ wrefresh (dialog);
++ break;
++ case 'L': /* Scroll right */
++ case 'l':
++ case KEY_RIGHT:
++ if (hscroll >= MAX_LEN)
++ break;
++ hscroll++;
++ /* Reprint current page to scroll horizontally */
++ back_lines (page_length);
++ print_page (text, height - 4, width - 2);
++ wmove (dialog, cur_y, cur_x);
++ wrefresh (dialog);
++ break;
++ case ESC:
++ break;
++ }
++ }
++
++ delwin (dialog);
++ free (buf);
++ close (fd);
++ return 1; /* ESC pressed */
++}
++
++/*
++ * Go back 'n' lines in text file. Called by dialog_textbox().
++ * 'page' will be updated to point to the desired line in 'buf'.
++ */
++static void
++back_lines (int n)
++{
++ int i, fpos;
++
++ begin_reached = 0;
++ /* We have to distinguish between end_reached and !end_reached
++ since at end of file, the line is not ended by a '\n'.
++ The code inside 'if' basically does a '--page' to move one
++ character backward so as to skip '\n' of the previous line */
++ if (!end_reached) {
++ /* Either beginning of buffer or beginning of file reached? */
++ if (page == buf) {
++ if ((fpos = lseek (fd, 0, SEEK_CUR)) == -1) {
++ endwin ();
++ fprintf (stderr, "\nError moving file pointer in "
++ "back_lines().\n");
++ exit (-1);
++ }
++ if (fpos > bytes_read) { /* Not beginning of file yet */
++ /* We've reached beginning of buffer, but not beginning of
++ file yet, so read previous part of file into buffer.
++ Note that we only move backward for BUF_SIZE/2 bytes,
++ but not BUF_SIZE bytes to avoid re-reading again in
++ print_page() later */
++ /* Really possible to move backward BUF_SIZE/2 bytes? */
++ if (fpos < BUF_SIZE / 2 + bytes_read) {
++ /* No, move less then */
++ if (lseek (fd, 0, SEEK_SET) == -1) {
++ endwin ();
++ fprintf (stderr, "\nError moving file pointer in "
++ "back_lines().\n");
++ exit (-1);
++ }
++ page = buf + fpos - bytes_read;
++ } else { /* Move backward BUF_SIZE/2 bytes */
++ if (lseek (fd, -(BUF_SIZE / 2 + bytes_read), SEEK_CUR)
++ == -1) {
++ endwin ();
++ fprintf (stderr, "\nError moving file pointer "
++ "in back_lines().\n");
++ exit (-1);
++ }
++ page = buf + BUF_SIZE / 2;
++ }
++ if ((bytes_read = read (fd, buf, BUF_SIZE)) == -1) {
++ endwin ();
++ fprintf (stderr, "\nError reading file in back_lines().\n");
++ exit (-1);
++ }
++ buf[bytes_read] = '\0';
++ } else { /* Beginning of file reached */
++ begin_reached = 1;
++ return;
++ }
++ }
++ if (*(--page) != '\n') { /* '--page' here */
++ /* Something's wrong... */
++ endwin ();
++ fprintf (stderr, "\nInternal error in back_lines().\n");
++ exit (-1);
++ }
++ }
++ /* Go back 'n' lines */
++ for (i = 0; i < n; i++)
++ do {
++ if (page == buf) {
++ if ((fpos = lseek (fd, 0, SEEK_CUR)) == -1) {
++ endwin ();
++ fprintf (stderr,
++ "\nError moving file pointer in back_lines().\n");
++ exit (-1);
++ }
++ if (fpos > bytes_read) {
++ /* Really possible to move backward BUF_SIZE/2 bytes? */
++ if (fpos < BUF_SIZE / 2 + bytes_read) {
++ /* No, move less then */
++ if (lseek (fd, 0, SEEK_SET) == -1) {
++ endwin ();
++ fprintf (stderr, "\nError moving file pointer "
++ "in back_lines().\n");
++ exit (-1);
++ }
++ page = buf + fpos - bytes_read;
++ } else { /* Move backward BUF_SIZE/2 bytes */
++ if (lseek (fd, -(BUF_SIZE / 2 + bytes_read),
++ SEEK_CUR) == -1) {
++ endwin ();
++ fprintf (stderr, "\nError moving file pointer"
++ " in back_lines().\n");
++ exit (-1);
++ }
++ page = buf + BUF_SIZE / 2;
++ }
++ if ((bytes_read = read (fd, buf, BUF_SIZE)) == -1) {
++ endwin ();
++ fprintf (stderr, "\nError reading file in "
++ "back_lines().\n");
++ exit (-1);
++ }
++ buf[bytes_read] = '\0';
++ } else { /* Beginning of file reached */
++ begin_reached = 1;
++ return;
++ }
++ }
++ } while (*(--page) != '\n');
++ page++;
++}
++
++/*
++ * Print a new page of text. Called by dialog_textbox().
++ */
++static void
++print_page (WINDOW * win, int height, int width)
++{
++ int i, passed_end = 0;
++
++ page_length = 0;
++ for (i = 0; i < height; i++) {
++ print_line (win, i, width);
++ if (!passed_end)
++ page_length++;
++ if (end_reached && !passed_end)
++ passed_end = 1;
++ }
++ wnoutrefresh (win);
++}
++
++/*
++ * Print a new line of text. Called by dialog_textbox() and print_page().
++ */
++static void
++print_line (WINDOW * win, int row, int width)
++{
++ int y, x;
++ char *line;
++
++ line = get_line ();
++ line += MIN (strlen (line), hscroll); /* Scroll horizontally */
++ wmove (win, row, 0); /* move cursor to correct line */
++ waddch (win, ' ');
++ waddnstr (win, line, MIN (strlen (line), width - 2));
++
++ getyx (win, y, x);
++ /* Clear 'residue' of previous line */
++#if OLD_NCURSES
++ {
++ int i;
++ for (i = 0; i < width - x; i++)
++ waddch (win, ' ');
++ }
++#else
++ wclrtoeol(win);
++#endif
++}
++
++/*
++ * Return current line of text. Called by dialog_textbox() and print_line().
++ * 'page' should point to start of current line before calling, and will be
++ * updated to point to start of next line.
++ */
++static char *
++get_line (void)
++{
++ int i = 0, fpos;
++ static char line[MAX_LEN + 1];
++
++ end_reached = 0;
++ while (*page != '\n') {
++ if (*page == '\0') {
++ /* Either end of file or end of buffer reached */
++ if ((fpos = lseek (fd, 0, SEEK_CUR)) == -1) {
++ endwin ();
++ fprintf (stderr, "\nError moving file pointer in "
++ "get_line().\n");
++ exit (-1);
++ }
++ if (fpos < file_size) { /* Not end of file yet */
++ /* We've reached end of buffer, but not end of file yet,
++ so read next part of file into buffer */
++ if ((bytes_read = read (fd, buf, BUF_SIZE)) == -1) {
++ endwin ();
++ fprintf (stderr, "\nError reading file in get_line().\n");
++ exit (-1);
++ }
++ buf[bytes_read] = '\0';
++ page = buf;
++ } else {
++ if (!end_reached)
++ end_reached = 1;
++ break;
++ }
++ } else if (i < MAX_LEN)
++ line[i++] = *(page++);
++ else {
++ /* Truncate lines longer than MAX_LEN characters */
++ if (i == MAX_LEN)
++ line[i++] = '\0';
++ page++;
++ }
++ }
++ if (i <= MAX_LEN)
++ line[i] = '\0';
++ if (!end_reached)
++ page++; /* move pass '\n' */
++
++ return line;
++}
++
++/*
++ * Print current position
++ */
++static void
++print_position (WINDOW * win, int height, int width)
++{
++ int fpos, percent;
++
++ if ((fpos = lseek (fd, 0, SEEK_CUR)) == -1) {
++ endwin ();
++ fprintf (stderr, "\nError moving file pointer in print_position().\n");
++ exit (-1);
++ }
++ wattrset (win, position_indicator_attr);
++ wbkgdset (win, position_indicator_attr & A_COLOR);
++ percent = !file_size ?
++ 100 : ((fpos - bytes_read + page - buf) * 100) / file_size;
++ wmove (win, height - 3, width - 9);
++ wprintw (win, "(%3d%%)", percent);
++}
+diff -Nur busybox-1.00/scripts/config/lxdialog/util.c busybox/scripts/config/lxdialog/util.c
+--- busybox-1.00/scripts/config/lxdialog/util.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/scripts/config/lxdialog/util.c 2005-06-04 08:20:02.000000000 +0200
+@@ -0,0 +1,375 @@
++/*
++ * util.c
++ *
++ * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
++ * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com)
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version 2
++ * of the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
++ */
++
++#include "dialog.h"
++
++
++/* use colors by default? */
++bool use_colors = 1;
++
++const char *backtitle = NULL;
++
++const char *dialog_result;
++
++/*
++ * Attribute values, default is for mono display
++ */
++chtype attributes[] =
++{
++ A_NORMAL, /* screen_attr */
++ A_NORMAL, /* shadow_attr */
++ A_NORMAL, /* dialog_attr */
++ A_BOLD, /* title_attr */
++ A_NORMAL, /* border_attr */
++ A_REVERSE, /* button_active_attr */
++ A_DIM, /* button_inactive_attr */
++ A_REVERSE, /* button_key_active_attr */
++ A_BOLD, /* button_key_inactive_attr */
++ A_REVERSE, /* button_label_active_attr */
++ A_NORMAL, /* button_label_inactive_attr */
++ A_NORMAL, /* inputbox_attr */
++ A_NORMAL, /* inputbox_border_attr */
++ A_NORMAL, /* searchbox_attr */
++ A_BOLD, /* searchbox_title_attr */
++ A_NORMAL, /* searchbox_border_attr */
++ A_BOLD, /* position_indicator_attr */
++ A_NORMAL, /* menubox_attr */
++ A_NORMAL, /* menubox_border_attr */
++ A_NORMAL, /* item_attr */
++ A_REVERSE, /* item_selected_attr */
++ A_BOLD, /* tag_attr */
++ A_REVERSE, /* tag_selected_attr */
++ A_BOLD, /* tag_key_attr */
++ A_REVERSE, /* tag_key_selected_attr */
++ A_BOLD, /* check_attr */
++ A_REVERSE, /* check_selected_attr */
++ A_BOLD, /* uarrow_attr */
++ A_BOLD /* darrow_attr */
++};
++
++
++#include "colors.h"
++
++/*
++ * Table of color values
++ */
++int color_table[][3] =
++{
++ {SCREEN_FG, SCREEN_BG, SCREEN_HL},
++ {SHADOW_FG, SHADOW_BG, SHADOW_HL},
++ {DIALOG_FG, DIALOG_BG, DIALOG_HL},
++ {TITLE_FG, TITLE_BG, TITLE_HL},
++ {BORDER_FG, BORDER_BG, BORDER_HL},
++ {BUTTON_ACTIVE_FG, BUTTON_ACTIVE_BG, BUTTON_ACTIVE_HL},
++ {BUTTON_INACTIVE_FG, BUTTON_INACTIVE_BG, BUTTON_INACTIVE_HL},
++ {BUTTON_KEY_ACTIVE_FG, BUTTON_KEY_ACTIVE_BG, BUTTON_KEY_ACTIVE_HL},
++ {BUTTON_KEY_INACTIVE_FG, BUTTON_KEY_INACTIVE_BG, BUTTON_KEY_INACTIVE_HL},
++ {BUTTON_LABEL_ACTIVE_FG, BUTTON_LABEL_ACTIVE_BG, BUTTON_LABEL_ACTIVE_HL},
++ {BUTTON_LABEL_INACTIVE_FG, BUTTON_LABEL_INACTIVE_BG,
++ BUTTON_LABEL_INACTIVE_HL},
++ {INPUTBOX_FG, INPUTBOX_BG, INPUTBOX_HL},
++ {INPUTBOX_BORDER_FG, INPUTBOX_BORDER_BG, INPUTBOX_BORDER_HL},
++ {SEARCHBOX_FG, SEARCHBOX_BG, SEARCHBOX_HL},
++ {SEARCHBOX_TITLE_FG, SEARCHBOX_TITLE_BG, SEARCHBOX_TITLE_HL},
++ {SEARCHBOX_BORDER_FG, SEARCHBOX_BORDER_BG, SEARCHBOX_BORDER_HL},
++ {POSITION_INDICATOR_FG, POSITION_INDICATOR_BG, POSITION_INDICATOR_HL},
++ {MENUBOX_FG, MENUBOX_BG, MENUBOX_HL},
++ {MENUBOX_BORDER_FG, MENUBOX_BORDER_BG, MENUBOX_BORDER_HL},
++ {ITEM_FG, ITEM_BG, ITEM_HL},
++ {ITEM_SELECTED_FG, ITEM_SELECTED_BG, ITEM_SELECTED_HL},
++ {TAG_FG, TAG_BG, TAG_HL},
++ {TAG_SELECTED_FG, TAG_SELECTED_BG, TAG_SELECTED_HL},
++ {TAG_KEY_FG, TAG_KEY_BG, TAG_KEY_HL},
++ {TAG_KEY_SELECTED_FG, TAG_KEY_SELECTED_BG, TAG_KEY_SELECTED_HL},
++ {CHECK_FG, CHECK_BG, CHECK_HL},
++ {CHECK_SELECTED_FG, CHECK_SELECTED_BG, CHECK_SELECTED_HL},
++ {UARROW_FG, UARROW_BG, UARROW_HL},
++ {DARROW_FG, DARROW_BG, DARROW_HL},
++}; /* color_table */
++
++/*
++ * Set window to attribute 'attr'
++ */
++void
++attr_clear (WINDOW * win, int height, int width, chtype attr)
++{
++ int i, j;
++
++ wattrset (win, attr);
++ for (i = 0; i < height; i++) {
++ wmove (win, i, 0);
++ for (j = 0; j < width; j++)
++ waddch (win, ' ');
++ }
++ touchwin (win);
++}
++
++void dialog_clear (void)
++{
++ attr_clear (stdscr, LINES, COLS, screen_attr);
++ /* Display background title if it exists ... - SLH */
++ if (backtitle != NULL) {
++ int i;
++
++ wattrset (stdscr, screen_attr);
++ mvwaddstr (stdscr, 0, 1, (char *)backtitle);
++ wmove (stdscr, 1, 1);
++ for (i = 1; i < COLS - 1; i++)
++ waddch (stdscr, ACS_HLINE);
++ }
++ wnoutrefresh (stdscr);
++}
++
++/*
++ * Do some initialization for dialog
++ */
++void
++init_dialog (void)
++{
++ initscr (); /* Init curses */
++ keypad (stdscr, TRUE);
++ cbreak ();
++ noecho ();
++
++
++ if (use_colors) /* Set up colors */
++ color_setup ();
++
++
++ dialog_clear ();
++}
++
++/*
++ * Setup for color display
++ */
++void
++color_setup (void)
++{
++ int i;
++
++ if (has_colors ()) { /* Terminal supports color? */
++ start_color ();
++
++ /* Initialize color pairs */
++ for (i = 0; i < ATTRIBUTE_COUNT; i++)
++ init_pair (i + 1, color_table[i][0], color_table[i][1]);
++
++ /* Setup color attributes */
++ for (i = 0; i < ATTRIBUTE_COUNT; i++)
++ attributes[i] = C_ATTR (color_table[i][2], i + 1);
++ }
++}
++
++/*
++ * End using dialog functions.
++ */
++void
++end_dialog (void)
++{
++ endwin ();
++}
++
++
++/*
++ * Print a string of text in a window, automatically wrap around to the
++ * next line if the string is too long to fit on one line. Newline
++ * characters '\n' are replaced by spaces. We start on a new line
++ * if there is no room for at least 4 nonblanks following a double-space.
++ */
++void
++print_autowrap (WINDOW * win, const char *prompt, int width, int y, int x)
++{
++ int newl, cur_x, cur_y;
++ int i, prompt_len, room, wlen;
++ char tempstr[MAX_LEN + 1], *word, *sp, *sp2;
++
++ strcpy (tempstr, prompt);
++
++ prompt_len = strlen(tempstr);
++
++ /*
++ * Remove newlines
++ */
++ for(i=0; i<prompt_len; i++) {
++ if(tempstr[i] == '\n') tempstr[i] = ' ';
++ }
++
++ if (prompt_len <= width - x * 2) { /* If prompt is short */
++ wmove (win, y, (width - prompt_len) / 2);
++ waddstr (win, tempstr);
++ } else {
++ cur_x = x;
++ cur_y = y;
++ newl = 1;
++ word = tempstr;
++ while (word && *word) {
++ sp = index(word, ' ');
++ if (sp)
++ *sp++ = 0;
++
++ /* Wrap to next line if either the word does not fit,
++ or it is the first word of a new sentence, and it is
++ short, and the next word does not fit. */
++ room = width - cur_x;
++ wlen = strlen(word);
++ if (wlen > room ||
++ (newl && wlen < 4 && sp && wlen+1+strlen(sp) > room
++ && (!(sp2 = index(sp, ' ')) || wlen+1+(sp2-sp) > room))) {
++ cur_y++;
++ cur_x = x;
++ }
++ wmove (win, cur_y, cur_x);
++ waddstr (win, word);
++ getyx (win, cur_y, cur_x);
++ cur_x++;
++ if (sp && *sp == ' ') {
++ cur_x++; /* double space */
++ while (*++sp == ' ');
++ newl = 1;
++ } else
++ newl = 0;
++ word = sp;
++ }
++ }
++}
++
++/*
++ * Print a button
++ */
++void
++print_button (WINDOW * win, const char *label, int y, int x, int selected)
++{
++ int i, temp;
++
++ wmove (win, y, x);
++ wattrset (win, selected ? button_active_attr : button_inactive_attr);
++ waddstr (win, "<");
++ temp = strspn (label, " ");
++ label += temp;
++ wattrset (win, selected ? button_label_active_attr
++ : button_label_inactive_attr);
++ for (i = 0; i < temp; i++)
++ waddch (win, ' ');
++ wattrset (win, selected ? button_key_active_attr
++ : button_key_inactive_attr);
++ waddch (win, label[0]);
++ wattrset (win, selected ? button_label_active_attr
++ : button_label_inactive_attr);
++ waddstr (win, (char *)label + 1);
++ wattrset (win, selected ? button_active_attr : button_inactive_attr);
++ waddstr (win, ">");
++ wmove (win, y, x + temp + 1);
++}
++
++/*
++ * Draw a rectangular box with line drawing characters
++ */
++void
++draw_box (WINDOW * win, int y, int x, int height, int width,
++ chtype box, chtype border)
++{
++ int i, j;
++
++ wattrset (win, 0);
++ for (i = 0; i < height; i++) {
++ wmove (win, y + i, x);
++ for (j = 0; j < width; j++)
++ if (!i && !j)
++ waddch (win, border | ACS_ULCORNER);
++ else if (i == height - 1 && !j)
++ waddch (win, border | ACS_LLCORNER);
++ else if (!i && j == width - 1)
++ waddch (win, box | ACS_URCORNER);
++ else if (i == height - 1 && j == width - 1)
++ waddch (win, box | ACS_LRCORNER);
++ else if (!i)
++ waddch (win, border | ACS_HLINE);
++ else if (i == height - 1)
++ waddch (win, box | ACS_HLINE);
++ else if (!j)
++ waddch (win, border | ACS_VLINE);
++ else if (j == width - 1)
++ waddch (win, box | ACS_VLINE);
++ else
++ waddch (win, box | ' ');
++ }
++}
++
++/*
++ * Draw shadows along the right and bottom edge to give a more 3D look
++ * to the boxes
++ */
++void
++draw_shadow (WINDOW * win, int y, int x, int height, int width)
++{
++ int i;
++
++ if (has_colors ()) { /* Whether terminal supports color? */
++ wattrset (win, shadow_attr);
++ wmove (win, y + height, x + 2);
++ for (i = 0; i < width; i++)
++ waddch (win, winch (win) & A_CHARTEXT);
++ for (i = y + 1; i < y + height + 1; i++) {
++ wmove (win, i, x + width);
++ waddch (win, winch (win) & A_CHARTEXT);
++ waddch (win, winch (win) & A_CHARTEXT);
++ }
++ wnoutrefresh (win);
++ }
++}
++
++/*
++ * Return the position of the first alphabetic character in a string.
++ */
++int
++first_alpha(const char *string, const char *exempt)
++{
++ int i, in_paren=0, c;
++
++ for (i = 0; i < strlen(string); i++) {
++ c = tolower(string[i]);
++
++ if (strchr("<[(", c)) ++in_paren;
++ if (strchr(">])", c) && in_paren > 0) --in_paren;
++
++ if ((! in_paren) && isalpha(c) &&
++ strchr(exempt, c) == 0)
++ return i;
++ }
++
++ return 0;
++}
++
++/*
++ * Get the first selected item in the dialog_list_item list.
++ */
++struct dialog_list_item *
++first_sel_item(int item_no, struct dialog_list_item ** items)
++{
++ int i;
++
++ for (i = 0; i < item_no; i++) {
++ if (items[i]->selected)
++ return items[i];
++ }
++
++ return NULL;
++}
+diff -Nur busybox-1.00/scripts/config/lxdialog/yesno.c busybox/scripts/config/lxdialog/yesno.c
+--- busybox-1.00/scripts/config/lxdialog/yesno.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/scripts/config/lxdialog/yesno.c 2005-06-04 08:20:02.000000000 +0200
+@@ -0,0 +1,118 @@
++/*
++ * yesno.c -- implements the yes/no box
++ *
++ * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
++ * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com)
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version 2
++ * of the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
++ */
++
++#include "dialog.h"
++
++/*
++ * Display termination buttons
++ */
++static void
++print_buttons(WINDOW *dialog, int height, int width, int selected)
++{
++ int x = width / 2 - 10;
++ int y = height - 2;
++
++ print_button (dialog, " Yes ", y, x, selected == 0);
++ print_button (dialog, " No ", y, x + 13, selected == 1);
++
++ wmove(dialog, y, x+1 + 13*selected );
++ wrefresh (dialog);
++}
++
++/*
++ * Display a dialog box with two buttons - Yes and No
++ */
++int
++dialog_yesno (const char *title, const char *prompt, int height, int width)
++{
++ int i, x, y, key = 0, button = 0;
++ WINDOW *dialog;
++
++ /* center dialog box on screen */
++ x = (COLS - width) / 2;
++ y = (LINES - height) / 2;
++
++ draw_shadow (stdscr, y, x, height, width);
++
++ dialog = newwin (height, width, y, x);
++ keypad (dialog, TRUE);
++
++ draw_box (dialog, 0, 0, height, width, dialog_attr, border_attr);
++ wattrset (dialog, border_attr);
++ mvwaddch (dialog, height-3, 0, ACS_LTEE);
++ for (i = 0; i < width - 2; i++)
++ waddch (dialog, ACS_HLINE);
++ wattrset (dialog, dialog_attr);
++ waddch (dialog, ACS_RTEE);
++
++ if (title != NULL && strlen(title) >= width-2 ) {
++ /* truncate long title -- mec */
++ char * title2 = malloc(width-2+1);
++ memcpy( title2, title, width-2 );
++ title2[width-2] = '\0';
++ title = title2;
++ }
++
++ if (title != NULL) {
++ wattrset (dialog, title_attr);
++ mvwaddch (dialog, 0, (width - strlen(title))/2 - 1, ' ');
++ waddstr (dialog, (char *)title);
++ waddch (dialog, ' ');
++ }
++
++ wattrset (dialog, dialog_attr);
++ print_autowrap (dialog, prompt, width - 2, 1, 3);
++
++ print_buttons(dialog, height, width, 0);
++
++ while (key != ESC) {
++ key = wgetch (dialog);
++ switch (key) {
++ case 'Y':
++ case 'y':
++ delwin (dialog);
++ return 0;
++ case 'N':
++ case 'n':
++ delwin (dialog);
++ return 1;
++
++ case TAB:
++ case KEY_LEFT:
++ case KEY_RIGHT:
++ button = ((key == KEY_LEFT ? --button : ++button) < 0)
++ ? 1 : (button > 1 ? 0 : button);
++
++ print_buttons(dialog, height, width, button);
++ wrefresh (dialog);
++ break;
++ case ' ':
++ case '\n':
++ delwin (dialog);
++ return button;
++ case ESC:
++ break;
++ }
++ }
++
++ delwin (dialog);
++ return -1; /* ESC pressed */
++}
+diff -Nur busybox-1.00/scripts/config/mconf.c busybox/scripts/config/mconf.c
+--- busybox-1.00/scripts/config/mconf.c 2004-10-08 09:58:30.000000000 +0200
++++ busybox/scripts/config/mconf.c 2005-06-04 08:20:03.000000000 +0200
+@@ -23,18 +23,150 @@
+ #include <termios.h>
+ #include <unistd.h>
+
+-#include "dialog.h"
++#include "lxdialog/dialog.h"
+
+ #define LKC_DIRECT_LINK
+ #include "lkc.h"
+
+ static char menu_backtitle[128];
+-static const char menu_instructions[] =
++static const char mconf_readme[] =
++"Overview\n"
++"--------\n"
++"Some features may be built directly into BusyBox. Some features\n"
++"may be completely removed altogether. There are also certain\n"
++"parameters which are not really features, but must be\n"
++"entered in as decimal or hexadecimal numbers or possibly text.\n"
++"\n"
++"Menu items beginning with [*] or [ ] represent features\n"
++"configured to be built in or removed respectively.\n"
++"\n"
++"To change any of these features, highlight it with the cursor\n"
++"keys and press <Y> to build it in or <N> to removed it.\n"
++"You may also press the <Space Bar> to cycle\n"
++"through the available options (ie. Y->N->Y).\n"
++"\n"
++"Some additional keyboard hints:\n"
++"\n"
++"Menus\n"
++"----------\n"
++"o Use the Up/Down arrow keys (cursor keys) to highlight the item\n"
++" you wish to change or submenu wish to select and press <Enter>.\n"
++" Submenus are designated by \"--->\".\n"
++"\n"
++" Shortcut: Press the option's highlighted letter (hotkey).\n"
++" Pressing a hotkey more than once will sequence\n"
++" through all visible items which use that hotkey.\n"
++"\n"
++" You may also use the <PAGE UP> and <PAGE DOWN> keys to scroll\n"
++" unseen options into view.\n"
++"\n"
++"o To exit a menu use the cursor keys to highlight the <Exit> button\n"
++" and press <ENTER>.\n"
++"\n"
++" Shortcut: Press <ESC><ESC> or <E> or <X> if there is no hotkey\n"
++" using those letters. You may press a single <ESC>, but\n"
++" there is a delayed response which you may find annoying.\n"
++"\n"
++" Also, the <TAB> and cursor keys will cycle between <Select>,\n"
++" <Exit> and <Help>\n"
++"\n"
++"o To get help with an item, use the cursor keys to highlight <Help>\n"
++" and Press <ENTER>.\n"
++"\n"
++" Shortcut: Press <H> or <?>.\n"
++"\n"
++"\n"
++"Radiolists (Choice lists)\n"
++"-----------\n"
++"o Use the cursor keys to select the option you wish to set and press\n"
++" <S> or the <SPACE BAR>.\n"
++"\n"
++" Shortcut: Press the first letter of the option you wish to set then\n"
++" press <S> or <SPACE BAR>.\n"
++"\n"
++"o To see available help for the item, use the cursor keys to highlight\n"
++" <Help> and Press <ENTER>.\n"
++"\n"
++" Shortcut: Press <H> or <?>.\n"
++"\n"
++" Also, the <TAB> and cursor keys will cycle between <Select> and\n"
++" <Help>\n"
++"\n"
++"\n"
++"Data Entry\n"
++"-----------\n"
++"o Enter the requested information and press <ENTER>\n"
++" If you are entering hexadecimal values, it is not necessary to\n"
++" add the '0x' prefix to the entry.\n"
++"\n"
++"o For help, use the <TAB> or cursor keys to highlight the help option\n"
++" and press <ENTER>. You can try <TAB><H> as well.\n"
++"\n"
++"\n"
++"Text Box (Help Window)\n"
++"--------\n"
++"o Use the cursor keys to scroll up/down/left/right. The VI editor\n"
++" keys h,j,k,l function here as do <SPACE BAR> and <B> for those\n"
++" who are familiar with less and lynx.\n"
++"\n"
++"o Press <E>, <X>, <Enter> or <Esc><Esc> to exit.\n"
++"\n"
++"\n"
++"Alternate Configuration Files\n"
++"-----------------------------\n"
++"Menuconfig supports the use of alternate configuration files for\n"
++"those who, for various reasons, find it necessary to switch\n"
++"between different configurations.\n"
++"\n"
++"At the end of the main menu you will find two options. One is\n"
++"for saving the current configuration to a file of your choosing.\n"
++"The other option is for loading a previously saved alternate\n"
++"configuration.\n"
++"\n"
++"Even if you don't use alternate configuration files, but you\n"
++"find during a Menuconfig session that you have completely messed\n"
++"up your settings, you may use the \"Load Alternate...\" option to\n"
++"restore your previously saved settings from \".config\" without\n"
++"restarting Menuconfig.\n"
++"\n"
++"Other information\n"
++"-----------------\n"
++"If you use Menuconfig in an XTERM window make sure you have your\n"
++"$TERM variable set to point to a xterm definition which supports color.\n"
++"Otherwise, Menuconfig will look rather bad. Menuconfig will not\n"
++"display correctly in a RXVT window because rxvt displays only one\n"
++"intensity of color, bright.\n"
++"\n"
++"Menuconfig will display larger menus on screens or xterms which are\n"
++"set to display more than the standard 25 row by 80 column geometry.\n"
++"In order for this to work, the \"stty size\" command must be able to\n"
++"display the screen's current row and column geometry. I STRONGLY\n"
++"RECOMMEND that you make sure you do NOT have the shell variables\n"
++"LINES and COLUMNS exported into your environment. Some distributions\n"
++"export those variables via /etc/profile. Some ncurses programs can\n"
++"become confused when those variables (LINES & COLUMNS) don't reflect\n"
++"the true screen size.\n"
++"\n"
++"Optional personality available\n"
++"------------------------------\n"
++"If you prefer to have all of the options listed in a single\n"
++"menu, rather than the default multimenu hierarchy, run the menuconfig\n"
++"with MENUCONFIG_MODE environment variable set to single_menu. Example:\n"
++"\n"
++"make MENUCONFIG_MODE=single_menu menuconfig\n"
++"\n"
++"<Enter> will then unroll the appropriate category, or enfold it if it\n"
++"is already unrolled.\n"
++"\n"
++"Note that this mode can eventually be a little more CPU expensive\n"
++"(especially with a larger number of unrolled categories) than the\n"
++"default mode.\n",
++menu_instructions[] =
+ "Arrow keys navigate the menu. "
+ "<Enter> selects submenus --->. "
+ "Highlighted letters are hotkeys. "
+ "Pressing <Y> selectes a feature, while <N> will exclude a feature. "
+- "Press <Esc><Esc> to exit, <?> for Help. "
++ "Press <Esc><Esc> to exit, <?> for Help, </> for Search. "
+ "Legend: [*] feature is selected [ ] feature is excluded",
+ radiolist_instructions[] =
+ "Use the arrow keys to navigate this window or "
+@@ -85,23 +217,50 @@
+ "\n"
+ "If you are uncertain what all this means then you should probably\n"
+ "leave this blank.\n",
+-top_menu_help[] =
++search_help[] =
+ "\n"
+- "Use the Up/Down arrow keys (cursor keys) to highlight the item\n"
+- "you wish to change or submenu wish to select and press <Enter>.\n"
+- "Submenus are designated by \"--->\".\n"
++ "Search for CONFIG_ symbols and display their relations.\n"
++ "Example: search for \"^FOO\"\n"
++ "Result:\n"
++ "-----------------------------------------------------------------\n"
++ "Symbol: FOO [=m]\n"
++ "Prompt: Foo bus is used to drive the bar HW\n"
++ "Defined at drivers/pci/Kconfig:47\n"
++ "Depends on: X86_LOCAL_APIC && X86_IO_APIC || IA64\n"
++ "Location:\n"
++ " -> Bus options (PCI, PCMCIA, EISA, MCA, ISA)\n"
++ " -> PCI support (PCI [=y])\n"
++ " -> PCI access mode (<choice> [=y])\n"
++ "Selects: LIBCRC32\n"
++ "Selected by: BAR\n"
++ "-----------------------------------------------------------------\n"
++ "o The line 'Prompt:' shows the text used in the menu structure for\n"
++ " this CONFIG_ symbol\n"
++ "o The 'Defined at' line tell at what file / line number the symbol\n"
++ " is defined\n"
++ "o The 'Depends on:' line tell what symbols needs to be defined for\n"
++ " this symbol to be visible in the menu (selectable)\n"
++ "o The 'Location:' lines tell where in the menu structure this symbol\n"
++ " is located\n"
++ " A location followed by a [=y] indicate that this is a selectable\n"
++ " menu item - and current value is displayed inside brackets.\n"
++ "o The 'Selects:' line tell what symbol will be automatically\n"
++ " selected if this symbol is selected (y or m)\n"
++ "o The 'Selected by' line tell what symbol has selected this symbol\n"
+ "\n"
+- "Shortcut: Press the option's highlighted letter (hotkey).\n"
+- "\n"
+- "You may also use the <PAGE UP> and <PAGE DOWN> keys to scroll\n"
+- "unseen options into view.\n"
+-;
++ "Only relevant lines are shown.\n"
++ "\n\n"
++ "Search examples:\n"
++ "Examples: USB => find all CONFIG_ symbols containing USB\n"
++ " ^USB => find all CONFIG_ symbols starting with USB\n"
++ " USB$ => find all CONFIG_ symbols ending with USB\n"
++ "\n";
+
+ static char filename[PATH_MAX+1] = ".config";
+-static int indent = 0;
++static int indent;
+ static struct termios ios_org;
+-static int rows, cols;
+-struct menu *current_menu;
++static int rows = 0, cols = 0;
++static struct menu *current_menu;
+ static int child_count;
+ static int single_menu_mode;
+
+@@ -116,33 +275,31 @@
+ static void show_textbox(const char *title, const char *text, int r, int c);
+ static void show_helptext(const char *title, const char *text);
+ static void show_help(struct menu *menu);
+-static void show_readme(void);
++static void show_file(const char *filename, const char *title, int r, int c);
+
+ static void init_wsize(void)
+ {
+ struct winsize ws;
+ char *env;
+
+- if (ioctl(1, TIOCGWINSZ, &ws) == -1) {
+- rows = 24;
+- cols = 80;
+- } else {
++ if (!ioctl(STDIN_FILENO, TIOCGWINSZ, &ws)) {
+ rows = ws.ws_row;
+ cols = ws.ws_col;
+- if (!rows) {
+- env = getenv("LINES");
+- if (env)
+- rows = atoi(env);
+- if (!rows)
+- rows = 24;
+- }
+- if (!cols) {
+- env = getenv("COLUMNS");
+- if (env)
+- cols = atoi(env);
+- if (!cols)
+- cols = 80;
+- }
++ }
++
++ if (!rows) {
++ env = getenv("LINES");
++ if (env)
++ rows = atoi(env);
++ if (!rows)
++ rows = 24;
++ }
++ if (!cols) {
++ env = getenv("COLUMNS");
++ if (env)
++ cols = atoi(env);
++ if (!cols)
++ cols = 80;
+ }
+
+ if (rows < 19 || cols < 80) {
+@@ -214,6 +371,103 @@
+ item_no = 0;
+ }
+
++static void get_prompt_str(struct gstr *r, struct property *prop)
++{
++ int i, j;
++ struct menu *submenu[8], *menu;
++
++ str_printf(r, "Prompt: %s\n", prop->text);
++ str_printf(r, " Defined at %s:%d\n", prop->menu->file->name,
++ prop->menu->lineno);
++ if (!expr_is_yes(prop->visible.expr)) {
++ str_append(r, " Depends on: ");
++ expr_gstr_print(prop->visible.expr, r);
++ str_append(r, "\n");
++ }
++ menu = prop->menu->parent;
++ for (i = 0; menu != &rootmenu && i < 8; menu = menu->parent)
++ submenu[i++] = menu;
++ if (i > 0) {
++ str_printf(r, " Location:\n");
++ for (j = 4; --i >= 0; j += 2) {
++ menu = submenu[i];
++ str_printf(r, "%*c-> %s", j, ' ', menu_get_prompt(menu));
++ if (menu->sym) {
++ str_printf(r, " (%s [=%s])", menu->sym->name ?
++ menu->sym->name : "<choice>",
++ sym_get_string_value(menu->sym));
++ }
++ str_append(r, "\n");
++ }
++ }
++}
++
++static void get_symbol_str(struct gstr *r, struct symbol *sym)
++{
++ bool hit;
++ struct property *prop;
++
++ str_printf(r, "Symbol: %s [=%s]\n", sym->name,
++ sym_get_string_value(sym));
++ for_all_prompts(sym, prop)
++ get_prompt_str(r, prop);
++ hit = false;
++ for_all_properties(sym, prop, P_SELECT) {
++ if (!hit) {
++ str_append(r, " Selects: ");
++ hit = true;
++ } else
++ str_printf(r, " && ");
++ expr_gstr_print(prop->expr, r);
++ }
++ if (hit)
++ str_append(r, "\n");
++ if (sym->rev_dep.expr) {
++ str_append(r, " Selected by: ");
++ expr_gstr_print(sym->rev_dep.expr, r);
++ str_append(r, "\n");
++ }
++ str_append(r, "\n\n");
++}
++
++static struct gstr get_relations_str(struct symbol **sym_arr)
++{
++ struct symbol *sym;
++ struct gstr res = str_new();
++ int i;
++
++ for (i = 0; sym_arr && (sym = sym_arr[i]); i++)
++ get_symbol_str(&res, sym);
++ if (!i)
++ str_append(&res, "No matches found.\n");
++ return res;
++}
++
++static void search_conf(void)
++{
++ struct symbol **sym_arr;
++ struct gstr res;
++
++again:
++ switch (dialog_inputbox("Search Configuration Parameter",
++ "Enter Keyword", 10, 75,
++ NULL)) {
++ case 0:
++ break;
++ case 1:
++ show_helptext("Search Configuration", search_help);
++ goto again;
++ default:
++ return;
++ }
++
++ sym_arr = sym_re_search(dialog_input_result);
++ res = get_relations_str(sym_arr);
++ free(sym_arr);
++ show_textbox("Search Results", str_get(&res), 0, 0);
++ str_free(&res);
++}
++
+ static void build_conf(struct menu *menu)
+ {
+ struct symbol *sym;
+@@ -308,6 +562,11 @@
+ return;
+ }
+ } else {
++ if (menu == current_menu) {
++ cprint_tag(":%p", menu);
++ cprint_name("---%*c%s", indent + 1, ' ', menu_get_prompt(menu));
++ goto conf_childs;
++ }
+ child_count++;
+ val = sym_get_tristate_value(sym);
+ if (sym_is_choice_value(sym) && val == yes) {
+@@ -376,7 +635,7 @@
+ while (1) {
+ indent = 0;
+ child_count = 0;
+- current_menu = menu;
++ current_menu = menu;
+ cdone(); cinit();
+ build_conf(menu);
+ if (!child_count)
+@@ -441,7 +700,7 @@
+ if (sym)
+ show_help(submenu);
+ else
+- show_readme();
++ show_helptext("README", mconf_readme);
+ break;
+ case 3:
+ if (type == 't') {
+@@ -465,6 +724,9 @@
+ else if (type == 'm')
+ conf(submenu);
+ break;
++ case 7:
++ search_conf();
++ break;
+ }
+ }
+ }
+@@ -476,37 +738,39 @@
+ fd = creat(".help.tmp", 0777);
+ write(fd, text, strlen(text));
+ close(fd);
+- while (dialog_textbox(title, ".help.tmp", r, c) < 0)
+- ;
++ show_file(".help.tmp", title, r, c);
+ unlink(".help.tmp");
+ }
+
+ static void show_helptext(const char *title, const char *text)
+ {
+- show_textbox(title, text, rows, cols);
++ show_textbox(title, text, 0, 0);
+ }
+
+ static void show_help(struct menu *menu)
+ {
+- const char *help;
+- char *helptext;
++ struct gstr help = str_new();
+ struct symbol *sym = menu->sym;
+
+- help = sym->help;
+- if (!help)
+- help = nohelp_text;
+- if (sym->name) {
+- helptext = malloc(strlen(sym->name) + strlen(help) + 16);
+- sprintf(helptext, "%s:\n\n%s", sym->name, help);
+- show_helptext(menu_get_prompt(menu), helptext);
+- free(helptext);
+- } else
+- show_helptext(menu_get_prompt(menu), help);
++ if (sym->help)
++ {
++ if (sym->name) {
++ str_printf(&help, "%s:\n\n", sym->name);
++ str_append(&help, sym->help);
++ str_append(&help, "\n");
++ }
++ } else {
++ str_append(&help, nohelp_text);
++ }
++ get_symbol_str(&help, sym);
++ show_helptext(menu_get_prompt(menu), str_get(&help));
++ str_free(&help);
+ }
+
+-static void show_readme(void)
++static void show_file(const char *filename, const char *title, int r, int c)
+ {
+- show_helptext("Help", top_menu_help);
++ while (dialog_textbox(title, filename, r ? r : rows, c ? c : cols) < 0)
++ ;
+ }
+
+ static void conf_choice(struct menu *menu)
+@@ -667,9 +931,9 @@
+
+ int main(int ac, char **av)
+ {
+- int stat;
+- char *mode;
+ struct symbol *sym;
++ char *mode;
++ int stat;
+
+ conf_parse(av[1]);
+ conf_read(NULL);
+@@ -697,7 +961,7 @@
+ init_dialog();
+ do {
+ stat = dialog_yesno(NULL,
+- "Do you wish to save your new BusyBox configuration?", 5, 60);
++ "Do you wish to save your new BusyBox configuration?", 5, 60);
+ } while (stat < 0);
+ end_dialog();
+
+diff -Nur busybox-1.00/scripts/config/menu.c busybox/scripts/config/menu.c
+--- busybox-1.00/scripts/config/menu.c 2004-07-15 08:01:05.000000000 +0200
++++ busybox/scripts/config/menu.c 2005-06-04 08:20:03.000000000 +0200
+@@ -10,7 +10,6 @@
+ #include "lkc.h"
+
+ struct menu rootmenu;
+-struct menu *current_menu, *current_entry;
+ static struct menu **last_entry_ptr;
+
+ struct file *file_list;
+@@ -389,43 +388,3 @@
+ return menu;
+ }
+
+-struct file *file_lookup(const char *name)
+-{
+- struct file *file;
+-
+- for (file = file_list; file; file = file->next) {
+- if (!strcmp(name, file->name))
+- return file;
+- }
+-
+- file = malloc(sizeof(*file));
+- memset(file, 0, sizeof(*file));
+- file->name = strdup(name);
+- file->next = file_list;
+- file_list = file;
+- return file;
+-}
+-
+-int file_write_dep(const char *name)
+-{
+- struct file *file;
+- FILE *out;
+-
+- if (!name)
+- name = ".config.cmd";
+- out = fopen(".config.tmp", "w");
+- if (!out)
+- return 1;
+- fprintf(out, "deps_config := \\\n");
+- for (file = file_list; file; file = file->next) {
+- if (file->next)
+- fprintf(out, "\t%s \\\n", file->name);
+- else
+- fprintf(out, "\t%s\n", file->name);
+- }
+- fprintf(out, "\n.config include/config.h: $(deps_config)\n\n$(deps_config):\n");
+- fclose(out);
+- rename(".config.tmp", name);
+- return 0;
+-}
+-
+diff -Nur busybox-1.00/scripts/config/menubox.c busybox/scripts/config/menubox.c
+--- busybox-1.00/scripts/config/menubox.c 2004-03-15 09:29:08.000000000 +0100
++++ busybox/scripts/config/menubox.c 1970-01-01 01:00:00.000000000 +0100
+@@ -1,436 +0,0 @@
+-/*
+- * menubox.c -- implements the menu box
+- *
+- * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
+- * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcapw@cfw.com)
+- *
+- * This program is free software; you can redistribute it and/or
+- * modify it under the terms of the GNU General Public License
+- * as published by the Free Software Foundation; either version 2
+- * of the License, or (at your option) any later version.
+- *
+- * This program is distributed in the hope that it will be useful,
+- * but WITHOUT ANY WARRANTY; without even the implied warranty of
+- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+- * GNU General Public License for more details.
+- *
+- * You should have received a copy of the GNU General Public License
+- * along with this program; if not, write to the Free Software
+- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+- */
+-
+-/*
+- * Changes by Clifford Wolf (god@clifford.at)
+- *
+- * [ 1998-06-13 ]
+- *
+- * *) A bugfix for the Page-Down problem
+- *
+- * *) Formerly when I used Page Down and Page Up, the cursor would be set
+- * to the first position in the menu box. Now lxdialog is a bit
+- * smarter and works more like other menu systems (just have a look at
+- * it).
+- *
+- * *) Formerly if I selected something my scrolling would be broken because
+- * lxdialog is re-invoked by the Menuconfig shell script, can't
+- * remember the last scrolling position, and just sets it so that the
+- * cursor is at the bottom of the box. Now it writes the temporary file
+- * lxdialog.scrltmp which contains this information. The file is
+- * deleted by lxdialog if the user leaves a submenu or enters a new
+- * one, but it would be nice if Menuconfig could make another "rm -f"
+- * just to be sure. Just try it out - you will recognise a difference!
+- *
+- * [ 1998-06-14 ]
+- *
+- * *) Now lxdialog is crash-safe against broken "lxdialog.scrltmp" files
+- * and menus change their size on the fly.
+- *
+- * *) If for some reason the last scrolling position is not saved by
+- * lxdialog, it sets the scrolling so that the selected item is in the
+- * middle of the menu box, not at the bottom.
+- *
+- * 02 January 1999, Michael Elizabeth Chastain (mec@shout.net)
+- * Reset 'scroll' to 0 if the value from lxdialog.scrltmp is bogus.
+- * This fixes a bug in Menuconfig where using ' ' to descend into menus
+- * would leave mis-synchronized lxdialog.scrltmp files lying around,
+- * fscanf would read in 'scroll', and eventually that value would get used.
+- */
+-
+-#include "dialog.h"
+-
+-static int menu_width, item_x;
+-
+-/*
+- * Print menu item
+- */
+-static void
+-print_item (WINDOW * win, const char *item, int choice, int selected, int hotkey)
+-{
+- int j;
+- char menu_item[menu_width+1];
+-
+- strncpy(menu_item, item, menu_width);
+- menu_item[menu_width] = 0;
+- j = first_alpha(menu_item, "YyNnMm");
+-
+- /* Clear 'residue' of last item */
+- wattrset (win, menubox_attr);
+- wmove (win, choice, 0);
+-#if OLD_NCURSES
+- {
+- int i;
+- for (i = 0; i < menu_width; i++)
+- waddch (win, ' ');
+- }
+-#else
+- wclrtoeol(win);
+-#endif
+- wattrset (win, selected ? item_selected_attr : item_attr);
+- mvwaddstr (win, choice, item_x, menu_item);
+- if (hotkey) {
+- wattrset (win, selected ? tag_key_selected_attr : tag_key_attr);
+- mvwaddch(win, choice, item_x+j, menu_item[j]);
+- }
+- if (selected) {
+- wmove (win, choice, item_x+1);
+- wrefresh (win);
+- }
+-}
+-
+-/*
+- * Print the scroll indicators.
+- */
+-static void
+-print_arrows (WINDOW * win, int item_no, int scroll,
+- int y, int x, int height)
+-{
+- int cur_y, cur_x;
+-
+- getyx(win, cur_y, cur_x);
+-
+- wmove(win, y, x);
+-
+- if (scroll > 0) {
+- wattrset (win, uarrow_attr);
+- waddch (win, ACS_UARROW);
+- waddstr (win, "(-)");
+- }
+- else {
+- wattrset (win, menubox_attr);
+- waddch (win, ACS_HLINE);
+- waddch (win, ACS_HLINE);
+- waddch (win, ACS_HLINE);
+- waddch (win, ACS_HLINE);
+- }
+-
+- y = y + height + 1;
+- wmove(win, y, x);
+-
+- if ((height < item_no) && (scroll + height < item_no)) {
+- wattrset (win, darrow_attr);
+- waddch (win, ACS_DARROW);
+- waddstr (win, "(+)");
+- }
+- else {
+- wattrset (win, menubox_border_attr);
+- waddch (win, ACS_HLINE);
+- waddch (win, ACS_HLINE);
+- waddch (win, ACS_HLINE);
+- waddch (win, ACS_HLINE);
+- }
+-
+- wmove(win, cur_y, cur_x);
+-}
+-
+-/*
+- * Display the termination buttons.
+- */
+-static void
+-print_buttons (WINDOW *win, int height, int width, int selected)
+-{
+- int x = width / 2 - 16;
+- int y = height - 2;
+-
+- print_button (win, "Select", y, x, selected == 0);
+- print_button (win, " Exit ", y, x + 12, selected == 1);
+- print_button (win, " Help ", y, x + 24, selected == 2);
+-
+- wmove(win, y, x+1+12*selected);
+- wrefresh (win);
+-}
+-
+-/*
+- * Display a menu for choosing among a number of options
+- */
+-int
+-dialog_menu (const char *title, const char *prompt, int height, int width,
+- int menu_height, const char *current, int item_no,
+- struct dialog_list_item ** items)
+-{
+- int i, j, x, y, box_x, box_y;
+- int key = 0, button = 0, scroll = 0, choice = 0, first_item = 0, max_choice;
+- WINDOW *dialog, *menu;
+- FILE *f;
+-
+- max_choice = MIN (menu_height, item_no);
+-
+- /* center dialog box on screen */
+- x = (COLS - width) / 2;
+- y = (LINES - height) / 2;
+-
+- draw_shadow (stdscr, y, x, height, width);
+-
+- dialog = newwin (height, width, y, x);
+- keypad (dialog, TRUE);
+-
+- draw_box (dialog, 0, 0, height, width, dialog_attr, border_attr);
+- wattrset (dialog, border_attr);
+- mvwaddch (dialog, height - 3, 0, ACS_LTEE);
+- for (i = 0; i < width - 2; i++)
+- waddch (dialog, ACS_HLINE);
+- wattrset (dialog, dialog_attr);
+- wbkgdset (dialog, dialog_attr & A_COLOR);
+- waddch (dialog, ACS_RTEE);
+-
+- if (title != NULL && strlen(title) >= width-2 ) {
+- /* truncate long title -- mec */
+- char * title2 = malloc(width-2+1);
+- memcpy( title2, title, width-2 );
+- title2[width-2] = '\0';
+- title = title2;
+- }
+-
+- if (title != NULL) {
+- wattrset (dialog, title_attr);
+- mvwaddch (dialog, 0, (width - strlen(title))/2 - 1, ' ');
+- waddstr (dialog, (char *)title);
+- waddch (dialog, ' ');
+- }
+-
+- wattrset (dialog, dialog_attr);
+- print_autowrap (dialog, prompt, width - 2, 1, 3);
+-
+- menu_width = width - 6;
+- box_y = height - menu_height - 5;
+- box_x = (width - menu_width) / 2 - 1;
+-
+- /* create new window for the menu */
+- menu = subwin (dialog, menu_height, menu_width,
+- y + box_y + 1, x + box_x + 1);
+- keypad (menu, TRUE);
+-
+- /* draw a box around the menu items */
+- draw_box (dialog, box_y, box_x, menu_height + 2, menu_width + 2,
+- menubox_border_attr, menubox_attr);
+-
+- /*
+- * Find length of longest item in order to center menu.
+- * Set 'choice' to default item.
+- */
+- item_x = 0;
+- for (i = 0; i < item_no; i++) {
+- item_x = MAX (item_x, MIN(menu_width, strlen (items[i]->name) + 2));
+- if (strcmp(current, items[i]->tag) == 0) choice = i;
+- }
+-
+- item_x = (menu_width - item_x) / 2;
+-
+- /* get the scroll info from the temp file */
+- if ( (f=fopen("lxdialog.scrltmp","r")) != NULL ) {
+- if ( (fscanf(f,"%d\n",&scroll) == 1) && (scroll <= choice) &&
+- (scroll+max_choice > choice) && (scroll >= 0) &&
+- (scroll+max_choice <= item_no) ) {
+- first_item = scroll;
+- choice = choice - scroll;
+- fclose(f);
+- } else {
+- scroll=0;
+- remove("lxdialog.scrltmp");
+- fclose(f);
+- f=NULL;
+- }
+- }
+- if ( (choice >= max_choice) || (f==NULL && choice >= max_choice/2) ) {
+- if (choice >= item_no-max_choice/2)
+- scroll = first_item = item_no-max_choice;
+- else
+- scroll = first_item = choice - max_choice/2;
+- choice = choice - scroll;
+- }
+-
+- /* Print the menu */
+- for (i=0; i < max_choice; i++) {
+- print_item (menu, items[first_item + i]->name, i, i == choice,
+- (items[first_item + i]->tag[0] != ':'));
+- }
+-
+- wnoutrefresh (menu);
+-
+- print_arrows(dialog, item_no, scroll,
+- box_y, box_x+item_x+1, menu_height);
+-
+- print_buttons (dialog, height, width, 0);
+- wmove (menu, choice, item_x+1);
+- wrefresh (menu);
+-
+- while (key != ESC) {
+- key = wgetch(menu);
+-
+- if (key < 256 && isalpha(key)) key = tolower(key);
+-
+- if (strchr("ynm", key))
+- i = max_choice;
+- else {
+- for (i = choice+1; i < max_choice; i++) {
+- j = first_alpha(items[scroll + i]->name, "YyNnMm>");
+- if (key == tolower(items[scroll + i]->name[j]))
+- break;
+- }
+- if (i == max_choice)
+- for (i = 0; i < max_choice; i++) {
+- j = first_alpha(items[scroll + i]->name, "YyNnMm>");
+- if (key == tolower(items[scroll + i]->name[j]))
+- break;
+- }
+- }
+-
+- if (i < max_choice ||
+- key == KEY_UP || key == KEY_DOWN ||
+- key == '-' || key == '+' ||
+- key == KEY_PPAGE || key == KEY_NPAGE) {
+-
+- print_item (menu, items[scroll + choice]->name, choice, FALSE,
+- (items[scroll + choice]->tag[0] != ':'));
+-
+- if (key == KEY_UP || key == '-') {
+- if (choice < 2 && scroll) {
+- /* Scroll menu down */
+- scrollok (menu, TRUE);
+- wscrl (menu, -1);
+- scrollok (menu, FALSE);
+-
+- scroll--;
+-
+- print_item (menu, items[scroll]->name, 0, FALSE,
+- (items[scroll]->tag[0] != ':'));
+- } else
+- choice = MAX(choice - 1, 0);
+-
+- } else if (key == KEY_DOWN || key == '+') {
+-
+- print_item (menu, items[scroll + choice]->name, choice, FALSE,
+- (items[scroll + choice]->tag[0] != ':'));
+-
+- if ((choice > max_choice-3) &&
+- (scroll + max_choice < item_no)
+- ) {
+- /* Scroll menu up */
+- scrollok (menu, TRUE);
+- scroll (menu);
+- scrollok (menu, FALSE);
+-
+- scroll++;
+-
+- print_item (menu, items[scroll + max_choice - 1]->name,
+- max_choice-1, FALSE,
+- (items[scroll + max_choice - 1]->tag[0] != ':'));
+- } else
+- choice = MIN(choice+1, max_choice-1);
+-
+- } else if (key == KEY_PPAGE) {
+- scrollok (menu, TRUE);
+- for (i=0; (i < max_choice); i++) {
+- if (scroll > 0) {
+- wscrl (menu, -1);
+- scroll--;
+- print_item (menu, items[scroll]->name, 0, FALSE,
+- (items[scroll]->tag[0] != ':'));
+- } else {
+- if (choice > 0)
+- choice--;
+- }
+- }
+- scrollok (menu, FALSE);
+-
+- } else if (key == KEY_NPAGE) {
+- for (i=0; (i < max_choice); i++) {
+- if (scroll+max_choice < item_no) {
+- scrollok (menu, TRUE);
+- scroll(menu);
+- scrollok (menu, FALSE);
+- scroll++;
+- print_item (menu, items[scroll + max_choice - 1]->name,
+- max_choice-1, FALSE,
+- (items[scroll + max_choice - 1]->tag[0] != ':'));
+- } else {
+- if (choice+1 < max_choice)
+- choice++;
+- }
+- }
+-
+- } else
+- choice = i;
+-
+- print_item (menu, items[scroll + choice]->name, choice, TRUE,
+- (items[scroll + choice]->tag[0] != ':'));
+-
+- print_arrows(dialog, item_no, scroll,
+- box_y, box_x+item_x+1, menu_height);
+-
+- wnoutrefresh (dialog);
+- wrefresh (menu);
+-
+- continue; /* wait for another key press */
+- }
+-
+- switch (key) {
+- case KEY_LEFT:
+- case TAB:
+- case KEY_RIGHT:
+- button = ((key == KEY_LEFT ? --button : ++button) < 0)
+- ? 2 : (button > 2 ? 0 : button);
+-
+- print_buttons(dialog, height, width, button);
+- wrefresh (menu);
+- break;
+- case ' ':
+- case 's':
+- case 'y':
+- case 'n':
+- case 'm':
+- /* save scroll info */
+- if ( (f=fopen("lxdialog.scrltmp","w")) != NULL ) {
+- fprintf(f,"%d\n",scroll);
+- fclose(f);
+- }
+- delwin (dialog);
+- items[scroll + choice]->selected = 1;
+- switch (key) {
+- case 's': return 3;
+- case 'y': return 3;
+- case 'n': return 4;
+- case 'm': return 5;
+- case ' ': return 6;
+- }
+- return 0;
+- case 'h':
+- case '?':
+- button = 2;
+- case '\n':
+- delwin (dialog);
+- items[scroll + choice]->selected = 1;
+-
+- remove("lxdialog.scrltmp");
+- return button;
+- case 'e':
+- case 'x':
+- key = ESC;
+- case ESC:
+- break;
+- }
+- }
+-
+- delwin (dialog);
+- remove("lxdialog.scrltmp");
+- return -1; /* ESC pressed */
+-}
+diff -Nur busybox-1.00/scripts/config/msgbox.c busybox/scripts/config/msgbox.c
+--- busybox-1.00/scripts/config/msgbox.c 2002-12-05 09:41:07.000000000 +0100
++++ busybox/scripts/config/msgbox.c 1970-01-01 01:00:00.000000000 +0100
+@@ -1,85 +0,0 @@
+-/*
+- * msgbox.c -- implements the message box and info box
+- *
+- * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
+- * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcapw@cfw.com)
+- *
+- * This program is free software; you can redistribute it and/or
+- * modify it under the terms of the GNU General Public License
+- * as published by the Free Software Foundation; either version 2
+- * of the License, or (at your option) any later version.
+- *
+- * This program is distributed in the hope that it will be useful,
+- * but WITHOUT ANY WARRANTY; without even the implied warranty of
+- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+- * GNU General Public License for more details.
+- *
+- * You should have received a copy of the GNU General Public License
+- * along with this program; if not, write to the Free Software
+- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+- */
+-
+-#include "dialog.h"
+-
+-/*
+- * Display a message box. Program will pause and display an "OK" button
+- * if the parameter 'pause' is non-zero.
+- */
+-int
+-dialog_msgbox (const char *title, const char *prompt, int height, int width,
+- int pause)
+-{
+- int i, x, y, key = 0;
+- WINDOW *dialog;
+-
+- /* center dialog box on screen */
+- x = (COLS - width) / 2;
+- y = (LINES - height) / 2;
+-
+- draw_shadow (stdscr, y, x, height, width);
+-
+- dialog = newwin (height, width, y, x);
+- keypad (dialog, TRUE);
+-
+- draw_box (dialog, 0, 0, height, width, dialog_attr, border_attr);
+-
+- if (title != NULL && strlen(title) >= width-2 ) {
+- /* truncate long title -- mec */
+- char * title2 = malloc(width-2+1);
+- memcpy( title2, title, width-2 );
+- title2[width-2] = '\0';
+- title = title2;
+- }
+-
+- if (title != NULL) {
+- wattrset (dialog, title_attr);
+- mvwaddch (dialog, 0, (width - strlen(title))/2 - 1, ' ');
+- waddstr (dialog, (char *)title);
+- waddch (dialog, ' ');
+- }
+- wattrset (dialog, dialog_attr);
+- print_autowrap (dialog, prompt, width - 2, 1, 2);
+-
+- if (pause) {
+- wattrset (dialog, border_attr);
+- mvwaddch (dialog, height - 3, 0, ACS_LTEE);
+- for (i = 0; i < width - 2; i++)
+- waddch (dialog, ACS_HLINE);
+- wattrset (dialog, dialog_attr);
+- waddch (dialog, ACS_RTEE);
+-
+- print_button (dialog, " Ok ",
+- height - 2, width / 2 - 4, TRUE);
+-
+- wrefresh (dialog);
+- while (key != ESC && key != '\n' && key != ' ' &&
+- key != 'O' && key != 'o' && key != 'X' && key != 'x')
+- key = wgetch (dialog);
+- } else {
+- key = '\n';
+- wrefresh (dialog);
+- }
+-
+- delwin (dialog);
+- return key == ESC ? -1 : 0;
+-}
+diff -Nur busybox-1.00/scripts/config/symbol.c busybox/scripts/config/symbol.c
+--- busybox-1.00/scripts/config/symbol.c 2004-07-15 08:01:05.000000000 +0200
++++ busybox/scripts/config/symbol.c 2005-06-04 08:20:03.000000000 +0200
+@@ -6,6 +6,7 @@
+ #include <ctype.h>
+ #include <stdlib.h>
+ #include <string.h>
++#include <regex.h>
+ #include <sys/utsname.h>
+
+ #define LKC_DIRECT_LINK
+@@ -414,7 +415,7 @@
+
+ bool sym_string_valid(struct symbol *sym, const char *str)
+ {
+- char ch;
++ signed char ch;
+
+ switch (sym->type) {
+ case S_STRING:
+@@ -649,6 +650,43 @@
+ return symbol;
+ }
+
++struct symbol **sym_re_search(const char *pattern)
++{
++ struct symbol *sym, **sym_arr = NULL;
++ int i, cnt, size;
++ regex_t re;
++
++ cnt = size = 0;
++ /* Skip if empty */
++ if (strlen(pattern) == 0)
++ return NULL;
++ if (regcomp(&re, pattern, REG_EXTENDED|REG_NOSUB|REG_ICASE))
++ return NULL;
++
++ for_all_symbols(i, sym) {
++ if (sym->flags & SYMBOL_CONST || !sym->name)
++ continue;
++ if (regexec(&re, sym->name, 0, NULL, 0))
++ continue;
++ if (cnt + 1 >= size) {
++ void *tmp = sym_arr;
++ size += 16;
++ sym_arr = realloc(sym_arr, size * sizeof(struct symbol *));
++ if (!sym_arr) {
++ free(tmp);
++ return NULL;
++ }
++ }
++ sym_arr[cnt++] = sym;
++ }
++ if (sym_arr)
++ sym_arr[cnt] = NULL;
++ regfree(&re);
++
++ return sym_arr;
++}
++
++
+ struct symbol *sym_check_deps(struct symbol *sym);
+
+ static struct symbol *sym_check_expr_deps(struct expr *e)
+diff -Nur busybox-1.00/scripts/config/textbox.c busybox/scripts/config/textbox.c
+--- busybox-1.00/scripts/config/textbox.c 2004-07-15 08:01:05.000000000 +0200
++++ busybox/scripts/config/textbox.c 1970-01-01 01:00:00.000000000 +0100
+@@ -1,556 +0,0 @@
+-/*
+- * textbox.c -- implements the text box
+- *
+- * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
+- * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com)
+- *
+- * This program is free software; you can redistribute it and/or
+- * modify it under the terms of the GNU General Public License
+- * as published by the Free Software Foundation; either version 2
+- * of the License, or (at your option) any later version.
+- *
+- * This program is distributed in the hope that it will be useful,
+- * but WITHOUT ANY WARRANTY; without even the implied warranty of
+- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+- * GNU General Public License for more details.
+- *
+- * You should have received a copy of the GNU General Public License
+- * along with this program; if not, write to the Free Software
+- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+- */
+-
+-#include "dialog.h"
+-
+-static void back_lines (int n);
+-static void print_page (WINDOW * win, int height, int width);
+-static void print_line (WINDOW * win, int row, int width);
+-static char *get_line (void);
+-static void print_position (WINDOW * win, int height, int width);
+-
+-static int hscroll, fd, file_size, bytes_read;
+-static int begin_reached = 1, end_reached, page_length;
+-static char *buf, *page;
+-
+-/*
+- * Display text from a file in a dialog box.
+- */
+-int
+-dialog_textbox (const char *title, const char *file, int height, int width)
+-{
+- int i, x, y, cur_x, cur_y, fpos, key = 0;
+- int passed_end;
+- char search_term[MAX_LEN + 1];
+- WINDOW *dialog, *text;
+-
+- search_term[0] = '\0'; /* no search term entered yet */
+-
+- /* Open input file for reading */
+- if ((fd = open (file, O_RDONLY)) == -1) {
+- endwin ();
+- fprintf (stderr,
+- "\nCan't open input file in dialog_textbox().\n");
+- exit (-1);
+- }
+- /* Get file size. Actually, 'file_size' is the real file size - 1,
+- since it's only the last byte offset from the beginning */
+- if ((file_size = lseek (fd, 0, SEEK_END)) == -1) {
+- endwin ();
+- fprintf (stderr, "\nError getting file size in dialog_textbox().\n");
+- exit (-1);
+- }
+- /* Restore file pointer to beginning of file after getting file size */
+- if (lseek (fd, 0, SEEK_SET) == -1) {
+- endwin ();
+- fprintf (stderr, "\nError moving file pointer in dialog_textbox().\n");
+- exit (-1);
+- }
+- /* Allocate space for read buffer */
+- if ((buf = malloc (BUF_SIZE + 1)) == NULL) {
+- endwin ();
+- fprintf (stderr, "\nCan't allocate memory in dialog_textbox().\n");
+- exit (-1);
+- }
+- if ((bytes_read = read (fd, buf, BUF_SIZE)) == -1) {
+- endwin ();
+- fprintf (stderr, "\nError reading file in dialog_textbox().\n");
+- exit (-1);
+- }
+- buf[bytes_read] = '\0'; /* mark end of valid data */
+- page = buf; /* page is pointer to start of page to be displayed */
+-
+- /* center dialog box on screen */
+- x = (COLS - width) / 2;
+- y = (LINES - height) / 2;
+-
+-
+- draw_shadow (stdscr, y, x, height, width);
+-
+- dialog = newwin (height, width, y, x);
+- keypad (dialog, TRUE);
+-
+- /* Create window for text region, used for scrolling text */
+- text = subwin (dialog, height - 4, width - 2, y + 1, x + 1);
+- wattrset (text, dialog_attr);
+- wbkgdset (text, dialog_attr & A_COLOR);
+-
+- keypad (text, TRUE);
+-
+- /* register the new window, along with its borders */
+- draw_box (dialog, 0, 0, height, width, dialog_attr, border_attr);
+-
+- wattrset (dialog, border_attr);
+- mvwaddch (dialog, height-3, 0, ACS_LTEE);
+- for (i = 0; i < width - 2; i++)
+- waddch (dialog, ACS_HLINE);
+- wattrset (dialog, dialog_attr);
+- wbkgdset (dialog, dialog_attr & A_COLOR);
+- waddch (dialog, ACS_RTEE);
+-
+- if (title != NULL && strlen(title) >= width-2 ) {
+- /* truncate long title -- mec */
+- char * title2 = malloc(width-2+1);
+- memcpy( title2, title, width-2 );
+- title2[width-2] = '\0';
+- title = title2;
+- }
+-
+- if (title != NULL) {
+- wattrset (dialog, title_attr);
+- mvwaddch (dialog, 0, (width - strlen(title))/2 - 1, ' ');
+- waddstr (dialog, (char *)title);
+- waddch (dialog, ' ');
+- }
+- print_button (dialog, " Exit ", height - 2, width / 2 - 4, TRUE);
+- wnoutrefresh (dialog);
+- getyx (dialog, cur_y, cur_x); /* Save cursor position */
+-
+- /* Print first page of text */
+- attr_clear (text, height - 4, width - 2, dialog_attr);
+- print_page (text, height - 4, width - 2);
+- print_position (dialog, height, width);
+- wmove (dialog, cur_y, cur_x); /* Restore cursor position */
+- wrefresh (dialog);
+-
+- while ((key != ESC) && (key != '\n')) {
+- key = wgetch (dialog);
+- switch (key) {
+- case 'E': /* Exit */
+- case 'e':
+- case 'X':
+- case 'x':
+- delwin (dialog);
+- free (buf);
+- close (fd);
+- return 0;
+- case 'g': /* First page */
+- case KEY_HOME:
+- if (!begin_reached) {
+- begin_reached = 1;
+- /* First page not in buffer? */
+- if ((fpos = lseek (fd, 0, SEEK_CUR)) == -1) {
+- endwin ();
+- fprintf (stderr,
+- "\nError moving file pointer in dialog_textbox().\n");
+- exit (-1);
+- }
+- if (fpos > bytes_read) { /* Yes, we have to read it in */
+- if (lseek (fd, 0, SEEK_SET) == -1) {
+- endwin ();
+- fprintf (stderr, "\nError moving file pointer in "
+- "dialog_textbox().\n");
+- exit (-1);
+- }
+- if ((bytes_read = read (fd, buf, BUF_SIZE)) == -1) {
+- endwin ();
+- fprintf (stderr,
+- "\nError reading file in dialog_textbox().\n");
+- exit (-1);
+- }
+- buf[bytes_read] = '\0';
+- }
+- page = buf;
+- print_page (text, height - 4, width - 2);
+- print_position (dialog, height, width);
+- wmove (dialog, cur_y, cur_x); /* Restore cursor position */
+- wrefresh (dialog);
+- }
+- break;
+- case 'G': /* Last page */
+- case KEY_END:
+-
+- end_reached = 1;
+- /* Last page not in buffer? */
+- if ((fpos = lseek (fd, 0, SEEK_CUR)) == -1) {
+- endwin ();
+- fprintf (stderr,
+- "\nError moving file pointer in dialog_textbox().\n");
+- exit (-1);
+- }
+- if (fpos < file_size) { /* Yes, we have to read it in */
+- if (lseek (fd, -BUF_SIZE, SEEK_END) == -1) {
+- endwin ();
+- fprintf (stderr,
+- "\nError moving file pointer in dialog_textbox().\n");
+- exit (-1);
+- }
+- if ((bytes_read = read (fd, buf, BUF_SIZE)) == -1) {
+- endwin ();
+- fprintf (stderr,
+- "\nError reading file in dialog_textbox().\n");
+- exit (-1);
+- }
+- buf[bytes_read] = '\0';
+- }
+- page = buf + bytes_read;
+- back_lines (height - 4);
+- print_page (text, height - 4, width - 2);
+- print_position (dialog, height, width);
+- wmove (dialog, cur_y, cur_x); /* Restore cursor position */
+- wrefresh (dialog);
+- break;
+- case 'K': /* Previous line */
+- case 'k':
+- case KEY_UP:
+- if (!begin_reached) {
+- back_lines (page_length + 1);
+-
+- /* We don't call print_page() here but use scrolling to ensure
+- faster screen update. However, 'end_reached' and
+- 'page_length' should still be updated, and 'page' should
+- point to start of next page. This is done by calling
+- get_line() in the following 'for' loop. */
+- scrollok (text, TRUE);
+- wscrl (text, -1); /* Scroll text region down one line */
+- scrollok (text, FALSE);
+- page_length = 0;
+- passed_end = 0;
+- for (i = 0; i < height - 4; i++) {
+- if (!i) {
+- /* print first line of page */
+- print_line (text, 0, width - 2);
+- wnoutrefresh (text);
+- } else
+- /* Called to update 'end_reached' and 'page' */
+- get_line ();
+- if (!passed_end)
+- page_length++;
+- if (end_reached && !passed_end)
+- passed_end = 1;
+- }
+-
+- print_position (dialog, height, width);
+- wmove (dialog, cur_y, cur_x); /* Restore cursor position */
+- wrefresh (dialog);
+- }
+- break;
+- case 'B': /* Previous page */
+- case 'b':
+- case KEY_PPAGE:
+- if (begin_reached)
+- break;
+- back_lines (page_length + height - 4);
+- print_page (text, height - 4, width - 2);
+- print_position (dialog, height, width);
+- wmove (dialog, cur_y, cur_x);
+- wrefresh (dialog);
+- break;
+- case 'J': /* Next line */
+- case 'j':
+- case KEY_DOWN:
+- if (!end_reached) {
+- begin_reached = 0;
+- scrollok (text, TRUE);
+- scroll (text); /* Scroll text region up one line */
+- scrollok (text, FALSE);
+- print_line (text, height - 5, width - 2);
+- wnoutrefresh (text);
+- print_position (dialog, height, width);
+- wmove (dialog, cur_y, cur_x); /* Restore cursor position */
+- wrefresh (dialog);
+- }
+- break;
+- case KEY_NPAGE: /* Next page */
+- case ' ':
+- if (end_reached)
+- break;
+-
+- begin_reached = 0;
+- print_page (text, height - 4, width - 2);
+- print_position (dialog, height, width);
+- wmove (dialog, cur_y, cur_x);
+- wrefresh (dialog);
+- break;
+- case '0': /* Beginning of line */
+- case 'H': /* Scroll left */
+- case 'h':
+- case KEY_LEFT:
+- if (hscroll <= 0)
+- break;
+-
+- if (key == '0')
+- hscroll = 0;
+- else
+- hscroll--;
+- /* Reprint current page to scroll horizontally */
+- back_lines (page_length);
+- print_page (text, height - 4, width - 2);
+- wmove (dialog, cur_y, cur_x);
+- wrefresh (dialog);
+- break;
+- case 'L': /* Scroll right */
+- case 'l':
+- case KEY_RIGHT:
+- if (hscroll >= MAX_LEN)
+- break;
+- hscroll++;
+- /* Reprint current page to scroll horizontally */
+- back_lines (page_length);
+- print_page (text, height - 4, width - 2);
+- wmove (dialog, cur_y, cur_x);
+- wrefresh (dialog);
+- break;
+- case ESC:
+- break;
+- }
+- }
+-
+- delwin (dialog);
+- free (buf);
+- close (fd);
+- return 1; /* ESC pressed */
+-}
+-
+-/*
+- * Go back 'n' lines in text file. Called by dialog_textbox().
+- * 'page' will be updated to point to the desired line in 'buf'.
+- */
+-static void
+-back_lines (int n)
+-{
+- int i, fpos;
+-
+- begin_reached = 0;
+- /* We have to distinguish between end_reached and !end_reached
+- since at end of file, the line is not ended by a '\n'.
+- The code inside 'if' basically does a '--page' to move one
+- character backward so as to skip '\n' of the previous line */
+- if (!end_reached) {
+- /* Either beginning of buffer or beginning of file reached? */
+- if (page == buf) {
+- if ((fpos = lseek (fd, 0, SEEK_CUR)) == -1) {
+- endwin ();
+- fprintf (stderr, "\nError moving file pointer in "
+- "back_lines().\n");
+- exit (-1);
+- }
+- if (fpos > bytes_read) { /* Not beginning of file yet */
+- /* We've reached beginning of buffer, but not beginning of
+- file yet, so read previous part of file into buffer.
+- Note that we only move backward for BUF_SIZE/2 bytes,
+- but not BUF_SIZE bytes to avoid re-reading again in
+- print_page() later */
+- /* Really possible to move backward BUF_SIZE/2 bytes? */
+- if (fpos < BUF_SIZE / 2 + bytes_read) {
+- /* No, move less then */
+- if (lseek (fd, 0, SEEK_SET) == -1) {
+- endwin ();
+- fprintf (stderr, "\nError moving file pointer in "
+- "back_lines().\n");
+- exit (-1);
+- }
+- page = buf + fpos - bytes_read;
+- } else { /* Move backward BUF_SIZE/2 bytes */
+- if (lseek (fd, -(BUF_SIZE / 2 + bytes_read), SEEK_CUR)
+- == -1) {
+- endwin ();
+- fprintf (stderr, "\nError moving file pointer "
+- "in back_lines().\n");
+- exit (-1);
+- }
+- page = buf + BUF_SIZE / 2;
+- }
+- if ((bytes_read = read (fd, buf, BUF_SIZE)) == -1) {
+- endwin ();
+- fprintf (stderr, "\nError reading file in back_lines().\n");
+- exit (-1);
+- }
+- buf[bytes_read] = '\0';
+- } else { /* Beginning of file reached */
+- begin_reached = 1;
+- return;
+- }
+- }
+- if (*(--page) != '\n') { /* '--page' here */
+- /* Something's wrong... */
+- endwin ();
+- fprintf (stderr, "\nInternal error in back_lines().\n");
+- exit (-1);
+- }
+- }
+- /* Go back 'n' lines */
+- for (i = 0; i < n; i++)
+- do {
+- if (page == buf) {
+- if ((fpos = lseek (fd, 0, SEEK_CUR)) == -1) {
+- endwin ();
+- fprintf (stderr,
+- "\nError moving file pointer in back_lines().\n");
+- exit (-1);
+- }
+- if (fpos > bytes_read) {
+- /* Really possible to move backward BUF_SIZE/2 bytes? */
+- if (fpos < BUF_SIZE / 2 + bytes_read) {
+- /* No, move less then */
+- if (lseek (fd, 0, SEEK_SET) == -1) {
+- endwin ();
+- fprintf (stderr, "\nError moving file pointer "
+- "in back_lines().\n");
+- exit (-1);
+- }
+- page = buf + fpos - bytes_read;
+- } else { /* Move backward BUF_SIZE/2 bytes */
+- if (lseek (fd, -(BUF_SIZE / 2 + bytes_read),
+- SEEK_CUR) == -1) {
+- endwin ();
+- fprintf (stderr, "\nError moving file pointer"
+- " in back_lines().\n");
+- exit (-1);
+- }
+- page = buf + BUF_SIZE / 2;
+- }
+- if ((bytes_read = read (fd, buf, BUF_SIZE)) == -1) {
+- endwin ();
+- fprintf (stderr, "\nError reading file in "
+- "back_lines().\n");
+- exit (-1);
+- }
+- buf[bytes_read] = '\0';
+- } else { /* Beginning of file reached */
+- begin_reached = 1;
+- return;
+- }
+- }
+- } while (*(--page) != '\n');
+- page++;
+-}
+-
+-/*
+- * Print a new page of text. Called by dialog_textbox().
+- */
+-static void
+-print_page (WINDOW * win, int height, int width)
+-{
+- int i, passed_end = 0;
+-
+- page_length = 0;
+- for (i = 0; i < height; i++) {
+- print_line (win, i, width);
+- if (!passed_end)
+- page_length++;
+- if (end_reached && !passed_end)
+- passed_end = 1;
+- }
+- wnoutrefresh (win);
+-}
+-
+-/*
+- * Print a new line of text. Called by dialog_textbox() and print_page().
+- */
+-static void
+-print_line (WINDOW * win, int row, int width)
+-{
+- int y, x;
+- char *line;
+-
+- line = get_line ();
+- line += MIN (strlen (line), hscroll); /* Scroll horizontally */
+- wmove (win, row, 0); /* move cursor to correct line */
+- waddch (win, ' ');
+- waddnstr (win, line, MIN (strlen (line), width - 2));
+-
+- getyx (win, y, x);
+- /* Clear 'residue' of previous line */
+-#if OLD_NCURSES
+- {
+- int i;
+- for (i = 0; i < width - x; i++)
+- waddch (win, ' ');
+- }
+-#else
+- wclrtoeol(win);
+-#endif
+-}
+-
+-/*
+- * Return current line of text. Called by dialog_textbox() and print_line().
+- * 'page' should point to start of current line before calling, and will be
+- * updated to point to start of next line.
+- */
+-static char *
+-get_line (void)
+-{
+- int i = 0, fpos;
+- static char line[MAX_LEN + 1];
+-
+- end_reached = 0;
+- while (*page != '\n') {
+- if (*page == '\0') {
+- /* Either end of file or end of buffer reached */
+- if ((fpos = lseek (fd, 0, SEEK_CUR)) == -1) {
+- endwin ();
+- fprintf (stderr, "\nError moving file pointer in "
+- "get_line().\n");
+- exit (-1);
+- }
+- if (fpos < file_size) { /* Not end of file yet */
+- /* We've reached end of buffer, but not end of file yet,
+- so read next part of file into buffer */
+- if ((bytes_read = read (fd, buf, BUF_SIZE)) == -1) {
+- endwin ();
+- fprintf (stderr, "\nError reading file in get_line().\n");
+- exit (-1);
+- }
+- buf[bytes_read] = '\0';
+- page = buf;
+- } else {
+- if (!end_reached)
+- end_reached = 1;
+- break;
+- }
+- } else if (i < MAX_LEN)
+- line[i++] = *(page++);
+- else {
+- /* Truncate lines longer than MAX_LEN characters */
+- if (i == MAX_LEN)
+- line[i++] = '\0';
+- page++;
+- }
+- }
+- if (i <= MAX_LEN)
+- line[i] = '\0';
+- if (!end_reached)
+- page++; /* move pass '\n' */
+-
+- return line;
+-}
+-
+-/*
+- * Print current position
+- */
+-static void
+-print_position (WINDOW * win, int height, int width)
+-{
+- int fpos, percent;
+-
+- if ((fpos = lseek (fd, 0, SEEK_CUR)) == -1) {
+- endwin ();
+- fprintf (stderr, "\nError moving file pointer in print_position().\n");
+- exit (-1);
+- }
+- wattrset (win, position_indicator_attr);
+- wbkgdset (win, position_indicator_attr & A_COLOR);
+- percent = !file_size ?
+- 100 : ((fpos - bytes_read + page - buf) * 100) / file_size;
+- wmove (win, height - 3, width - 9);
+- wprintw (win, "(%3d%%)", percent);
+-}
+diff -Nur busybox-1.00/scripts/config/util.c busybox/scripts/config/util.c
+--- busybox-1.00/scripts/config/util.c 2004-07-15 08:01:05.000000000 +0200
++++ busybox/scripts/config/util.c 2005-06-04 08:20:03.000000000 +0200
+@@ -1,375 +1,109 @@
+ /*
+- * util.c
++ * Copyright (C) 2002-2005 Roman Zippel <zippel@linux-m68k.org>
++ * Copyright (C) 2002-2005 Sam Ravnborg <sam@ravnborg.org>
+ *
+- * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
+- * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com)
+- *
+- * This program is free software; you can redistribute it and/or
+- * modify it under the terms of the GNU General Public License
+- * as published by the Free Software Foundation; either version 2
+- * of the License, or (at your option) any later version.
+- *
+- * This program is distributed in the hope that it will be useful,
+- * but WITHOUT ANY WARRANTY; without even the implied warranty of
+- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+- * GNU General Public License for more details.
+- *
+- * You should have received a copy of the GNU General Public License
+- * along with this program; if not, write to the Free Software
+- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+- */
+-
+-#include "dialog.h"
+-
+-
+-/* use colors by default? */
+-bool use_colors = 1;
+-
+-char *backtitle = NULL;
+-
+-const char *dialog_result;
+-
+-/*
+- * Attribute values, default is for mono display
++ * Released under the terms of the GNU GPL v2.0.
+ */
+-chtype attributes[] =
+-{
+- A_NORMAL, /* screen_attr */
+- A_NORMAL, /* shadow_attr */
+- A_NORMAL, /* dialog_attr */
+- A_BOLD, /* title_attr */
+- A_NORMAL, /* border_attr */
+- A_REVERSE, /* button_active_attr */
+- A_DIM, /* button_inactive_attr */
+- A_REVERSE, /* button_key_active_attr */
+- A_BOLD, /* button_key_inactive_attr */
+- A_REVERSE, /* button_label_active_attr */
+- A_NORMAL, /* button_label_inactive_attr */
+- A_NORMAL, /* inputbox_attr */
+- A_NORMAL, /* inputbox_border_attr */
+- A_NORMAL, /* searchbox_attr */
+- A_BOLD, /* searchbox_title_attr */
+- A_NORMAL, /* searchbox_border_attr */
+- A_BOLD, /* position_indicator_attr */
+- A_NORMAL, /* menubox_attr */
+- A_NORMAL, /* menubox_border_attr */
+- A_NORMAL, /* item_attr */
+- A_REVERSE, /* item_selected_attr */
+- A_BOLD, /* tag_attr */
+- A_REVERSE, /* tag_selected_attr */
+- A_BOLD, /* tag_key_attr */
+- A_REVERSE, /* tag_key_selected_attr */
+- A_BOLD, /* check_attr */
+- A_REVERSE, /* check_selected_attr */
+- A_BOLD, /* uarrow_attr */
+- A_BOLD /* darrow_attr */
+-};
+
++#include <string.h>
++#include "lkc.h"
+
+-#include "colors.h"
+-
+-/*
+- * Table of color values
+- */
+-int color_table[][3] =
++/* file already present in list? If not add it */
++struct file *file_lookup(const char *name)
+ {
+- {SCREEN_FG, SCREEN_BG, SCREEN_HL},
+- {SHADOW_FG, SHADOW_BG, SHADOW_HL},
+- {DIALOG_FG, DIALOG_BG, DIALOG_HL},
+- {TITLE_FG, TITLE_BG, TITLE_HL},
+- {BORDER_FG, BORDER_BG, BORDER_HL},
+- {BUTTON_ACTIVE_FG, BUTTON_ACTIVE_BG, BUTTON_ACTIVE_HL},
+- {BUTTON_INACTIVE_FG, BUTTON_INACTIVE_BG, BUTTON_INACTIVE_HL},
+- {BUTTON_KEY_ACTIVE_FG, BUTTON_KEY_ACTIVE_BG, BUTTON_KEY_ACTIVE_HL},
+- {BUTTON_KEY_INACTIVE_FG, BUTTON_KEY_INACTIVE_BG, BUTTON_KEY_INACTIVE_HL},
+- {BUTTON_LABEL_ACTIVE_FG, BUTTON_LABEL_ACTIVE_BG, BUTTON_LABEL_ACTIVE_HL},
+- {BUTTON_LABEL_INACTIVE_FG, BUTTON_LABEL_INACTIVE_BG,
+- BUTTON_LABEL_INACTIVE_HL},
+- {INPUTBOX_FG, INPUTBOX_BG, INPUTBOX_HL},
+- {INPUTBOX_BORDER_FG, INPUTBOX_BORDER_BG, INPUTBOX_BORDER_HL},
+- {SEARCHBOX_FG, SEARCHBOX_BG, SEARCHBOX_HL},
+- {SEARCHBOX_TITLE_FG, SEARCHBOX_TITLE_BG, SEARCHBOX_TITLE_HL},
+- {SEARCHBOX_BORDER_FG, SEARCHBOX_BORDER_BG, SEARCHBOX_BORDER_HL},
+- {POSITION_INDICATOR_FG, POSITION_INDICATOR_BG, POSITION_INDICATOR_HL},
+- {MENUBOX_FG, MENUBOX_BG, MENUBOX_HL},
+- {MENUBOX_BORDER_FG, MENUBOX_BORDER_BG, MENUBOX_BORDER_HL},
+- {ITEM_FG, ITEM_BG, ITEM_HL},
+- {ITEM_SELECTED_FG, ITEM_SELECTED_BG, ITEM_SELECTED_HL},
+- {TAG_FG, TAG_BG, TAG_HL},
+- {TAG_SELECTED_FG, TAG_SELECTED_BG, TAG_SELECTED_HL},
+- {TAG_KEY_FG, TAG_KEY_BG, TAG_KEY_HL},
+- {TAG_KEY_SELECTED_FG, TAG_KEY_SELECTED_BG, TAG_KEY_SELECTED_HL},
+- {CHECK_FG, CHECK_BG, CHECK_HL},
+- {CHECK_SELECTED_FG, CHECK_SELECTED_BG, CHECK_SELECTED_HL},
+- {UARROW_FG, UARROW_BG, UARROW_HL},
+- {DARROW_FG, DARROW_BG, DARROW_HL},
+-}; /* color_table */
++ struct file *file;
+
+-/*
+- * Set window to attribute 'attr'
+- */
+-void
+-attr_clear (WINDOW * win, int height, int width, chtype attr)
+-{
+- int i, j;
++ for (file = file_list; file; file = file->next) {
++ if (!strcmp(name, file->name))
++ return file;
++ }
+
+- wattrset (win, attr);
+- for (i = 0; i < height; i++) {
+- wmove (win, i, 0);
+- for (j = 0; j < width; j++)
+- waddch (win, ' ');
+- }
+- touchwin (win);
++ file = malloc(sizeof(*file));
++ memset(file, 0, sizeof(*file));
++ file->name = strdup(name);
++ file->next = file_list;
++ file_list = file;
++ return file;
++}
++
++/* write a dependency file as used by kbuild to track dependencies */
++int file_write_dep(const char *name)
++{
++ struct file *file;
++ FILE *out;
++
++ if (!name)
++ name = ".config.cmd";
++ out = fopen(".config.tmp", "w");
++ if (!out)
++ return 1;
++ fprintf(out, "deps_config := \\\n");
++ for (file = file_list; file; file = file->next) {
++ if (file->next)
++ fprintf(out, "\t%s \\\n", file->name);
++ else
++ fprintf(out, "\t%s\n", file->name);
++ }
++ fprintf(out, "\n.config include/config.h: $(deps_config)\n\n$(deps_config):\n");
++ fclose(out);
++ rename(".config.tmp", name);
++ return 0;
+ }
+
+-void dialog_clear (void)
+-{
+- attr_clear (stdscr, LINES, COLS, screen_attr);
+- /* Display background title if it exists ... - SLH */
+- if (backtitle != NULL) {
+- int i;
+-
+- wattrset (stdscr, screen_attr);
+- mvwaddstr (stdscr, 0, 1, (char *)backtitle);
+- wmove (stdscr, 1, 1);
+- for (i = 1; i < COLS - 1; i++)
+- waddch (stdscr, ACS_HLINE);
+- }
+- wnoutrefresh (stdscr);
+-}
+
+-/*
+- * Do some initialization for dialog
+- */
+-void
+-init_dialog (void)
++/* Allocate initial growable sting */
++struct gstr str_new(void)
+ {
+- initscr (); /* Init curses */
+- keypad (stdscr, TRUE);
+- cbreak ();
+- noecho ();
+-
+-
+- if (use_colors) /* Set up colors */
+- color_setup ();
+-
+-
+- dialog_clear ();
++ struct gstr gs;
++ gs.s = malloc(sizeof(char) * 64);
++ gs.len = 16;
++ strcpy(gs.s, "\0");
++ return gs;
+ }
+
+-/*
+- * Setup for color display
+- */
+-void
+-color_setup (void)
++/* Allocate and assign growable string */
++struct gstr str_assign(const char *s)
+ {
+- int i;
+-
+- if (has_colors ()) { /* Terminal supports color? */
+- start_color ();
+-
+- /* Initialize color pairs */
+- for (i = 0; i < ATTRIBUTE_COUNT; i++)
+- init_pair (i + 1, color_table[i][0], color_table[i][1]);
+-
+- /* Setup color attributes */
+- for (i = 0; i < ATTRIBUTE_COUNT; i++)
+- attributes[i] = C_ATTR (color_table[i][2], i + 1);
+- }
++ struct gstr gs;
++ gs.s = strdup(s);
++ gs.len = strlen(s) + 1;
++ return gs;
+ }
+
+-/*
+- * End using dialog functions.
+- */
+-void
+-end_dialog (void)
++/* Free storage for growable string */
++void str_free(struct gstr *gs)
+ {
+- endwin ();
++ if (gs->s)
++ free(gs->s);
++ gs->s = NULL;
++ gs->len = 0;
+ }
+
+-
+-/*
+- * Print a string of text in a window, automatically wrap around to the
+- * next line if the string is too long to fit on one line. Newline
+- * characters '\n' are replaced by spaces. We start on a new line
+- * if there is no room for at least 4 nonblanks following a double-space.
+- */
+-void
+-print_autowrap (WINDOW * win, const char *prompt, int width, int y, int x)
++/* Append to growable string */
++void str_append(struct gstr *gs, const char *s)
+ {
+- int newl, cur_x, cur_y;
+- int i, prompt_len, room, wlen;
+- char tempstr[MAX_LEN + 1], *word, *sp, *sp2;
+-
+- strcpy (tempstr, prompt);
+-
+- prompt_len = strlen(tempstr);
+-
+- /*
+- * Remove newlines
+- */
+- for(i=0; i<prompt_len; i++) {
+- if(tempstr[i] == '\n') tempstr[i] = ' ';
+- }
+-
+- if (prompt_len <= width - x * 2) { /* If prompt is short */
+- wmove (win, y, (width - prompt_len) / 2);
+- waddstr (win, tempstr);
+- } else {
+- cur_x = x;
+- cur_y = y;
+- newl = 1;
+- word = tempstr;
+- while (word && *word) {
+- sp = index(word, ' ');
+- if (sp)
+- *sp++ = 0;
+-
+- /* Wrap to next line if either the word does not fit,
+- or it is the first word of a new sentence, and it is
+- short, and the next word does not fit. */
+- room = width - cur_x;
+- wlen = strlen(word);
+- if (wlen > room ||
+- (newl && wlen < 4 && sp && wlen+1+strlen(sp) > room
+- && (!(sp2 = index(sp, ' ')) || wlen+1+(sp2-sp) > room))) {
+- cur_y++;
+- cur_x = x;
+- }
+- wmove (win, cur_y, cur_x);
+- waddstr (win, word);
+- getyx (win, cur_y, cur_x);
+- cur_x++;
+- if (sp && *sp == ' ') {
+- cur_x++; /* double space */
+- while (*++sp == ' ');
+- newl = 1;
+- } else
+- newl = 0;
+- word = sp;
++ size_t l = strlen(gs->s) + strlen(s) + 1;
++ if (l > gs->len) {
++ gs->s = realloc(gs->s, l);
++ gs->len = l;
+ }
+- }
+-}
+-
+-/*
+- * Print a button
+- */
+-void
+-print_button (WINDOW * win, const char *label, int y, int x, int selected)
+-{
+- int i, temp;
+-
+- wmove (win, y, x);
+- wattrset (win, selected ? button_active_attr : button_inactive_attr);
+- waddstr (win, "<");
+- temp = strspn (label, " ");
+- label += temp;
+- wattrset (win, selected ? button_label_active_attr
+- : button_label_inactive_attr);
+- for (i = 0; i < temp; i++)
+- waddch (win, ' ');
+- wattrset (win, selected ? button_key_active_attr
+- : button_key_inactive_attr);
+- waddch (win, label[0]);
+- wattrset (win, selected ? button_label_active_attr
+- : button_label_inactive_attr);
+- waddstr (win, (char *)label + 1);
+- wattrset (win, selected ? button_active_attr : button_inactive_attr);
+- waddstr (win, ">");
+- wmove (win, y, x + temp + 1);
++ strcat(gs->s, s);
+ }
+
+-/*
+- * Draw a rectangular box with line drawing characters
+- */
+-void
+-draw_box (WINDOW * win, int y, int x, int height, int width,
+- chtype box, chtype border)
+-{
+- int i, j;
+-
+- wattrset (win, 0);
+- for (i = 0; i < height; i++) {
+- wmove (win, y + i, x);
+- for (j = 0; j < width; j++)
+- if (!i && !j)
+- waddch (win, border | ACS_ULCORNER);
+- else if (i == height - 1 && !j)
+- waddch (win, border | ACS_LLCORNER);
+- else if (!i && j == width - 1)
+- waddch (win, box | ACS_URCORNER);
+- else if (i == height - 1 && j == width - 1)
+- waddch (win, box | ACS_LRCORNER);
+- else if (!i)
+- waddch (win, border | ACS_HLINE);
+- else if (i == height - 1)
+- waddch (win, box | ACS_HLINE);
+- else if (!j)
+- waddch (win, border | ACS_VLINE);
+- else if (j == width - 1)
+- waddch (win, box | ACS_VLINE);
+- else
+- waddch (win, box | ' ');
+- }
+-}
+-
+-/*
+- * Draw shadows along the right and bottom edge to give a more 3D look
+- * to the boxes
+- */
+-void
+-draw_shadow (WINDOW * win, int y, int x, int height, int width)
++/* Append printf formatted string to growable string */
++void str_printf(struct gstr *gs, const char *fmt, ...)
+ {
+- int i;
+-
+- if (has_colors ()) { /* Whether terminal supports color? */
+- wattrset (win, shadow_attr);
+- wmove (win, y + height, x + 2);
+- for (i = 0; i < width; i++)
+- waddch (win, winch (win) & A_CHARTEXT);
+- for (i = y + 1; i < y + height + 1; i++) {
+- wmove (win, i, x + width);
+- waddch (win, winch (win) & A_CHARTEXT);
+- waddch (win, winch (win) & A_CHARTEXT);
+- }
+- wnoutrefresh (win);
+- }
++ va_list ap;
++ char s[10000]; /* big enough... */
++ va_start(ap, fmt);
++ vsnprintf(s, sizeof(s), fmt, ap);
++ str_append(gs, s);
++ va_end(ap);
+ }
+
+-/*
+- * Return the position of the first alphabetic character in a string.
+- */
+-int
+-first_alpha(const char *string, const char *exempt)
++/* Retreive value of growable string */
++const char *str_get(struct gstr *gs)
+ {
+- int i, in_paren=0, c;
+-
+- for (i = 0; i < strlen(string); i++) {
+- c = tolower(string[i]);
+-
+- if (strchr("<[(", c)) ++in_paren;
+- if (strchr(">])", c) && in_paren > 0) --in_paren;
+-
+- if ((! in_paren) && isalpha(c) &&
+- strchr(exempt, c) == 0)
+- return i;
+- }
+-
+- return 0;
++ return gs->s;
+ }
+
+-/*
+- * Get the first selected item in the dialog_list_item list.
+- */
+-struct dialog_list_item *
+-first_sel_item(int item_no, struct dialog_list_item ** items)
+-{
+- int i;
+-
+- for (i = 0; i < item_no; i++) {
+- if (items[i]->selected)
+- return items[i];
+- }
+-
+- return NULL;
+-}
+diff -Nur busybox-1.00/scripts/config/yesno.c busybox/scripts/config/yesno.c
+--- busybox-1.00/scripts/config/yesno.c 2002-12-05 09:41:08.000000000 +0100
++++ busybox/scripts/config/yesno.c 1970-01-01 01:00:00.000000000 +0100
+@@ -1,118 +0,0 @@
+-/*
+- * yesno.c -- implements the yes/no box
+- *
+- * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
+- * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com)
+- *
+- * This program is free software; you can redistribute it and/or
+- * modify it under the terms of the GNU General Public License
+- * as published by the Free Software Foundation; either version 2
+- * of the License, or (at your option) any later version.
+- *
+- * This program is distributed in the hope that it will be useful,
+- * but WITHOUT ANY WARRANTY; without even the implied warranty of
+- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+- * GNU General Public License for more details.
+- *
+- * You should have received a copy of the GNU General Public License
+- * along with this program; if not, write to the Free Software
+- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+- */
+-
+-#include "dialog.h"
+-
+-/*
+- * Display termination buttons
+- */
+-static void
+-print_buttons(WINDOW *dialog, int height, int width, int selected)
+-{
+- int x = width / 2 - 10;
+- int y = height - 2;
+-
+- print_button (dialog, " Yes ", y, x, selected == 0);
+- print_button (dialog, " No ", y, x + 13, selected == 1);
+-
+- wmove(dialog, y, x+1 + 13*selected );
+- wrefresh (dialog);
+-}
+-
+-/*
+- * Display a dialog box with two buttons - Yes and No
+- */
+-int
+-dialog_yesno (const char *title, const char *prompt, int height, int width)
+-{
+- int i, x, y, key = 0, button = 0;
+- WINDOW *dialog;
+-
+- /* center dialog box on screen */
+- x = (COLS - width) / 2;
+- y = (LINES - height) / 2;
+-
+- draw_shadow (stdscr, y, x, height, width);
+-
+- dialog = newwin (height, width, y, x);
+- keypad (dialog, TRUE);
+-
+- draw_box (dialog, 0, 0, height, width, dialog_attr, border_attr);
+- wattrset (dialog, border_attr);
+- mvwaddch (dialog, height-3, 0, ACS_LTEE);
+- for (i = 0; i < width - 2; i++)
+- waddch (dialog, ACS_HLINE);
+- wattrset (dialog, dialog_attr);
+- waddch (dialog, ACS_RTEE);
+-
+- if (title != NULL && strlen(title) >= width-2 ) {
+- /* truncate long title -- mec */
+- char * title2 = malloc(width-2+1);
+- memcpy( title2, title, width-2 );
+- title2[width-2] = '\0';
+- title = title2;
+- }
+-
+- if (title != NULL) {
+- wattrset (dialog, title_attr);
+- mvwaddch (dialog, 0, (width - strlen(title))/2 - 1, ' ');
+- waddstr (dialog, (char *)title);
+- waddch (dialog, ' ');
+- }
+-
+- wattrset (dialog, dialog_attr);
+- print_autowrap (dialog, prompt, width - 2, 1, 3);
+-
+- print_buttons(dialog, height, width, 0);
+-
+- while (key != ESC) {
+- key = wgetch (dialog);
+- switch (key) {
+- case 'Y':
+- case 'y':
+- delwin (dialog);
+- return 0;
+- case 'N':
+- case 'n':
+- delwin (dialog);
+- return 1;
+-
+- case TAB:
+- case KEY_LEFT:
+- case KEY_RIGHT:
+- button = ((key == KEY_LEFT ? --button : ++button) < 0)
+- ? 1 : (button > 1 ? 0 : button);
+-
+- print_buttons(dialog, height, width, button);
+- wrefresh (dialog);
+- break;
+- case ' ':
+- case '\n':
+- delwin (dialog);
+- return button;
+- case ESC:
+- break;
+- }
+- }
+-
+- delwin (dialog);
+- return -1; /* ESC pressed */
+-}
+diff -Nur busybox-1.00/scripts/config/zconf.tab.c_shipped busybox/scripts/config/zconf.tab.c_shipped
+--- busybox-1.00/scripts/config/zconf.tab.c_shipped 2004-03-15 09:29:08.000000000 +0100
++++ busybox/scripts/config/zconf.tab.c_shipped 2005-06-04 08:20:03.000000000 +0200
+@@ -175,6 +175,8 @@
+
+ struct symbol *symbol_hash[257];
+
++static struct menu *current_menu, *current_entry;
++
+ #define YYERROR_VERBOSE
+
+
+@@ -227,7 +229,7 @@
+ # define YYSTACK_ALLOC alloca
+ # else
+ # ifndef YYSTACK_USE_ALLOCA
+-# if defined (alloca) || defined (_ALLOCA_H)
++# if defined (alloca) || (defined (_ALLOCA_H) && defined (__GNUC__))
+ # define YYSTACK_ALLOC alloca
+ # else
+ # ifdef __GNUC__
+@@ -2119,6 +2121,7 @@
+ }
+
+ #include "lex.zconf.c"
++#include "util.c"
+ #include "confdata.c"
+ #include "expr.c"
+ #include "symbol.c"
+diff -Nur busybox-1.00/scripts/config/zconf.y busybox/scripts/config/zconf.y
+--- busybox-1.00/scripts/config/zconf.y 2003-08-05 07:59:48.000000000 +0200
++++ busybox/scripts/config/zconf.y 2005-06-04 08:20:03.000000000 +0200
+@@ -25,6 +25,8 @@
+
+ struct symbol *symbol_hash[257];
+
++static struct menu *current_menu, *current_entry;
++
+ #define YYERROR_VERBOSE
+ %}
+ %expect 40
+@@ -681,6 +683,7 @@
+ }
+
+ #include "lex.zconf.c"
++#include "util.c"
+ #include "confdata.c"
+ #include "expr.c"
+ #include "symbol.c"
+diff -Nur busybox-1.00/shell/Config.in busybox/shell/Config.in
+--- busybox-1.00/shell/Config.in 2004-09-24 11:09:44.000000000 +0200
++++ busybox/shell/Config.in 2005-06-04 08:20:11.000000000 +0200
+@@ -53,6 +53,17 @@
+ help
+ Enable job control in the ash shell.
+
++config CONFIG_ASH_TIMEOUT
++ bool " Enable read timeout support."
++ default n
++ depends on CONFIG_ASH_JOB_CONTROL
++ help
++ This option provides read -t <seconds> support.
++
++ read builtin which allows the function to pass control back
++ if no character input is read from the terminal within a set
++ number of seconds.
++
+ config CONFIG_ASH_ALIAS
+ bool " Enable alias support"
+ default y
+diff -Nur busybox-1.00/shell/ash.c busybox/shell/ash.c
+--- busybox-1.00/shell/ash.c 2004-10-08 11:43:34.000000000 +0200
++++ busybox/shell/ash.c 2005-06-04 08:20:11.000000000 +0200
+@@ -3722,27 +3722,13 @@
+ {
+ int repeated = 0;
+ #ifdef CONFIG_FEATURE_SH_STANDALONE_SHELL
+- int flg_bb = 0;
+- char *name = cmd;
+-
+- if(strchr(name, '/') == NULL && find_applet_by_name(name) != NULL) {
+- flg_bb = 1;
+- }
+- if(flg_bb) {
+- char **ap;
+- char **new;
+-
+- *argv = name;
+- if(strcmp(name, "busybox")) {
+- for (ap = argv; *ap; ap++);
+- ap = new = xmalloc((ap - argv + 2) * sizeof(char *));
+- *ap++ = cmd = "/bin/busybox";
+- while ((*ap++ = *argv++));
+- argv = new;
+- repeated++;
+- } else {
+- cmd = "/bin/busybox";
+- }
++ if(find_applet_by_name(cmd) != NULL) {
++ /* re-exec ourselves with the new arguments */
++ execve("/proc/self/exe",argv,envp);
++ /* If proc isn't mounted, try hardcoded path to busybox binary*/
++ execve("/bin/busybox",argv,envp);
++ /* If they called chroot or otherwise made the binary no longer
++ * executable, fall through */
+ }
+ #endif
+
+@@ -12583,17 +12569,34 @@
+ char *prompt;
+ const char *ifs;
+ char *p;
++#if defined(CONFIG_ASH_TIMEOUT)
++ fd_set set;
++ int timeout;
++ struct timeval timeout_struct;
++ struct termios tty, old_tty;
++#endif
+ int startword;
+ int status;
+ int i;
+
+ rflag = 0;
+ prompt = NULL;
+- while ((i = nextopt("p:r")) != '\0') {
++#if defined(CONFIG_ASH_TIMEOUT)
++ timeout = 0;
++
++ while ((i = nextopt("p:rt:")) != '\0')
++#else
++ while ((i = nextopt("p:r")) != '\0')
++#endif
++ {
+ if (i == 'p')
+ prompt = optionarg;
+- else
++ else if (i == 'r')
+ rflag = 1;
++#if defined(CONFIG_ASH_TIMEOUT)
++ else
++ timeout = atoi(optionarg);
++#endif
+ }
+ if (prompt && isatty(0)) {
+ out2str(prompt);
+@@ -12602,11 +12605,53 @@
+ error("arg count");
+ if ((ifs = bltinlookup("IFS")) == NULL)
+ ifs = defifs;
++#if defined(CONFIG_ASH_TIMEOUT)
++ c = 0;
++#endif
+ status = 0;
+ startword = 1;
+ backslash = 0;
++
+ STARTSTACKSTR(p);
+- for (;;) {
++#if defined(CONFIG_ASH_TIMEOUT)
++ if (timeout > 0) {
++ tcgetattr(0, &tty);
++ old_tty = tty;
++
++ /* cfmakeraw(...) disables too much; we just do this instead. */
++ tty.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN);
++ tcsetattr(0, TCSANOW, &tty);
++
++ FD_ZERO (&set);
++ FD_SET (0, &set);
++
++ timeout_struct.tv_sec = timeout;
++ timeout_struct.tv_usec = 0;
++
++ if ((i = select (FD_SETSIZE, &set, NULL, NULL, &timeout_struct)) == 1)
++ {
++ read(0, &c, 1);
++ if(c == '\n' || c == 4) /* Handle newlines and EOF */
++ i = 0; /* Don't read further... */
++ else
++ STPUTC(c, p); /* Keep reading... */
++ }
++ tcsetattr(0, TCSANOW, &old_tty);
++
++ /* Echo the character so the user knows it was read...
++ Yes, this can be done by setting the ECHO flag, but that
++ echoes ^D and other control characters at this state */
++ if(c != 0)
++ write(1, &c, 1);
++
++ } else
++ i = 1;
++
++ for (;i == 1;)
++#else
++ for (;;)
++#endif
++ {
+ if (read(0, &c, 1) != 1) {
+ status = 1;
+ break;
+diff -Nur busybox-1.00/shell/lash.c busybox/shell/lash.c
+--- busybox-1.00/shell/lash.c 2004-08-16 10:38:34.000000000 +0200
++++ busybox/shell/lash.c 2005-06-04 08:20:11.000000000 +0200
+@@ -1277,11 +1277,17 @@
+ name = child->argv[0];
+
+ {
+- char** argv_l=child->argv;
+- int argc_l;
+- for(argc_l=0;*argv_l!=NULL; argv_l++, argc_l++);
+- optind = 1;
+- run_applet_by_name(name, argc_l, child->argv);
++ char** argv_l=child->argv;
++ int argc_l;
++#ifdef _NEWLIB_VERSION
++ /* newlib uses __getopt_initialized for getopt() in
++ * addition to optind, see newlib/libc/sys/linux/getopt.c
++ */
++ extern int __getopt_initialized = 0;
++#endif
++ for(argc_l=0;*argv_l!=NULL; argv_l++, argc_l++);
++ optind = 1;
++ run_applet_by_name(name, argc_l, child->argv);
+ }
+ #endif
+
+diff -Nur busybox-1.00/sysdeps/linux/Config.in busybox/sysdeps/linux/Config.in
+--- busybox-1.00/sysdeps/linux/Config.in 2004-05-25 13:30:22.000000000 +0200
++++ busybox/sysdeps/linux/Config.in 2005-06-04 08:20:20.000000000 +0200
+@@ -113,6 +113,13 @@
+
+ cp = --- # disable applet cp for everyone
+
++ The file has to be owned by user root, group root and has to be
++ writeable only by root:
++ (chown 0.0 /etc/busybox.conf; chmod 600 /etc/busybox.conf)
++ The busybox executable has to be owned by user root, group
++ root and has to be setuid root for this to work:
++ (chown 0.0 /bin/busybox; chmod 4755 /bin/busybox)
++
+ Robert 'sandman' Griebl has more information here:
+ <url: http://www.softforge.de/bb/suid.html >.
+
+@@ -221,6 +228,7 @@
+ source coreutils/Config.in
+ source console-tools/Config.in
+ source debianutils/Config.in
++source e2fsprogs/Config.in
+ source editors/Config.in
+ source findutils/Config.in
+ source init/Config.in
+@@ -291,4 +299,3 @@
+
+
+ endmenu
+-
+diff -Nur busybox-1.00/sysklogd/Makefile busybox/sysklogd/Makefile
+--- busybox-1.00/sysklogd/Makefile 2004-10-08 09:45:51.000000000 +0200
++++ busybox/sysklogd/Makefile 2005-06-04 08:20:09.000000000 +0200
+@@ -1,6 +1,6 @@
+ # Makefile for busybox
+ #
+-# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
++# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+ #
+ # This program is free software; you can redistribute it and/or modify
+ # it under the terms of the GNU General Public License as published by
+@@ -18,7 +18,7 @@
+ #
+
+ top_srcdir=..
+-top_buildddir=..
++top_builddir=..
+ srcdir=$(top_srcdir)/sysklogd
+ SYSKLOGD_DIR:=./
+ include $(top_builddir)/Rules.mak
+@@ -29,4 +29,3 @@
+
+ clean:
+ rm -f *.o *.a $(AR_TARGET)
+-
+diff -Nur busybox-1.00/sysklogd/logger.c busybox/sysklogd/logger.c
+--- busybox-1.00/sysklogd/logger.c 2004-08-27 00:18:59.000000000 +0200
++++ busybox/sysklogd/logger.c 2005-06-04 08:20:09.000000000 +0200
+@@ -127,7 +127,7 @@
+ }
+ }
+
+- openlog(name, option, (pri | LOG_FACMASK));
++ openlog(name, option, 0);
+ if (optind == argc) {
+ do {
+ /* read from stdin */
+@@ -152,8 +152,8 @@
+ message = xrealloc(message, len);
+ if(!i)
+ message[0] = 0;
+- else
+- strcat(message, " ");
++ else
++ strcat(message, " ");
+ strcat(message, *argv);
+ argv++;
+ }
+diff -Nur busybox-1.00/testsuite/sed/sed-branch-conditional-inverted busybox/testsuite/sed/sed-branch-conditional-inverted
+--- busybox-1.00/testsuite/sed/sed-branch-conditional-inverted 1970-01-01 01:00:00.000000000 +0100
++++ busybox/testsuite/sed/sed-branch-conditional-inverted 2005-06-04 08:20:19.000000000 +0200
+@@ -0,0 +1,14 @@
++busybox sed 's/a/1/;T notone;p;: notone;p'>output <<EOF
++a
++b
++c
++EOF
++cmp -s output - <<EOF
++1
++1
++1
++b
++b
++c
++c
++EOF
+diff -Nur busybox-1.00/util-linux/Config.in busybox/util-linux/Config.in
+--- busybox-1.00/util-linux/Config.in 2004-05-19 13:06:20.000000000 +0200
++++ busybox/util-linux/Config.in 2005-06-04 08:20:22.000000000 +0200
+@@ -5,7 +5,6 @@
+
+ menu "Linux System Utilities"
+
+-
+ config CONFIG_DMESG
+ bool "dmesg"
+ default n
+@@ -27,7 +26,6 @@
+ interface to access a graphics display. Enable this option
+ if you wish to enable the 'fbset' utility.
+
+-
+ config CONFIG_FEATURE_FBSET_FANCY
+ bool " Turn on extra fbset options"
+ default n
+@@ -353,5 +351,11 @@
+ value is /etc/mtab, which is where this file is located on most desktop
+ Linux systems.
+
++config CONFIG_READPROFILE
++ bool "readprofile"
++ default n
++ help
++ This allows you to parse /proc/profile for basic profiling.
++
+ endmenu
+
+diff -Nur busybox-1.00/util-linux/Makefile busybox/util-linux/Makefile
+--- busybox-1.00/util-linux/Makefile 2004-10-08 09:46:08.000000000 +0200
++++ busybox/util-linux/Makefile 2005-06-04 08:20:22.000000000 +0200
+@@ -1,6 +1,6 @@
+ # Makefile for busybox
+ #
+-# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
++# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+ #
+ # This program is free software; you can redistribute it and/or modify
+ # it under the terms of the GNU General Public License as published by
+@@ -18,7 +18,7 @@
+ #
+
+ top_srcdir=..
+-top_buildddir=..
++top_builddir=..
+ srcdir=$(top_srcdir)/util-linux
+ UTILLINUX_DIR:=./
+ include $(top_builddir)/Rules.mak
+@@ -29,4 +29,3 @@
+
+ clean:
+ rm -f *.o *.a $(AR_TARGET)
+-
+diff -Nur busybox-1.00/util-linux/Makefile.in busybox/util-linux/Makefile.in
+--- busybox-1.00/util-linux/Makefile.in 2004-10-08 09:46:08.000000000 +0200
++++ busybox/util-linux/Makefile.in 2005-06-04 08:20:22.000000000 +0200
+@@ -44,6 +44,7 @@
+ UTILLINUX-$(CONFIG_RDATE) +=rdate.o
+ UTILLINUX-$(CONFIG_SWAPONOFF) +=swaponoff.o
+ UTILLINUX-$(CONFIG_UMOUNT) +=umount.o
++UTILLINUX-$(CONFIG_READPROFILE) +=readprofile.o
+
+ libraries-y+=$(UTILLINUX_DIR)$(UTILLINUX_AR)
+
+@@ -63,4 +64,3 @@
+
+ endif
+ endif
+-
+diff -Nur busybox-1.00/util-linux/hwclock.c busybox/util-linux/hwclock.c
+--- busybox-1.00/util-linux/hwclock.c 2004-04-14 19:51:38.000000000 +0200
++++ busybox/util-linux/hwclock.c 2005-06-04 08:20:22.000000000 +0200
+@@ -46,7 +46,7 @@
+ int tm_yday;
+ int tm_isdst;
+ };
+-
++
+ #define RTC_SET_TIME _IOW('p', 0x0a, struct linux_rtc_time) /* Set RTC time */
+ #define RTC_RD_TIME _IOR('p', 0x09, struct linux_rtc_time) /* Read RTC time */
+
+@@ -182,11 +182,11 @@
+ return utc;
+ }
+
+-#define HWCLOCK_OPT_LOCALTIME 1
+-#define HWCLOCK_OPT_UTC 2
+-#define HWCLOCK_OPT_SHOW 4
+-#define HWCLOCK_OPT_HCTOSYS 8
+-#define HWCLOCK_OPT_SYSTOHC 16
++#define HWCLOCK_OPT_LOCALTIME 0x01
++#define HWCLOCK_OPT_UTC 0x02
++#define HWCLOCK_OPT_SHOW 0x04
++#define HWCLOCK_OPT_HCTOSYS 0x08
++#define HWCLOCK_OPT_SYSTOHC 0x10
+
+ extern int hwclock_main ( int argc, char **argv )
+ {
+@@ -208,16 +208,16 @@
+ bb_opt_complementaly = "r~ws:w~rs:s~wr:l~u:u~l";
+ opt = bb_getopt_ulflags(argc, argv, "lursw");
+ /* Check only one mode was given */
+- if(opt & 0x80000000UL) {
++ if(opt & BB_GETOPT_ERROR) {
+ bb_show_usage();
+ }
+
+ /* If -u or -l wasn't given check if we are using utc */
+- if (opt & (HWCLOCK_OPT_UTC | HWCLOCK_OPT_LOCALTIME))
++ if (opt & (HWCLOCK_OPT_UTC | HWCLOCK_OPT_LOCALTIME))
+ utc = opt & HWCLOCK_OPT_UTC;
+ else
+ utc = check_utc();
+-
++
+ if (opt & HWCLOCK_OPT_HCTOSYS) {
+ return to_sys_clock ( utc );
+ }
+diff -Nur busybox-1.00/util-linux/readprofile.c busybox/util-linux/readprofile.c
+--- busybox-1.00/util-linux/readprofile.c 1970-01-01 01:00:00.000000000 +0100
++++ busybox/util-linux/readprofile.c 2005-06-04 08:20:22.000000000 +0200
+@@ -0,0 +1,302 @@
++/*
++ * readprofile.c - used to read /proc/profile
++ *
++ * Copyright (C) 1994,1996 Alessandro Rubini (rubini@ipvvis.unipv.it)
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
++ */
++
++/*
++ * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
++ * - added Native Language Support
++ * 1999-09-01 Stephane Eranian <eranian@cello.hpl.hp.com>
++ * - 64bit clean patch
++ * 3Feb2001 Andrew Morton <andrewm@uow.edu.au>
++ * - -M option to write profile multiplier.
++ * 2001-11-07 Werner Almesberger <wa@almesberger.net>
++ * - byte order auto-detection and -n option
++ * 2001-11-09 Werner Almesberger <wa@almesberger.net>
++ * - skip step size (index 0)
++ * 2002-03-09 John Levon <moz@compsoc.man.ac.uk>
++ * - make maplineno do something
++ * 2002-11-28 Mads Martin Joergensen +
++ * - also try /boot/System.map-`uname -r`
++ * 2003-04-09 Werner Almesberger <wa@almesberger.net>
++ * - fixed off-by eight error and improved heuristics in byte order detection
++ * 2003-08-12 Nikita Danilov <Nikita@Namesys.COM>
++ * - added -s option; example of use:
++ * "readprofile -s -m /boot/System.map-test | grep __d_lookup | sort -n -k3"
++ *
++ * Taken from util-linux and adapted for busybox by
++ * Paul Mundt <lethal@linux-sh.org>.
++ */
++
++#include <errno.h>
++#include <stdio.h>
++#include <fcntl.h>
++#include <stdlib.h>
++#include <unistd.h>
++#include <string.h>
++#include <sys/types.h>
++#include <sys/stat.h>
++#include <sys/utsname.h>
++
++#include "busybox.h"
++
++#define S_LEN 128
++
++/* These are the defaults */
++static char defaultmap[]="/boot/System.map";
++static char defaultpro[]="/proc/profile";
++
++int readprofile_main(int argc, char **argv)
++{
++ FILE *map;
++ int proFd;
++ char *mapFile, *proFile, *mult=0;
++ unsigned long len=0, indx=1;
++ unsigned long long add0=0;
++ unsigned int step;
++ unsigned int *buf, total, fn_len;
++ unsigned long long fn_add, next_add; /* current and next address */
++ char fn_name[S_LEN], next_name[S_LEN]; /* current and next name */
++ char mode[8];
++ int c;
++ int optAll=0, optInfo=0, optReset=0, optVerbose=0, optNative=0;
++ int optBins=0, optSub=0;
++ char mapline[S_LEN];
++ int maplineno=1;
++ int header_printed;
++
++#define next (current^1)
++
++ proFile = defaultpro;
++ mapFile = defaultmap;
++
++ while ((c = getopt(argc, argv, "M:m:np:itvarVbs")) != -1) {
++ switch(c) {
++ case 'm':
++ mapFile = optarg;
++ break;
++ case 'n':
++ optNative++;
++ break;
++ case 'p':
++ proFile = optarg;
++ break;
++ case 'a':
++ optAll++;
++ break;
++ case 'b':
++ optBins++;
++ break;
++ case 's':
++ optSub++;
++ break;
++ case 'i':
++ optInfo++;
++ break;
++ case 'M':
++ mult = optarg;
++ break;
++ case 'r':
++ optReset++;
++ break;
++ case 'v':
++ optVerbose++;
++ break;
++ default:
++ bb_show_usage();
++ }
++ }
++
++ if (optReset || mult) {
++ int multiplier, fd, to_write;
++
++ /*
++ * When writing the multiplier, if the length of the write is
++ * not sizeof(int), the multiplier is not changed
++ */
++ if (mult) {
++ multiplier = strtoul(mult, 0, 10);
++ to_write = sizeof(int);
++ } else {
++ multiplier = 0;
++ to_write = 1; /* sth different from sizeof(int) */
++ }
++
++ fd = bb_xopen(defaultpro,O_WRONLY);
++ if (fd < 0)
++ bb_perror_msg_and_die(defaultpro);
++
++ if (write(fd, &multiplier, to_write) != to_write)
++ bb_perror_msg_and_die("error writing %s", defaultpro);
++
++ close(fd);
++ return EXIT_SUCCESS;
++ }
++
++ /*
++ * Use an fd for the profiling buffer, to skip stdio overhead
++ */
++ if (((proFd = bb_xopen(proFile,O_RDONLY)) < 0)
++ || ((int)(len=lseek(proFd,0,SEEK_END)) < 0)
++ || (lseek(proFd,0,SEEK_SET) < 0))
++ bb_perror_msg_and_die(proFile);
++
++ if (!(buf = xmalloc(len)))
++ bb_perror_nomsg_and_die();
++
++ if (read(proFd,buf,len) != len)
++ bb_perror_msg_and_die(proFile);
++
++ close(proFd);
++
++ if (!optNative) {
++ int entries = len/sizeof(*buf);
++ int big = 0,small = 0,i;
++ unsigned *p;
++
++ for (p = buf+1; p < buf+entries; p++) {
++ if (*p & ~0U << (sizeof(*buf)*4))
++ big++;
++ if (*p & ((1 << (sizeof(*buf)*4))-1))
++ small++;
++ }
++ if (big > small) {
++ fprintf(stderr,"Assuming reversed byte order. "
++ "Use -n to force native byte order.\n");
++ for (p = buf; p < buf+entries; p++)
++ for (i = 0; i < sizeof(*buf)/2; i++) {
++ unsigned char *b = (unsigned char *) p;
++ unsigned char tmp;
++
++ tmp = b[i];
++ b[i] = b[sizeof(*buf)-i-1];
++ b[sizeof(*buf)-i-1] = tmp;
++ }
++ }
++ }
++
++ step = buf[0];
++ if (optInfo) {
++ printf("Sampling_step: %i\n", step);
++ return EXIT_SUCCESS;
++ }
++
++ total = 0;
++
++ map = bb_xfopen(mapFile, "r");
++ if (map == NULL)
++ bb_perror_msg_and_die(mapFile);
++
++ while (fgets(mapline,S_LEN,map)) {
++ if (sscanf(mapline,"%llx %s %s",&fn_add,mode,fn_name) != 3)
++ bb_error_msg_and_die("%s(%i): wrong map line",
++ mapFile, maplineno);
++
++ if (!strcmp(fn_name,"_stext")) /* only elf works like this */ {
++ add0 = fn_add;
++ break;
++ }
++ maplineno++;
++ }
++
++ if (!add0)
++ bb_error_msg_and_die("can't find \"_stext\" in %s\n", mapFile);
++
++ /*
++ * Main loop.
++ */
++ while (fgets(mapline,S_LEN,map)) {
++ unsigned int this = 0;
++
++ if (sscanf(mapline,"%llx %s %s",&next_add,mode,next_name) != 3)
++ bb_error_msg_and_die("%s(%i): wrong map line\n",
++ mapFile, maplineno);
++
++ header_printed = 0;
++
++ /* ignore any LEADING (before a '[tT]' symbol is found)
++ Absolute symbols */
++ if ((*mode == 'A' || *mode == '?') && total == 0) continue;
++ if (*mode != 'T' && *mode != 't' &&
++ *mode != 'W' && *mode != 'w')
++ break; /* only text is profiled */
++
++ if (indx >= len / sizeof(*buf))
++ bb_error_msg_and_die("profile address out of range. "
++ "Wrong map file?");
++
++ while (indx < (next_add-add0)/step) {
++ if (optBins && (buf[indx] || optAll)) {
++ if (!header_printed) {
++ printf ("%s:\n", fn_name);
++ header_printed = 1;
++ }
++ printf ("\t%llx\t%u\n", (indx - 1)*step + add0, buf[indx]);
++ }
++ this += buf[indx++];
++ }
++ total += this;
++
++ if (optBins) {
++ if (optVerbose || this > 0)
++ printf (" total\t\t\t\t%u\n", this);
++ } else if ((this || optAll) &&
++ (fn_len = next_add-fn_add) != 0) {
++ if (optVerbose)
++ printf("%016llx %-40s %6i %8.4f\n", fn_add,
++ fn_name,this,this/(double)fn_len);
++ else
++ printf("%6i %-40s %8.4f\n",
++ this,fn_name,this/(double)fn_len);
++ if (optSub) {
++ unsigned long long scan;
++
++ for (scan = (fn_add-add0)/step + 1;
++ scan < (next_add-add0)/step; scan++) {
++ unsigned long long addr;
++
++ addr = (scan - 1)*step + add0;
++ printf("\t%#llx\t%s+%#llx\t%u\n",
++ addr, fn_name, addr - fn_add,
++ buf[scan]);
++ }
++ }
++ }
++
++ fn_add = next_add;
++ strcpy(fn_name,next_name);
++
++ maplineno++;
++ }
++
++ /* clock ticks, out of kernel text - probably modules */
++ printf("%6i %s\n", buf[len/sizeof(*buf)-1], "*unknown*");
++
++ /* trailer */
++ if (optVerbose)
++ printf("%016x %-40s %6i %8.4f\n",
++ 0,"total",total,total/(double)(fn_add-add0));
++ else
++ printf("%6i %-40s %8.4f\n",
++ total,"total",total/(double)(fn_add-add0));
++
++ fclose(map);
++ free(buf);
++
++ return EXIT_SUCCESS;
++}