Project import generated by Copybara.

GitOrigin-RevId: 32b8332674f60706a7a5effc4b1721e22f487ca7
diff --git a/extras/ext4_utils/Android.mk b/extras/ext4_utils/Android.mk
new file mode 100644
index 0000000..a9fb5dd
--- /dev/null
+++ b/extras/ext4_utils/Android.mk
@@ -0,0 +1,190 @@
+# Copyright 2010 The Android Open Source Project
+
+LOCAL_PATH:= $(call my-dir)
+
+libext4_utils_src_files := \
+    make_ext4fs.c \
+    ext4fixup.c \
+    ext4_utils.c \
+    allocate.c \
+    contents.c \
+    extent.c \
+    indirect.c \
+    uuid.c \
+    sha1.c \
+    wipe.c
+
+#
+# -- All host/targets including windows
+#
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(libext4_utils_src_files)
+LOCAL_MODULE := libext4_utils_host
+LOCAL_C_INCLUDES += external/zlib
+LOCAL_STATIC_LIBRARIES += libsparse_host
+ifeq ($(BUILD_EUREKA),true)
+  LOCAL_CFLAGS += -include $(HOST_OUT_COMMON_INTERMEDIATES)/have_selinux.h
+endif # BUILD_EUREKA
+ifeq ($(HAVE_SELINUX), true)
+  LOCAL_C_INCLUDES += external/libselinux/include
+  LOCAL_STATIC_LIBRARIES += libselinux
+  LOCAL_CFLAGS += -DHAVE_SELINUX
+endif # HAVE_SELINUX
+include $(BUILD_HOST_STATIC_LIBRARY)
+
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := make_ext4fs_main.c
+LOCAL_MODULE := make_ext4fs
+LOCAL_STATIC_LIBRARIES += \
+    libext4_utils_host \
+    libsparse_host \
+    libfsconfig \
+    libz
+ifeq ($(HOST_OS),windows)
+  LOCAL_LDLIBS += -lws2_32
+else
+  ifeq ($(BUILD_EUREKA),true)
+    LOCAL_CFLAGS += -include $(HOST_OUT_COMMON_INTERMEDIATES)/have_selinux.h
+  endif # BUILD_EUREKA
+  ifeq ($(HAVE_SELINUX), true)
+    LOCAL_C_INCLUDES += external/libselinux/include
+    LOCAL_STATIC_LIBRARIES += libselinux
+    LOCAL_CFLAGS += -DHAVE_SELINUX
+  endif # HAVE_SELINUX
+endif
+include $(BUILD_HOST_EXECUTABLE)
+
+
+#
+# -- All host/targets excluding windows
+#
+
+ifneq ($(HOST_OS),windows)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(libext4_utils_src_files)
+LOCAL_MODULE := libext4_utils
+LOCAL_C_INCLUDES += external/zlib
+LOCAL_SHARED_LIBRARIES := \
+    libsparse \
+    libz
+ifeq ($(BUILD_EUREKA),true)
+  LOCAL_CFLAGS += -include $(HOST_OUT_COMMON_INTERMEDIATES)/have_selinux.h
+endif # BUILD_EUREKA
+ifeq ($(HAVE_SELINUX), true)
+  LOCAL_C_INCLUDES += external/libselinux/include
+  LOCAL_SHARED_LIBRARIES += libselinux
+  LOCAL_CFLAGS += -DHAVE_SELINUX
+endif # HAVE_SELINUX
+include $(BUILD_SHARED_LIBRARY)
+
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(libext4_utils_src_files)
+LOCAL_MODULE := libext4_utils_static
+LOCAL_C_INCLUDES += external/zlib
+LOCAL_STATIC_LIBRARIES += \
+    libsparse_static
+ifeq ($(BUILD_EUREKA),true)
+  LOCAL_CFLAGS += -include $(HOST_OUT_COMMON_INTERMEDIATES)/have_selinux.h
+endif # BUILD_EUREKA
+ifeq ($(HAVE_SELINUX), true)
+  LOCAL_C_INCLUDES += external/libselinux/include
+  LOCAL_STATIC_LIBRARIES += libselinux
+  LOCAL_CFLAGS += -DHAVE_SELINUX
+endif # HAVE_SELINUX
+include $(BUILD_STATIC_LIBRARY)
+
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := make_ext4fs_main.c
+LOCAL_MODULE := make_ext4fs
+LOCAL_STATIC_LIBRARIES := libfsconfig
+LOCAL_SHARED_LIBRARIES += libext4_utils libz
+ifeq ($(BUILD_EUREKA),true)
+  LOCAL_CFLAGS += -include $(HOST_OUT_COMMON_INTERMEDIATES)/have_selinux.h
+endif # BUILD_EUREKA
+ifeq ($(HAVE_SELINUX), true)
+  LOCAL_C_INCLUDES += external/libselinux/include
+  LOCAL_SHARED_LIBRARIES += libselinux
+  LOCAL_CFLAGS += -DHAVE_SELINUX
+endif # HAVE_SELINUX
+include $(BUILD_EXECUTABLE)
+
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := ext2simg.c
+LOCAL_MODULE := ext2simg
+LOCAL_SHARED_LIBRARIES += \
+    libext4_utils \
+    libsparse \
+    libz
+ifeq ($(BUILD_EUREKA),true)
+  LOCAL_CFLAGS += -include $(HOST_OUT_COMMON_INTERMEDIATES)/have_selinux.h
+endif # BUILD_EUREKA
+ifeq ($(HAVE_SELINUX), true)
+  LOCAL_C_INCLUDES += external/libselinux/include
+  LOCAL_SHARED_LIBRARIES += libselinux
+  LOCAL_CFLAGS += -DHAVE_SELINUX
+endif # HAVE_SELINUX
+include $(BUILD_EXECUTABLE)
+
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := ext2simg.c
+LOCAL_MODULE := ext2simg
+LOCAL_STATIC_LIBRARIES += \
+    libext4_utils_host \
+    libsparse_host \
+    libz
+ifeq ($(BUILD_EUREKA),true)
+  LOCAL_CFLAGS += -include $(HOST_OUT_COMMON_INTERMEDIATES)/have_selinux.h
+endif # BUILD_EUREKA
+ifeq ($(HAVE_SELINUX), true)
+  LOCAL_C_INCLUDES += external/libselinux/include
+  LOCAL_STATIC_LIBRARIES += libselinux
+  LOCAL_CFLAGS += -DHAVE_SELINUX
+endif # HAVE_SELINUX
+include $(BUILD_HOST_EXECUTABLE)
+
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := setup_fs.c
+LOCAL_MODULE := setup_fs
+LOCAL_SHARED_LIBRARIES += libcutils
+include $(BUILD_EXECUTABLE)
+
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := ext4fixup_main.c
+LOCAL_MODULE := ext4fixup
+LOCAL_SHARED_LIBRARIES += \
+    libext4_utils \
+    libsparse \
+    libz
+include $(BUILD_EXECUTABLE)
+
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := ext4fixup_main.c
+LOCAL_MODULE := ext4fixup
+LOCAL_STATIC_LIBRARIES += \
+    libext4_utils_host \
+    libsparse_host \
+    libz
+include $(BUILD_HOST_EXECUTABLE)
+
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := mkuserimg.sh
+LOCAL_SRC_FILES := mkuserimg.sh
+LOCAL_MODULE_CLASS := EXECUTABLES
+# We don't need any additional suffix.
+LOCAL_MODULE_SUFFIX :=
+LOCAL_BUILT_MODULE_STEM := $(notdir $(LOCAL_SRC_FILES))
+LOCAL_IS_HOST_MODULE := true
+include $(BUILD_PREBUILT)
+
+endif
diff --git a/extras/ext4_utils/MODULE_LICENSE_APACHE2 b/extras/ext4_utils/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/extras/ext4_utils/MODULE_LICENSE_APACHE2
diff --git a/extras/ext4_utils/NOTICE b/extras/ext4_utils/NOTICE
new file mode 100644
index 0000000..5d14293
--- /dev/null
+++ b/extras/ext4_utils/NOTICE
@@ -0,0 +1,190 @@
+
+   Copyright (c) 2010, The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
diff --git a/extras/ext4_utils/allocate.c b/extras/ext4_utils/allocate.c
new file mode 100644
index 0000000..3fd39c6
--- /dev/null
+++ b/extras/ext4_utils/allocate.c
@@ -0,0 +1,733 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ext4_utils.h"
+#include "allocate.h"
+#include "ext4.h"
+
+#include <sparse/sparse.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+struct region_list {
+	struct region *first;
+	struct region *last;
+	struct region *iter;
+	u32 partial_iter;
+};
+
+struct block_allocation {
+	struct region_list list;
+	struct region_list oob_list;
+};
+
+struct region {
+	u32 block;
+	u32 len;
+	int bg;
+	struct region *next;
+	struct region *prev;
+};
+
+struct block_group_info {
+	u32 first_block;
+	int header_blocks;
+	int data_blocks_used;
+	int has_superblock;
+	u8 *bitmaps;
+	u8 *block_bitmap;
+	u8 *inode_bitmap;
+	u8 *inode_table;
+	u32 free_blocks;
+	u32 first_free_block;
+	u32 free_inodes;
+	u32 first_free_inode;
+	u16 used_dirs;
+};
+
+struct block_allocation *create_allocation()
+{
+	struct block_allocation *alloc = malloc(sizeof(struct block_allocation));
+	alloc->list.first = NULL;
+	alloc->list.last = NULL;
+	alloc->oob_list.first = NULL;
+	alloc->oob_list.last = NULL;
+	alloc->list.iter = NULL;
+	alloc->list.partial_iter = 0;
+	alloc->oob_list.iter = NULL;
+	alloc->oob_list.partial_iter = 0;
+	return alloc;
+}
+
+static void region_list_remove(struct region_list *list, struct region *reg)
+{
+	if (reg->prev)
+		reg->prev->next = reg->next;
+
+	if (reg->next)
+		reg->next->prev = reg->prev;
+
+	if (list->first == reg)
+		list->first = reg->next;
+
+	if (list->last == reg)
+		list->last = reg->prev;
+
+	reg->next = NULL;
+	reg->prev = NULL;
+}
+
+static void region_list_append(struct region_list *list, struct region *reg)
+{
+	if (list->first == NULL) {
+		list->first = reg;
+		list->last = reg;
+		list->iter = reg;
+		list->partial_iter = 0;
+		reg->prev = NULL;
+	} else {
+		list->last->next = reg;
+		reg->prev = list->last;
+		list->last = reg;
+	}
+	reg->next = NULL;
+}
+
+#if 0
+static void dump_starting_from(struct region *reg)
+{
+	for (; reg; reg = reg->next) {
+		printf("%p: Blocks %d-%d (%d)\n", reg,
+				reg->bg * info.blocks_per_group + reg->block,
+				reg->bg * info.blocks_per_group + reg->block + reg->len - 1,
+				reg->len);
+	}
+}
+
+static void dump_region_lists(struct block_allocation *alloc) {
+
+	printf("Main list:\n");
+	dump_starting_from(alloc->list.first);
+
+	printf("OOB list:\n");
+	dump_starting_from(alloc->oob_list.first);
+}
+#endif
+
+void append_region(struct block_allocation *alloc,
+		u32 block, u32 len, int bg_num)
+{
+	struct region *reg;
+	reg = malloc(sizeof(struct region));
+	reg->block = block;
+	reg->len = len;
+	reg->bg = bg_num;
+	reg->next = NULL;
+
+	region_list_append(&alloc->list, reg);
+}
+
+static void allocate_bg_inode_table(struct block_group_info *bg)
+{
+	if (bg->inode_table != NULL)
+		return;
+
+	u32 block = bg->first_block + 2;
+
+	if (bg->has_superblock)
+		block += aux_info.bg_desc_blocks + info.bg_desc_reserve_blocks + 1;
+
+	bg->inode_table = calloc(aux_info.inode_table_blocks, info.block_size);
+	if (bg->inode_table == NULL)
+		critical_error_errno("calloc");
+
+	sparse_file_add_data(info.sparse_file, bg->inode_table,
+			aux_info.inode_table_blocks	* info.block_size, block);
+}
+
+void init_unused_inode_tables(void)
+{
+	unsigned int i;
+	u32 block;
+	struct block_group_info *bg;
+
+	for (i = 0; i < aux_info.groups; i++) {
+		if (!aux_info.bgs[i].inode_table) {
+			bg = &aux_info.bgs[i];
+			block = bg->first_block + 2;
+			if (bg->has_superblock)
+				block += aux_info.bg_desc_blocks + info.bg_desc_reserve_blocks + 1;
+			sparse_file_add_fill(info.sparse_file, 0,
+					aux_info.inode_table_blocks * info.block_size, block);
+		}
+	}
+}
+
+static int bitmap_set_bit(u8 *bitmap, u32 bit)
+{
+	if (bitmap[bit / 8] & 1 << (bit % 8))
+		return 1;
+
+	bitmap[bit / 8] |= 1 << (bit % 8);
+	return 0;
+}
+
+static int bitmap_set_8_bits(u8 *bitmap, u32 bit)
+{
+	int ret = bitmap[bit / 8];
+	bitmap[bit / 8] = 0xFF;
+	return ret;
+}
+
+/* Marks a the first num_blocks blocks in a block group as used, and accounts
+ for them in the block group free block info. */
+static int reserve_blocks(struct block_group_info *bg, u32 start, u32 num)
+{
+	unsigned int i = 0;
+
+	u32 block = start;
+	if (num > bg->free_blocks)
+		return -1;
+
+	for (i = 0; i < num && block % 8 != 0; i++, block++) {
+		if (bitmap_set_bit(bg->block_bitmap, block)) {
+			error("attempted to reserve already reserved block");
+			return -1;
+		}
+	}
+
+	for (; i + 8 <= (num & ~7); i += 8, block += 8) {
+		if (bitmap_set_8_bits(bg->block_bitmap, block)) {
+			error("attempted to reserve already reserved block");
+			return -1;
+		}
+	}
+
+	for (; i < num; i++, block++) {
+		if (bitmap_set_bit(bg->block_bitmap, block)) {
+			error("attempted to reserve already reserved block");
+			return -1;
+		}
+	}
+
+	bg->free_blocks -= num;
+	if (start == bg->first_free_block)
+		bg->first_free_block = start + num;
+
+	return 0;
+}
+
+static void free_blocks(struct block_group_info *bg, u32 num_blocks)
+{
+	unsigned int i;
+	u32 block = bg->first_free_block - 1;
+	for (i = 0; i < num_blocks; i++, block--)
+		bg->block_bitmap[block / 8] &= ~(1 << (block % 8));
+	bg->free_blocks += num_blocks;
+	bg->first_free_block -= num_blocks;
+}
+
+/* Reduces an existing allocation by len blocks by return the last blocks
+   to the free pool in their block group. Assumes that the blocks being
+   returned were the last ones allocated out of the block group */
+void reduce_allocation(struct block_allocation *alloc, u32 len)
+{
+	while (len) {
+		struct region *last_reg = alloc->list.last;
+
+		if (last_reg->len > len) {
+			free_blocks(&aux_info.bgs[last_reg->bg], len);
+			last_reg->len -= len;
+			len = 0;
+		} else {
+			struct region *reg = alloc->list.last->prev;
+			free_blocks(&aux_info.bgs[last_reg->bg], last_reg->len);
+			len -= last_reg->len;
+			if (reg) {
+				reg->next = NULL;
+			} else {
+				alloc->list.first = NULL;
+				alloc->list.last = NULL;
+				alloc->list.iter = NULL;
+				alloc->list.partial_iter = 0;
+			}
+			free(last_reg);
+		}
+	}
+}
+
+static void init_bg(struct block_group_info *bg, unsigned int i)
+{
+	int header_blocks = 2 + aux_info.inode_table_blocks;
+
+	bg->has_superblock = ext4_bg_has_super_block(i);
+
+	if (bg->has_superblock)
+		header_blocks += 1 + aux_info.bg_desc_blocks + info.bg_desc_reserve_blocks;
+
+	bg->bitmaps = calloc(info.block_size, 2);
+	bg->block_bitmap = bg->bitmaps;
+	bg->inode_bitmap = bg->bitmaps + info.block_size;
+
+	bg->header_blocks = header_blocks;
+	bg->first_block = aux_info.first_data_block + i * info.blocks_per_group;
+
+	u32 block = bg->first_block;
+	if (bg->has_superblock)
+		block += 1 + aux_info.bg_desc_blocks +  info.bg_desc_reserve_blocks;
+	sparse_file_add_data(info.sparse_file, bg->bitmaps, 2 * info.block_size,
+			block);
+
+	bg->data_blocks_used = 0;
+	bg->free_blocks = info.blocks_per_group;
+	bg->first_free_block = 0;
+	bg->free_inodes = info.inodes_per_group;
+	bg->first_free_inode = 1;
+
+	if (reserve_blocks(bg, bg->first_free_block, bg->header_blocks) < 0)
+		error("failed to reserve %u blocks in block group %u\n", bg->header_blocks, i);
+
+	u32 overrun = bg->first_block + info.blocks_per_group - aux_info.len_blocks;
+	if (overrun > 0)
+		reserve_blocks(bg, info.blocks_per_group - overrun, overrun);
+}
+
+void block_allocator_init()
+{
+	unsigned int i;
+
+	aux_info.bgs = calloc(sizeof(struct block_group_info), aux_info.groups);
+	if (aux_info.bgs == NULL)
+		critical_error_errno("calloc");
+
+	for (i = 0; i < aux_info.groups; i++)
+		init_bg(&aux_info.bgs[i], i);
+}
+
+void block_allocator_free()
+{
+	unsigned int i;
+
+	for (i = 0; i < aux_info.groups; i++) {
+		free(aux_info.bgs[i].bitmaps);
+		free(aux_info.bgs[i].inode_table);
+	}
+	free(aux_info.bgs);
+}
+
+static u32 ext4_allocate_blocks_from_block_group(u32 len, int bg_num)
+{
+	if (get_free_blocks(bg_num) < len)
+		return EXT4_ALLOCATE_FAILED;
+
+	u32 block = aux_info.bgs[bg_num].first_free_block;
+	struct block_group_info *bg = &aux_info.bgs[bg_num];
+	if (reserve_blocks(bg, bg->first_free_block, len) < 0) {
+		error("failed to reserve %u blocks in block group %u\n", len, bg_num);
+		return EXT4_ALLOCATE_FAILED;
+	}
+
+	aux_info.bgs[bg_num].data_blocks_used += len;
+
+	return bg->first_block + block;
+}
+
+/* Allocate a single block and return its block number */
+u32 allocate_block()
+{
+	unsigned int i;
+	for (i = 0; i < aux_info.groups; i++) {
+		u32 block = ext4_allocate_blocks_from_block_group(1, i);
+
+		if (block != EXT4_ALLOCATE_FAILED)
+			return block;
+	}
+
+	return EXT4_ALLOCATE_FAILED;
+}
+
+static struct region *ext4_allocate_best_fit_partial(u32 len)
+{
+	unsigned int i;
+	unsigned int found_bg = 0;
+	u32 found_bg_len = 0;
+
+	for (i = 0; i < aux_info.groups; i++) {
+		u32 bg_len = aux_info.bgs[i].free_blocks;
+
+		if ((len <= bg_len && (found_bg_len == 0 || bg_len < found_bg_len)) ||
+		    (len > found_bg_len && bg_len > found_bg_len)) {
+			found_bg = i;
+			found_bg_len = bg_len;
+		}
+	}
+
+	if (found_bg_len) {
+		u32 allocate_len = min(len, found_bg_len);
+		struct region *reg;
+		u32 block = ext4_allocate_blocks_from_block_group(allocate_len, found_bg);
+		if (block == EXT4_ALLOCATE_FAILED) {
+			error("failed to allocate %d blocks in block group %d", allocate_len, found_bg);
+			return NULL;
+		}
+		reg = malloc(sizeof(struct region));
+		reg->block = block;
+		reg->len = allocate_len;
+		reg->next = NULL;
+		reg->prev = NULL;
+		reg->bg = found_bg;
+		return reg;
+	} else {
+		error("failed to allocate %u blocks, out of space?", len);
+	}
+	return NULL;
+}
+
+static struct region *ext4_allocate_best_fit(u32 len)
+{
+	struct region *first_reg = NULL;
+	struct region *prev_reg = NULL;
+	struct region *reg;
+
+	while (len > 0) {
+		reg = ext4_allocate_best_fit_partial(len);
+		if (reg == NULL)
+			return NULL;
+
+		if (first_reg == NULL)
+			first_reg = reg;
+
+		if (prev_reg) {
+			prev_reg->next = reg;
+			reg->prev = prev_reg;
+		}
+
+		prev_reg = reg;
+		len -= reg->len;
+	}
+
+	return first_reg;
+}
+
+/* Allocate len blocks.  The blocks may be spread across multiple block groups,
+   and are returned in a linked list of the blocks in each block group.  The
+   allocation algorithm is:
+       1.  If the remaining allocation is larger than any available
+           contiguous region, allocate the largest contiguous region and loop
+       2.  Otherwise, allocate the smallest contiguous region that it fits in
+*/
+struct block_allocation *allocate_blocks(u32 len)
+{
+	struct region *reg = ext4_allocate_best_fit(len);
+
+	if (reg == NULL)
+		return NULL;
+
+	struct block_allocation *alloc = create_allocation();
+	alloc->list.first = reg;
+	alloc->list.last = reg;
+	alloc->list.iter = alloc->list.first;
+	alloc->list.partial_iter = 0;
+	return alloc;
+}
+
+/* Returns the number of discontiguous regions used by an allocation */
+int block_allocation_num_regions(struct block_allocation *alloc)
+{
+	unsigned int i;
+	struct region *reg = alloc->list.first;
+
+	for (i = 0; reg != NULL; reg = reg->next)
+		i++;
+
+	return i;
+}
+
+int block_allocation_len(struct block_allocation *alloc)
+{
+	unsigned int i;
+	struct region *reg = alloc->list.first;
+
+	for (i = 0; reg != NULL; reg = reg->next)
+		i += reg->len;
+
+	return i;
+}
+
+/* Returns the block number of the block'th block in an allocation */
+u32 get_block(struct block_allocation *alloc, u32 block)
+{
+	struct region *reg = alloc->list.iter;
+	block += alloc->list.partial_iter;
+
+	for (; reg; reg = reg->next) {
+		if (block < reg->len)
+			return reg->block + block;
+		block -= reg->len;
+	}
+	return EXT4_ALLOCATE_FAILED;
+}
+
+u32 get_oob_block(struct block_allocation *alloc, u32 block)
+{
+	struct region *reg = alloc->oob_list.iter;
+	block += alloc->oob_list.partial_iter;
+
+	for (; reg; reg = reg->next) {
+		if (block < reg->len)
+			return reg->block + block;
+		block -= reg->len;
+	}
+	return EXT4_ALLOCATE_FAILED;
+}
+
+/* Gets the starting block and length in blocks of the first region
+   of an allocation */
+void get_region(struct block_allocation *alloc, u32 *block, u32 *len)
+{
+	*block = alloc->list.iter->block;
+	*len = alloc->list.iter->len - alloc->list.partial_iter;
+}
+
+/* Move to the next region in an allocation */
+void get_next_region(struct block_allocation *alloc)
+{
+	alloc->list.iter = alloc->list.iter->next;
+	alloc->list.partial_iter = 0;
+}
+
+/* Returns the number of free blocks in a block group */
+u32 get_free_blocks(u32 bg)
+{
+	return aux_info.bgs[bg].free_blocks;
+}
+
+int last_region(struct block_allocation *alloc)
+{
+	return (alloc->list.iter == NULL);
+}
+
+void rewind_alloc(struct block_allocation *alloc)
+{
+	alloc->list.iter = alloc->list.first;
+	alloc->list.partial_iter = 0;
+}
+
+static struct region *do_split_allocation(struct block_allocation *alloc, u32 len)
+{
+	struct region *reg = alloc->list.iter;
+	struct region *new;
+	struct region *tmp;
+
+	while (reg && len >= reg->len) {
+		len -= reg->len;
+		reg = reg->next;
+	}
+
+	if (reg == NULL && len > 0)
+		return NULL;
+
+	if (len > 0) {
+		new = malloc(sizeof(struct region));
+
+		new->bg = reg->bg;
+		new->block = reg->block + len;
+		new->len = reg->len - len;
+		new->next = reg->next;
+		new->prev = reg;
+
+		reg->next = new;
+		reg->len = len;
+
+		tmp = alloc->list.iter;
+		alloc->list.iter = new;
+		return tmp;
+	} else {
+		return reg;
+	}
+}
+
+/* Splits an allocation into two allocations.  The returned allocation will
+   point to the first half, and the original allocation ptr will point to the
+   second half. */
+static struct region *split_allocation(struct block_allocation *alloc, u32 len)
+{
+	/* First make sure there is a split at the current ptr */
+	do_split_allocation(alloc, alloc->list.partial_iter);
+
+	/* Then split off len blocks */
+	struct region *middle = do_split_allocation(alloc, len);
+	alloc->list.partial_iter = 0;
+	return middle;
+}
+
+/* Reserve the next blocks for oob data (indirect or extent blocks) */
+int reserve_oob_blocks(struct block_allocation *alloc, int blocks)
+{
+	struct region *oob = split_allocation(alloc, blocks);
+	struct region *next;
+
+	if (oob == NULL)
+		return -1;
+
+	while (oob && oob != alloc->list.iter) {
+		next = oob->next;
+		region_list_remove(&alloc->list, oob);
+		region_list_append(&alloc->oob_list, oob);
+		oob = next;
+	}
+
+	return 0;
+}
+
+static int advance_list_ptr(struct region_list *list, int blocks)
+{
+	struct region *reg = list->iter;
+
+	while (reg != NULL && blocks > 0) {
+		if (reg->len > list->partial_iter + blocks) {
+			list->partial_iter += blocks;
+			return 0;
+		}
+
+		blocks -= (reg->len - list->partial_iter);
+		list->partial_iter = 0;
+		reg = reg->next;
+	}
+
+	if (blocks > 0)
+		return -1;
+
+	return 0;
+}
+
+/* Move the allocation pointer forward */
+int advance_blocks(struct block_allocation *alloc, int blocks)
+{
+	return advance_list_ptr(&alloc->list, blocks);
+}
+
+int advance_oob_blocks(struct block_allocation *alloc, int blocks)
+{
+	return advance_list_ptr(&alloc->oob_list, blocks);
+}
+
+int append_oob_allocation(struct block_allocation *alloc, u32 len)
+{
+	struct region *reg = ext4_allocate_best_fit(len);
+
+	if (reg == NULL) {
+		error("failed to allocate %d blocks", len);
+		return -1;
+	}
+
+	for (; reg; reg = reg->next)
+		region_list_append(&alloc->oob_list, reg);
+
+	return 0;
+}
+
+/* Returns an ext4_inode structure for an inode number */
+struct ext4_inode *get_inode(u32 inode)
+{
+	inode -= 1;
+	int bg = inode / info.inodes_per_group;
+	inode %= info.inodes_per_group;
+
+	allocate_bg_inode_table(&aux_info.bgs[bg]);
+	return (struct ext4_inode *)(aux_info.bgs[bg].inode_table + inode *
+		info.inode_size);
+}
+
+/* Mark the first len inodes in a block group as used */
+u32 reserve_inodes(int bg, u32 num)
+{
+	unsigned int i;
+	u32 inode;
+
+	if (get_free_inodes(bg) < num)
+		return EXT4_ALLOCATE_FAILED;
+
+	for (i = 0; i < num; i++) {
+		inode = aux_info.bgs[bg].first_free_inode + i - 1;
+		aux_info.bgs[bg].inode_bitmap[inode / 8] |= 1 << (inode % 8);
+	}
+
+	inode = aux_info.bgs[bg].first_free_inode;
+
+	aux_info.bgs[bg].first_free_inode += num;
+	aux_info.bgs[bg].free_inodes -= num;
+
+	return inode;
+}
+
+/* Returns the first free inode number
+   TODO: Inodes should be allocated in the block group of the data? */
+u32 allocate_inode()
+{
+	unsigned int bg;
+	u32 inode;
+
+	for (bg = 0; bg < aux_info.groups; bg++) {
+		inode = reserve_inodes(bg, 1);
+		if (inode != EXT4_ALLOCATE_FAILED)
+			return bg * info.inodes_per_group + inode;
+	}
+
+	return EXT4_ALLOCATE_FAILED;
+}
+
+/* Returns the number of free inodes in a block group */
+u32 get_free_inodes(u32 bg)
+{
+	return aux_info.bgs[bg].free_inodes;
+}
+
+/* Increments the directory count of the block group that contains inode */
+void add_directory(u32 inode)
+{
+	int bg = (inode - 1) / info.inodes_per_group;
+	aux_info.bgs[bg].used_dirs += 1;
+}
+
+/* Returns the number of inodes in a block group that are directories */
+u16 get_directories(int bg)
+{
+	return aux_info.bgs[bg].used_dirs;
+}
+
+/* Frees the memory used by a linked list of allocation regions */
+void free_alloc(struct block_allocation *alloc)
+{
+	struct region *reg;
+
+	reg = alloc->list.first;
+	while (reg) {
+		struct region *next = reg->next;
+		free(reg);
+		reg = next;
+	}
+
+	reg = alloc->oob_list.first;
+	while (reg) {
+		struct region *next = reg->next;
+		free(reg);
+		reg = next;
+	}
+
+	free(alloc);
+}
diff --git a/extras/ext4_utils/allocate.h b/extras/ext4_utils/allocate.h
new file mode 100644
index 0000000..fb6e0f7
--- /dev/null
+++ b/extras/ext4_utils/allocate.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ALLOCATE_H_
+#define _ALLOCATE_H_
+
+#define EXT4_ALLOCATE_FAILED (u32)(~0)
+
+#include "ext4_utils.h"
+#include "ext4.h"
+
+struct block_allocation;
+
+void block_allocator_init();
+void block_allocator_free();
+u32 allocate_block();
+struct block_allocation *allocate_blocks(u32 len);
+int block_allocation_num_regions(struct block_allocation *alloc);
+int block_allocation_len(struct block_allocation *alloc);
+struct ext4_inode *get_inode(u32 inode);
+void reduce_allocation(struct block_allocation *alloc, u32 len);
+u32 get_block(struct block_allocation *alloc, u32 block);
+u32 get_oob_block(struct block_allocation *alloc, u32 block);
+void get_next_region(struct block_allocation *alloc);
+void get_region(struct block_allocation *alloc, u32 *block, u32 *len);
+u32 get_free_blocks(u32 bg);
+u32 get_free_inodes(u32 bg);
+u32 reserve_inodes(int bg, u32 inodes);
+void add_directory(u32 inode);
+u16 get_directories(int bg);
+void init_unused_inode_tables(void);
+u32 allocate_inode();
+void free_alloc(struct block_allocation *alloc);
+int reserve_oob_blocks(struct block_allocation *alloc, int blocks);
+int advance_blocks(struct block_allocation *alloc, int blocks);
+int advance_oob_blocks(struct block_allocation *alloc, int blocks);
+int last_region(struct block_allocation *alloc);
+void rewind_alloc(struct block_allocation *alloc);
+void append_region(struct block_allocation *alloc,
+	u32 block, u32 len, int bg);
+struct block_allocation *create_allocation();
+int append_oob_allocation(struct block_allocation *alloc, u32 len);
+
+#endif
diff --git a/extras/ext4_utils/contents.c b/extras/ext4_utils/contents.c
new file mode 100644
index 0000000..aeed31e
--- /dev/null
+++ b/extras/ext4_utils/contents.c
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sys/stat.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "ext4_utils.h"
+#include "ext4.h"
+#include "make_ext4fs.h"
+#include "allocate.h"
+#include "contents.h"
+#include "extent.h"
+#include "indirect.h"
+#include "xattr.h"
+
+#ifdef USE_MINGW
+#define S_IFLNK 0  /* used by make_link, not needed under mingw */
+#endif
+
+static u32 dentry_size(u32 entries, struct dentry *dentries)
+{
+	u32 len = 24;
+	unsigned int i;
+	unsigned int dentry_len;
+
+	for (i = 0; i < entries; i++) {
+		dentry_len = 8 + ALIGN(strlen(dentries[i].filename), 4);
+		if (len % info.block_size + dentry_len > info.block_size)
+			len += info.block_size - (len % info.block_size);
+		len += dentry_len;
+	}
+
+	return len;
+}
+
+static struct ext4_dir_entry_2 *add_dentry(u8 *data, u32 *offset,
+		struct ext4_dir_entry_2 *prev, u32 inode, const char *name,
+		u8 file_type)
+{
+	u8 name_len = strlen(name);
+	u16 rec_len = 8 + ALIGN(name_len, 4);
+	struct ext4_dir_entry_2 *dentry;
+
+	u32 start_block = *offset / info.block_size;
+	u32 end_block = (*offset + rec_len - 1) / info.block_size;
+	if (start_block != end_block) {
+		/* Adding this dentry will cross a block boundary, so pad the previous
+		   dentry to the block boundary */
+		if (!prev)
+			critical_error("no prev");
+		prev->rec_len += end_block * info.block_size - *offset;
+		*offset = end_block * info.block_size;
+	}
+
+	dentry = (struct ext4_dir_entry_2 *)(data + *offset);
+	dentry->inode = inode;
+	dentry->rec_len = rec_len;
+	dentry->name_len = name_len;
+	dentry->file_type = file_type;
+	memcpy(dentry->name, name, name_len);
+
+	*offset += rec_len;
+	return dentry;
+}
+
+/* Creates a directory structure for an array of directory entries, dentries,
+   and stores the location of the structure in an inode.  The new inode's
+   .. link is set to dir_inode_num.  Stores the location of the inode number
+   of each directory entry into dentries[i].inode, to be filled in later
+   when the inode for the entry is allocated.  Returns the inode number of the
+   new directory */
+u32 make_directory(u32 dir_inode_num, u32 entries, struct dentry *dentries,
+	u32 dirs)
+{
+	struct ext4_inode *inode;
+	u32 blocks;
+	u32 len;
+	u32 offset = 0;
+	u32 inode_num;
+	u8 *data;
+	unsigned int i;
+	struct ext4_dir_entry_2 *dentry;
+
+	blocks = DIV_ROUND_UP(dentry_size(entries, dentries), info.block_size);
+	len = blocks * info.block_size;
+
+	if (dir_inode_num) {
+		inode_num = allocate_inode(info);
+	} else {
+		dir_inode_num = EXT4_ROOT_INO;
+		inode_num = EXT4_ROOT_INO;
+	}
+
+	if (inode_num == EXT4_ALLOCATE_FAILED) {
+		error("failed to allocate inode\n");
+		return EXT4_ALLOCATE_FAILED;
+	}
+
+	add_directory(inode_num);
+
+	inode = get_inode(inode_num);
+	if (inode == NULL) {
+		error("failed to get inode %u", inode_num);
+		return EXT4_ALLOCATE_FAILED;
+	}
+
+	data = inode_allocate_data_extents(inode, len, len);
+	if (data == NULL) {
+		error("failed to allocate %u extents", len);
+		return EXT4_ALLOCATE_FAILED;
+	}
+
+	inode->i_mode = S_IFDIR;
+	inode->i_links_count = dirs + 2;
+	inode->i_flags |= aux_info.default_i_flags;
+
+	dentry = NULL;
+
+	dentry = add_dentry(data, &offset, NULL, inode_num, ".", EXT4_FT_DIR);
+	if (!dentry) {
+		error("failed to add . directory");
+		return EXT4_ALLOCATE_FAILED;
+	}
+
+	dentry = add_dentry(data, &offset, dentry, dir_inode_num, "..", EXT4_FT_DIR);
+	if (!dentry) {
+		error("failed to add .. directory");
+		return EXT4_ALLOCATE_FAILED;
+	}
+
+	for (i = 0; i < entries; i++) {
+		dentry = add_dentry(data, &offset, dentry, 0,
+				dentries[i].filename, dentries[i].file_type);
+		if (offset > len || (offset == len && i != entries - 1))
+			critical_error("internal error: dentry for %s ends at %d, past %d\n",
+				dentries[i].filename, offset, len);
+		dentries[i].inode = &dentry->inode;
+		if (!dentry) {
+			error("failed to add directory");
+			return EXT4_ALLOCATE_FAILED;
+		}
+	}
+
+	/* pad the last dentry out to the end of the block */
+	dentry->rec_len += len - offset;
+
+	return inode_num;
+}
+
+/* Creates a file on disk.  Returns the inode number of the new file */
+u32 make_file(const char *filename, u64 len)
+{
+	struct ext4_inode *inode;
+	u32 inode_num;
+
+	inode_num = allocate_inode(info);
+	if (inode_num == EXT4_ALLOCATE_FAILED) {
+		error("failed to allocate inode\n");
+		return EXT4_ALLOCATE_FAILED;
+	}
+
+	inode = get_inode(inode_num);
+	if (inode == NULL) {
+		error("failed to get inode %u", inode_num);
+		return EXT4_ALLOCATE_FAILED;
+	}
+
+	if (len > 0)
+		inode_allocate_file_extents(inode, len, filename);
+
+	inode->i_mode = S_IFREG;
+	inode->i_links_count = 1;
+	inode->i_flags |= aux_info.default_i_flags;
+
+	return inode_num;
+}
+
+/* Creates a file on disk.  Returns the inode number of the new file */
+u32 make_link(const char *filename, const char *link)
+{
+	struct ext4_inode *inode;
+	u32 inode_num;
+	u32 len = strlen(link);
+
+	inode_num = allocate_inode(info);
+	if (inode_num == EXT4_ALLOCATE_FAILED) {
+		error("failed to allocate inode\n");
+		return EXT4_ALLOCATE_FAILED;
+	}
+
+	inode = get_inode(inode_num);
+	if (inode == NULL) {
+		error("failed to get inode %u", inode_num);
+		return EXT4_ALLOCATE_FAILED;
+	}
+
+	inode->i_mode = S_IFLNK;
+	inode->i_links_count = 1;
+	inode->i_flags |= aux_info.default_i_flags;
+	inode->i_size_lo = len;
+
+	if (len + 1 <= sizeof(inode->i_block)) {
+		/* Fast symlink */
+		memcpy((char*)inode->i_block, link, len);
+	} else {
+		u8 *data = inode_allocate_data_indirect(inode, info.block_size, info.block_size);
+		memcpy(data, link, len);
+		inode->i_blocks_lo = info.block_size / 512;
+	}
+
+	return inode_num;
+}
+
+int inode_set_permissions(u32 inode_num, u16 mode, u16 uid, u16 gid, u32 mtime)
+{
+	struct ext4_inode *inode = get_inode(inode_num);
+
+	if (!inode)
+		return -1;
+
+	inode->i_mode |= mode;
+	inode->i_uid = uid;
+	inode->i_gid = gid;
+	inode->i_mtime = mtime;
+	inode->i_atime = mtime;
+	inode->i_ctime = mtime;
+
+	return 0;
+}
+
+#ifdef HAVE_SELINUX
+#define XATTR_SELINUX_SUFFIX "selinux"
+
+/* XXX */
+#define cpu_to_le32(x) (x)
+#define cpu_to_le16(x) (x)
+
+int inode_set_selinux(u32 inode_num, const char *secon)
+{
+	struct ext4_inode *inode = get_inode(inode_num);
+	u32 *hdr;
+	struct ext4_xattr_entry *entry;
+	size_t name_len = strlen(XATTR_SELINUX_SUFFIX);
+	size_t value_len;
+	size_t size, min_offs;
+	char *val;
+
+	if (!secon)
+		return 0;
+
+	if (!inode)
+		return -1;
+
+	hdr = (u32 *) (inode + 1);
+	*hdr = cpu_to_le32(EXT4_XATTR_MAGIC);
+	entry = (struct ext4_xattr_entry *) (hdr+1);
+	memset(entry, 0, EXT4_XATTR_LEN(name_len));
+	entry->e_name_index = EXT4_XATTR_INDEX_SECURITY;
+	entry->e_name_len = name_len;
+	memcpy(entry->e_name, XATTR_SELINUX_SUFFIX, name_len);
+	value_len = strlen(secon)+1;
+	entry->e_value_size = cpu_to_le32(value_len);
+	min_offs = (char *)inode + info.inode_size - (char*) entry;
+	size = EXT4_XATTR_SIZE(value_len);
+	val = (char *)entry + min_offs - size;
+	entry->e_value_offs = cpu_to_le16(min_offs - size);
+	memset(val + size - EXT4_XATTR_PAD, 0, EXT4_XATTR_PAD);
+	memcpy(val, secon, value_len);
+	inode->i_extra_isize = cpu_to_le16(sizeof(struct ext4_inode) - EXT4_GOOD_OLD_INODE_SIZE);
+
+	return 0;
+}
+#else
+int inode_set_selinux(u32 inode_num, const char *secon)
+{
+	return 0;
+}
+#endif
diff --git a/extras/ext4_utils/contents.h b/extras/ext4_utils/contents.h
new file mode 100644
index 0000000..35867fd
--- /dev/null
+++ b/extras/ext4_utils/contents.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _DIRECTORY_H_
+#define _DIRECTORY_H_
+
+struct dentry {
+	char *path;
+	char *full_path;
+	const char *filename;
+	char *link;
+	unsigned long size;
+	u8 file_type;
+	u16 mode;
+	u16 uid;
+	u16 gid;
+	u32 *inode;
+	u32 mtime;
+	char *secon;
+};
+
+u32 make_directory(u32 dir_inode_num, u32 entries, struct dentry *dentries,
+	u32 dirs);
+u32 make_file(const char *filename, u64 len);
+u32 make_link(const char *filename, const char *link);
+int inode_set_permissions(u32 inode_num, u16 mode, u16 uid, u16 gid, u32 mtime);
+int inode_set_selinux(u32 inode_num, const char *secon);
+#endif
diff --git a/extras/ext4_utils/ext2simg.c b/extras/ext4_utils/ext2simg.c
new file mode 100644
index 0000000..f4b6c93
--- /dev/null
+++ b/extras/ext4_utils/ext2simg.c
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define _FILE_OFFSET_BITS 64
+#define _LARGEFILE64_SOURCE 1
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <unistd.h>
+
+#include <sparse/sparse.h>
+
+#include "ext4_utils.h"
+#include "make_ext4fs.h"
+#include "allocate.h"
+
+#if defined(__APPLE__) && defined(__MACH__)
+#define off64_t off_t
+#endif
+
+#ifndef USE_MINGW /* O_BINARY is windows-specific flag */
+#define O_BINARY 0
+#endif
+
+extern struct fs_info info;
+
+static int verbose = 0;
+
+static void usage(char *path)
+{
+	fprintf(stderr, "%s [ options ] <image or block device> <output image>\n", path);
+	fprintf(stderr, "\n");
+	fprintf(stderr, "  -c include CRC block\n");
+	fprintf(stderr, "  -v verbose output\n");
+	fprintf(stderr, "  -z gzip output\n");
+	fprintf(stderr, "  -S don't use sparse output format\n");
+}
+
+static int read_ext(int fd)
+{
+	off64_t ret;
+	struct ext4_super_block sb;
+	unsigned int i;
+
+	ret = lseek64(fd, 1024, SEEK_SET);
+	if (ret < 0)
+		critical_error_errno("failed to seek to superblock");
+
+	ret = read(fd, &sb, sizeof(sb));
+	if (ret < 0)
+		critical_error_errno("failed to read superblock");
+	if (ret != sizeof(sb))
+		critical_error("failed to read all of superblock");
+
+	ext4_parse_sb(&sb);
+
+	ret = lseek64(fd, info.len, SEEK_SET);
+	if (ret < 0)
+		critical_error_errno("failed to seek to end of input image");
+
+	ret = lseek64(fd, info.block_size * (aux_info.first_data_block + 1), SEEK_SET);
+	if (ret < 0)
+		critical_error_errno("failed to seek to block group descriptors");
+
+	ret = read(fd, aux_info.bg_desc, info.block_size * aux_info.bg_desc_blocks);
+	if (ret < 0)
+		critical_error_errno("failed to read block group descriptors");
+	if (ret != (int)info.block_size * (int)aux_info.bg_desc_blocks)
+		critical_error("failed to read all of block group descriptors");
+
+	if (verbose) {
+		printf("Found filesystem with parameters:\n");
+		printf("    Size: %llu\n", info.len);
+		printf("    Block size: %d\n", info.block_size);
+		printf("    Blocks per group: %d\n", info.blocks_per_group);
+		printf("    Inodes per group: %d\n", info.inodes_per_group);
+		printf("    Inode size: %d\n", info.inode_size);
+		printf("    Label: %s\n", info.label);
+		printf("    Blocks: %llu\n", aux_info.len_blocks);
+		printf("    Block groups: %d\n", aux_info.groups);
+		printf("    Reserved block group size: %d\n", info.bg_desc_reserve_blocks);
+		printf("    Used %d/%d inodes and %d/%d blocks\n",
+				aux_info.sb->s_inodes_count - aux_info.sb->s_free_inodes_count,
+				aux_info.sb->s_inodes_count,
+				aux_info.sb->s_blocks_count_lo - aux_info.sb->s_free_blocks_count_lo,
+				aux_info.sb->s_blocks_count_lo);
+	}
+
+	return 0;
+}
+
+static int bitmap_get_bit(u8 *bitmap, u32 bit)
+{
+	if (bitmap[bit / 8] & 1 << (bit % 8))
+		return 1;
+
+	return 0;
+}
+
+static int build_sparse_ext(int fd, const char *filename)
+{
+	unsigned int i;
+	unsigned int block;
+	int start_contiguous_block;
+	u8 *block_bitmap;
+	off64_t ret;
+
+	block_bitmap = malloc(info.block_size);
+	if (!block_bitmap)
+		critical_error("failed to allocate block bitmap");
+
+	if (aux_info.first_data_block > 0)
+		sparse_file_add_file(info.sparse_file, filename, 0,
+				info.block_size * aux_info.first_data_block, 0);
+
+	for (i = 0; i < aux_info.groups; i++) {
+		u32 first_block = aux_info.first_data_block + i * info.blocks_per_group;
+		u32 last_block = min(info.blocks_per_group, aux_info.len_blocks - first_block);
+
+		ret = lseek64(fd, (u64)info.block_size * aux_info.bg_desc[i].bg_block_bitmap,
+				SEEK_SET);
+		if (ret < 0)
+			critical_error_errno("failed to seek to block group bitmap %d", i);
+
+		ret = read(fd, block_bitmap, info.block_size);
+		if (ret < 0)
+			critical_error_errno("failed to read block group bitmap %d", i);
+		if (ret != (int)info.block_size)
+			critical_error("failed to read all of block group bitmap %d", i);
+
+		start_contiguous_block = -1;
+		for (block = 0; block < last_block; block++) {
+			if (start_contiguous_block >= 0) {
+				if (!bitmap_get_bit(block_bitmap, block)) {
+					u32 start_block = first_block + start_contiguous_block;
+					u32 len_blocks = block - start_contiguous_block;
+
+					sparse_file_add_file(info.sparse_file, filename,
+							(u64)info.block_size * start_block,
+							info.block_size * len_blocks, start_block);
+					start_contiguous_block = -1;
+				}
+			} else {
+				if (bitmap_get_bit(block_bitmap, block))
+					start_contiguous_block = block;
+			}
+		}
+
+		if (start_contiguous_block >= 0) {
+			u32 start_block = first_block + start_contiguous_block;
+			u32 len_blocks = last_block - start_contiguous_block;
+			sparse_file_add_file(info.sparse_file, filename,
+					(u64)info.block_size * start_block,
+					info.block_size * len_blocks, start_block);
+		}
+	}
+
+	return 0;
+}
+
+int main(int argc, char **argv)
+{
+	int opt;
+	const char *in = NULL;
+	const char *out = NULL;
+	int gzip = 0;
+	int sparse = 1;
+	int infd, outfd;
+	int crc = 0;
+
+	while ((opt = getopt(argc, argv, "cvzS")) != -1) {
+		switch (opt) {
+		case 'c':
+			crc = 1;
+			break;
+		case 'v':
+			verbose = 1;
+			break;
+		case 'z':
+			gzip = 1;
+			break;
+		case 'S':
+			sparse = 0;
+			break;
+		}
+	}
+
+	if (optind >= argc) {
+		fprintf(stderr, "Expected image or block device after options\n");
+		usage(argv[0]);
+		exit(EXIT_FAILURE);
+	}
+
+	in = argv[optind++];
+
+	if (optind >= argc) {
+		fprintf(stderr, "Expected output image after input image\n");
+		usage(argv[0]);
+		exit(EXIT_FAILURE);
+	}
+
+	out = argv[optind++];
+
+	if (optind < argc) {
+		fprintf(stderr, "Unexpected argument: %s\n", argv[optind]);
+		usage(argv[0]);
+		exit(EXIT_FAILURE);
+	}
+
+	infd = open(in, O_RDONLY);
+
+	if (infd < 0)
+		critical_error_errno("failed to open input image");
+
+	read_ext(infd);
+
+	info.sparse_file = sparse_file_new(info.block_size, info.len);
+
+	build_sparse_ext(infd, in);
+
+	close(infd);
+
+	if (strcmp(out, "-")) {
+		outfd = open(out, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
+		if (outfd < 0) {
+			error_errno("open");
+			return EXIT_FAILURE;
+		}
+	} else {
+		outfd = STDOUT_FILENO;
+	}
+
+	write_ext4_image(outfd, gzip, sparse, crc);
+	close(outfd);
+
+	sparse_file_destroy(info.sparse_file);
+
+	return 0;
+}
diff --git a/extras/ext4_utils/ext4.h b/extras/ext4_utils/ext4.h
new file mode 100644
index 0000000..68b5233
--- /dev/null
+++ b/extras/ext4_utils/ext4.h
@@ -0,0 +1,577 @@
+/****************************************************************************
+ ****************************************************************************
+ ***
+ ***   This header was automatically generated from a Linux kernel header
+ ***   of the same name, to make information necessary for userspace to
+ ***   call into the kernel available to libc.  It contains only constants,
+ ***   structures, and macros generated from the original header, and thus,
+ ***   contains no copyrightable information.
+ ***
+ ****************************************************************************
+ ****************************************************************************/
+#ifndef _EXT4_H
+#define _EXT4_H
+
+#include <sys/types.h>
+
+#undef EXT4FS_DEBUG
+
+#ifdef EXT4FS_DEBUG
+#define ext4_debug(f, a...)   do {   printk(KERN_DEBUG "EXT4-fs DEBUG (%s, %d): %s:",   __FILE__, __LINE__, __func__);   printk(KERN_DEBUG f, ## a);   } while (0)
+#else
+#define ext4_debug(f, a...) do {} while (0)
+#endif
+
+#define EXT4_ERROR_INODE(inode, fmt, a...)   ext4_error_inode(__func__, (inode), (fmt), ## a);
+
+#define EXT4_ERROR_FILE(file, fmt, a...)   ext4_error_file(__func__, (file), (fmt), ## a);
+
+typedef int ext4_grpblk_t;
+
+typedef unsigned long long ext4_fsblk_t;
+
+typedef __u32 ext4_lblk_t;
+
+typedef unsigned int ext4_group_t;
+
+#define EXT4_MB_HINT_MERGE 0x0001
+
+#define EXT4_MB_HINT_RESERVED 0x0002
+
+#define EXT4_MB_HINT_METADATA 0x0004
+
+#define EXT4_MB_HINT_FIRST 0x0008
+
+#define EXT4_MB_HINT_BEST 0x0010
+
+#define EXT4_MB_HINT_DATA 0x0020
+
+#define EXT4_MB_HINT_NOPREALLOC 0x0040
+
+#define EXT4_MB_HINT_GROUP_ALLOC 0x0080
+
+#define EXT4_MB_HINT_GOAL_ONLY 0x0100
+
+#define EXT4_MB_HINT_TRY_GOAL 0x0200
+
+#define EXT4_MB_DELALLOC_RESERVED 0x0400
+
+#define EXT4_MB_STREAM_ALLOC 0x0800
+
+struct ext4_allocation_request {
+
+ struct inode *inode;
+
+ unsigned int len;
+
+ ext4_lblk_t logical;
+
+ ext4_lblk_t lleft;
+
+ ext4_lblk_t lright;
+
+ ext4_fsblk_t goal;
+
+ ext4_fsblk_t pleft;
+
+ ext4_fsblk_t pright;
+
+ unsigned int flags;
+};
+
+#define EXT4_BAD_INO 1  
+#define EXT4_ROOT_INO 2  
+#define EXT4_BOOT_LOADER_INO 5  
+#define EXT4_UNDEL_DIR_INO 6  
+#define EXT4_RESIZE_INO 7  
+#define EXT4_JOURNAL_INO 8  
+
+#define EXT4_GOOD_OLD_FIRST_INO 11
+
+#define EXT4_LINK_MAX 65000
+
+#define EXT4_MIN_BLOCK_SIZE 1024
+#define EXT4_MAX_BLOCK_SIZE 65536
+#define EXT4_MIN_BLOCK_LOG_SIZE 10
+#define EXT4_BLOCK_SIZE(s) (EXT4_MIN_BLOCK_SIZE << (s)->s_log_block_size)
+#define EXT4_ADDR_PER_BLOCK(s) (EXT4_BLOCK_SIZE(s) / sizeof(__u32))
+#define EXT4_BLOCK_SIZE_BITS(s) ((s)->s_log_block_size + 10)
+#define EXT4_INODE_SIZE(s) (((s)->s_rev_level == EXT4_GOOD_OLD_REV) ?   EXT4_GOOD_OLD_INODE_SIZE :   (s)->s_inode_size)
+#define EXT4_FIRST_INO(s) (((s)->s_rev_level == EXT4_GOOD_OLD_REV) ?   EXT4_GOOD_OLD_FIRST_INO :   (s)->s_first_ino)
+#define EXT4_BLOCK_ALIGN(size, blkbits) ALIGN((size), (1 << (blkbits)))
+
+struct ext4_group_desc
+{
+ __le32 bg_block_bitmap_lo;
+ __le32 bg_inode_bitmap_lo;
+ __le32 bg_inode_table_lo;
+ __le16 bg_free_blocks_count_lo;
+ __le16 bg_free_inodes_count_lo;
+ __le16 bg_used_dirs_count_lo;
+ __le16 bg_flags;
+ __u32 bg_reserved[2];
+ __le16 bg_itable_unused_lo;
+ __le16 bg_checksum;
+ __le32 bg_block_bitmap_hi;
+ __le32 bg_inode_bitmap_hi;
+ __le32 bg_inode_table_hi;
+ __le16 bg_free_blocks_count_hi;
+ __le16 bg_free_inodes_count_hi;
+ __le16 bg_used_dirs_count_hi;
+ __le16 bg_itable_unused_hi;
+ __u32 bg_reserved2[3];
+};
+
+#define EXT4_BG_INODE_UNINIT 0x0001  
+#define EXT4_BG_BLOCK_UNINIT 0x0002  
+#define EXT4_BG_INODE_ZEROED 0x0004  
+
+#define EXT4_MIN_DESC_SIZE 32
+#define EXT4_MIN_DESC_SIZE_64BIT 64
+#define EXT4_MAX_DESC_SIZE EXT4_MIN_BLOCK_SIZE
+#define EXT4_DESC_SIZE(s) (EXT4_SB(s)->s_desc_size)
+#define EXT4_BLOCKS_PER_GROUP(s) ((s)->s_blocks_per_group)
+#define EXT4_DESC_PER_BLOCK(s) (EXT4_BLOCK_SIZE(s) / EXT4_DESC_SIZE(s))
+#define EXT4_INODES_PER_GROUP(s) ((s)->s_inodes_per_group)
+
+#define EXT4_NDIR_BLOCKS 12
+#define EXT4_IND_BLOCK EXT4_NDIR_BLOCKS
+#define EXT4_DIND_BLOCK (EXT4_IND_BLOCK + 1)
+#define EXT4_TIND_BLOCK (EXT4_DIND_BLOCK + 1)
+#define EXT4_N_BLOCKS (EXT4_TIND_BLOCK + 1)
+
+#define EXT4_SECRM_FL 0x00000001  
+#define EXT4_UNRM_FL 0x00000002  
+#define EXT4_COMPR_FL 0x00000004  
+#define EXT4_SYNC_FL 0x00000008  
+#define EXT4_IMMUTABLE_FL 0x00000010  
+#define EXT4_APPEND_FL 0x00000020  
+#define EXT4_NODUMP_FL 0x00000040  
+#define EXT4_NOATIME_FL 0x00000080  
+
+#define EXT4_DIRTY_FL 0x00000100
+#define EXT4_COMPRBLK_FL 0x00000200  
+#define EXT4_NOCOMPR_FL 0x00000400  
+#define EXT4_ECOMPR_FL 0x00000800  
+
+#define EXT4_INDEX_FL 0x00001000  
+#define EXT4_IMAGIC_FL 0x00002000  
+#define EXT4_JOURNAL_DATA_FL 0x00004000  
+#define EXT4_NOTAIL_FL 0x00008000  
+#define EXT4_DIRSYNC_FL 0x00010000  
+#define EXT4_TOPDIR_FL 0x00020000  
+#define EXT4_HUGE_FILE_FL 0x00040000  
+#define EXT4_EXTENTS_FL 0x00080000  
+#define EXT4_EA_INODE_FL 0x00200000  
+#define EXT4_EOFBLOCKS_FL 0x00400000  
+#define EXT4_RESERVED_FL 0x80000000  
+
+#define EXT4_FL_USER_VISIBLE 0x004BDFFF  
+#define EXT4_FL_USER_MODIFIABLE 0x004B80FF  
+
+#define EXT4_FL_INHERITED (EXT4_SECRM_FL | EXT4_UNRM_FL | EXT4_COMPR_FL |  EXT4_SYNC_FL | EXT4_IMMUTABLE_FL | EXT4_APPEND_FL |  EXT4_NODUMP_FL | EXT4_NOATIME_FL |  EXT4_NOCOMPR_FL | EXT4_JOURNAL_DATA_FL |  EXT4_NOTAIL_FL | EXT4_DIRSYNC_FL)
+
+#define EXT4_REG_FLMASK (~(EXT4_DIRSYNC_FL | EXT4_TOPDIR_FL))
+
+#define EXT4_OTHER_FLMASK (EXT4_NODUMP_FL | EXT4_NOATIME_FL)
+
+struct ext4_new_group_data {
+ __u32 group;
+ __u64 block_bitmap;
+ __u64 inode_bitmap;
+ __u64 inode_table;
+ __u32 blocks_count;
+ __u16 reserved_blocks;
+ __u16 unused;
+ __u32 free_blocks_count;
+};
+
+#define EXT4_GET_BLOCKS_CREATE 0x0001
+
+#define EXT4_GET_BLOCKS_UNINIT_EXT 0x0002
+#define EXT4_GET_BLOCKS_CREATE_UNINIT_EXT (EXT4_GET_BLOCKS_UNINIT_EXT|  EXT4_GET_BLOCKS_CREATE)
+
+#define EXT4_GET_BLOCKS_DELALLOC_RESERVE 0x0004
+
+#define EXT4_GET_BLOCKS_PRE_IO 0x0008
+#define EXT4_GET_BLOCKS_CONVERT 0x0010
+#define EXT4_GET_BLOCKS_IO_CREATE_EXT (EXT4_GET_BLOCKS_PRE_IO|  EXT4_GET_BLOCKS_CREATE_UNINIT_EXT)
+
+#define EXT4_GET_BLOCKS_IO_CONVERT_EXT (EXT4_GET_BLOCKS_CONVERT|  EXT4_GET_BLOCKS_CREATE_UNINIT_EXT)
+
+#define EXT4_FREE_BLOCKS_METADATA 0x0001
+#define EXT4_FREE_BLOCKS_FORGET 0x0002
+#define EXT4_FREE_BLOCKS_VALIDATED 0x0004
+
+#define EXT4_IOC_GETFLAGS FS_IOC_GETFLAGS
+#define EXT4_IOC_SETFLAGS FS_IOC_SETFLAGS
+#define EXT4_IOC_GETVERSION _IOR('f', 3, long)
+#define EXT4_IOC_SETVERSION _IOW('f', 4, long)
+#define EXT4_IOC_GETVERSION_OLD FS_IOC_GETVERSION
+#define EXT4_IOC_SETVERSION_OLD FS_IOC_SETVERSION
+#define EXT4_IOC_GETRSVSZ _IOR('f', 5, long)
+#define EXT4_IOC_SETRSVSZ _IOW('f', 6, long)
+#define EXT4_IOC_GROUP_EXTEND _IOW('f', 7, unsigned long)
+#define EXT4_IOC_GROUP_ADD _IOW('f', 8, struct ext4_new_group_input)
+#define EXT4_IOC_MIGRATE _IO('f', 9)
+
+#define EXT4_IOC_ALLOC_DA_BLKS _IO('f', 12)
+#define EXT4_IOC_MOVE_EXT _IOWR('f', 15, struct move_extent)
+
+#define EXT4_IOC32_GETFLAGS FS_IOC32_GETFLAGS
+#define EXT4_IOC32_SETFLAGS FS_IOC32_SETFLAGS
+#define EXT4_IOC32_GETVERSION _IOR('f', 3, int)
+#define EXT4_IOC32_SETVERSION _IOW('f', 4, int)
+#define EXT4_IOC32_GETRSVSZ _IOR('f', 5, int)
+#define EXT4_IOC32_SETRSVSZ _IOW('f', 6, int)
+#define EXT4_IOC32_GROUP_EXTEND _IOW('f', 7, unsigned int)
+#define EXT4_IOC32_GETVERSION_OLD FS_IOC32_GETVERSION
+#define EXT4_IOC32_SETVERSION_OLD FS_IOC32_SETVERSION
+
+#define EXT4_MAX_BLOCK_FILE_PHYS 0xFFFFFFFF
+
+struct ext4_inode {
+ __le16 i_mode;
+ __le16 i_uid;
+ __le32 i_size_lo;
+ __le32 i_atime;
+ __le32 i_ctime;
+ __le32 i_mtime;
+ __le32 i_dtime;
+ __le16 i_gid;
+ __le16 i_links_count;
+ __le32 i_blocks_lo;
+ __le32 i_flags;
+ union {
+ struct {
+ __le32 l_i_version;
+ } linux1;
+ struct {
+ __u32 h_i_translator;
+ } hurd1;
+ struct {
+ __u32 m_i_reserved1;
+ } masix1;
+ } osd1;
+ __le32 i_block[EXT4_N_BLOCKS];
+ __le32 i_generation;
+ __le32 i_file_acl_lo;
+ __le32 i_size_high;
+ __le32 i_obso_faddr;
+ union {
+ struct {
+ __le16 l_i_blocks_high;
+ __le16 l_i_file_acl_high;
+ __le16 l_i_uid_high;
+ __le16 l_i_gid_high;
+ __u32 l_i_reserved2;
+ } linux2;
+ struct {
+ __le16 h_i_reserved1;
+ __u16 h_i_mode_high;
+ __u16 h_i_uid_high;
+ __u16 h_i_gid_high;
+ __u32 h_i_author;
+ } hurd2;
+ struct {
+ __le16 h_i_reserved1;
+ __le16 m_i_file_acl_high;
+ __u32 m_i_reserved2[2];
+ } masix2;
+ } osd2;
+ __le16 i_extra_isize;
+ __le16 i_pad1;
+ __le32 i_ctime_extra;
+ __le32 i_mtime_extra;
+ __le32 i_atime_extra;
+ __le32 i_crtime;
+ __le32 i_crtime_extra;
+ __le32 i_version_hi;
+};
+
+struct move_extent {
+ __u32 reserved;
+ __u32 donor_fd;
+ __u64 orig_start;
+ __u64 donor_start;
+ __u64 len;
+ __u64 moved_len;
+};
+
+#define EXT4_EPOCH_BITS 2
+#define EXT4_EPOCH_MASK ((1 << EXT4_EPOCH_BITS) - 1)
+#define EXT4_NSEC_MASK (~0UL << EXT4_EPOCH_BITS)
+
+#define EXT4_FITS_IN_INODE(ext4_inode, einode, field)   ((offsetof(typeof(*ext4_inode), field) +   sizeof((ext4_inode)->field))   <= (EXT4_GOOD_OLD_INODE_SIZE +   (einode)->i_extra_isize))  
+#define EXT4_INODE_SET_XTIME(xtime, inode, raw_inode)  do {   (raw_inode)->xtime = cpu_to_le32((inode)->xtime.tv_sec);   if (EXT4_FITS_IN_INODE(raw_inode, EXT4_I(inode), xtime ## _extra))   (raw_inode)->xtime ## _extra =   ext4_encode_extra_time(&(inode)->xtime);  } while (0)
+#define EXT4_EINODE_SET_XTIME(xtime, einode, raw_inode)  do {   if (EXT4_FITS_IN_INODE(raw_inode, einode, xtime))   (raw_inode)->xtime = cpu_to_le32((einode)->xtime.tv_sec);   if (EXT4_FITS_IN_INODE(raw_inode, einode, xtime ## _extra))   (raw_inode)->xtime ## _extra =   ext4_encode_extra_time(&(einode)->xtime);  } while (0)
+#define EXT4_INODE_GET_XTIME(xtime, inode, raw_inode)  do {   (inode)->xtime.tv_sec = (signed)le32_to_cpu((raw_inode)->xtime);   if (EXT4_FITS_IN_INODE(raw_inode, EXT4_I(inode), xtime ## _extra))   ext4_decode_extra_time(&(inode)->xtime,   raw_inode->xtime ## _extra);  } while (0)
+#define EXT4_EINODE_GET_XTIME(xtime, einode, raw_inode)  do {   if (EXT4_FITS_IN_INODE(raw_inode, einode, xtime))   (einode)->xtime.tv_sec =   (signed)le32_to_cpu((raw_inode)->xtime);   if (EXT4_FITS_IN_INODE(raw_inode, einode, xtime ## _extra))   ext4_decode_extra_time(&(einode)->xtime,   raw_inode->xtime ## _extra);  } while (0)
+#define i_disk_version osd1.linux1.l_i_version
+
+#define i_reserved1 osd1.linux1.l_i_reserved1
+#define i_file_acl_high osd2.linux2.l_i_file_acl_high
+#define i_blocks_high osd2.linux2.l_i_blocks_high
+#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
+
+#define EXT4_VALID_FS 0x0001  
+#define EXT4_ERROR_FS 0x0002  
+#define EXT4_ORPHAN_FS 0x0004  
+
+#define EXT2_FLAGS_SIGNED_HASH 0x0001  
+#define EXT2_FLAGS_UNSIGNED_HASH 0x0002  
+#define EXT2_FLAGS_TEST_FILESYS 0x0004  
+
+#define EXT4_MOUNT_OLDALLOC 0x00002  
+#define EXT4_MOUNT_GRPID 0x00004  
+#define EXT4_MOUNT_DEBUG 0x00008  
+#define EXT4_MOUNT_ERRORS_CONT 0x00010  
+#define EXT4_MOUNT_ERRORS_RO 0x00020  
+#define EXT4_MOUNT_ERRORS_PANIC 0x00040  
+#define EXT4_MOUNT_MINIX_DF 0x00080  
+#define EXT4_MOUNT_NOLOAD 0x00100  
+#define EXT4_MOUNT_DATA_FLAGS 0x00C00  
+#define EXT4_MOUNT_JOURNAL_DATA 0x00400  
+#define EXT4_MOUNT_ORDERED_DATA 0x00800  
+#define EXT4_MOUNT_WRITEBACK_DATA 0x00C00  
+#define EXT4_MOUNT_UPDATE_JOURNAL 0x01000  
+#define EXT4_MOUNT_NO_UID32 0x02000  
+#define EXT4_MOUNT_XATTR_USER 0x04000  
+#define EXT4_MOUNT_POSIX_ACL 0x08000  
+#define EXT4_MOUNT_NO_AUTO_DA_ALLOC 0x10000  
+#define EXT4_MOUNT_BARRIER 0x20000  
+#define EXT4_MOUNT_NOBH 0x40000  
+#define EXT4_MOUNT_QUOTA 0x80000  
+#define EXT4_MOUNT_USRQUOTA 0x100000  
+#define EXT4_MOUNT_GRPQUOTA 0x200000  
+#define EXT4_MOUNT_DIOREAD_NOLOCK 0x400000  
+#define EXT4_MOUNT_JOURNAL_CHECKSUM 0x800000  
+#define EXT4_MOUNT_JOURNAL_ASYNC_COMMIT 0x1000000  
+#define EXT4_MOUNT_I_VERSION 0x2000000  
+#define EXT4_MOUNT_DELALLOC 0x8000000  
+#define EXT4_MOUNT_DATA_ERR_ABORT 0x10000000  
+#define EXT4_MOUNT_BLOCK_VALIDITY 0x20000000  
+#define EXT4_MOUNT_DISCARD 0x40000000  
+
+#define clear_opt(o, opt) o &= ~EXT4_MOUNT_##opt
+#define set_opt(o, opt) o |= EXT4_MOUNT_##opt
+#define test_opt(sb, opt) (EXT4_SB(sb)->s_mount_opt &   EXT4_MOUNT_##opt)
+
+#define ext4_set_bit ext2_set_bit
+#define ext4_set_bit_atomic ext2_set_bit_atomic
+#define ext4_clear_bit ext2_clear_bit
+#define ext4_clear_bit_atomic ext2_clear_bit_atomic
+#define ext4_test_bit ext2_test_bit
+#define ext4_find_first_zero_bit ext2_find_first_zero_bit
+#define ext4_find_next_zero_bit ext2_find_next_zero_bit
+#define ext4_find_next_bit ext2_find_next_bit
+
+#define EXT4_DFL_MAX_MNT_COUNT 20  
+#define EXT4_DFL_CHECKINTERVAL 0  
+
+#define EXT4_ERRORS_CONTINUE 1  
+#define EXT4_ERRORS_RO 2  
+#define EXT4_ERRORS_PANIC 3  
+#define EXT4_ERRORS_DEFAULT EXT4_ERRORS_CONTINUE
+
+struct ext4_super_block {
+  __le32 s_inodes_count;
+ __le32 s_blocks_count_lo;
+ __le32 s_r_blocks_count_lo;
+ __le32 s_free_blocks_count_lo;
+  __le32 s_free_inodes_count;
+ __le32 s_first_data_block;
+ __le32 s_log_block_size;
+ __le32 s_obso_log_frag_size;
+  __le32 s_blocks_per_group;
+ __le32 s_obso_frags_per_group;
+ __le32 s_inodes_per_group;
+ __le32 s_mtime;
+  __le32 s_wtime;
+ __le16 s_mnt_count;
+ __le16 s_max_mnt_count;
+ __le16 s_magic;
+ __le16 s_state;
+ __le16 s_errors;
+ __le16 s_minor_rev_level;
+  __le32 s_lastcheck;
+ __le32 s_checkinterval;
+ __le32 s_creator_os;
+ __le32 s_rev_level;
+  __le16 s_def_resuid;
+ __le16 s_def_resgid;
+
+ __le32 s_first_ino;
+ __le16 s_inode_size;
+ __le16 s_block_group_nr;
+ __le32 s_feature_compat;
+  __le32 s_feature_incompat;
+ __le32 s_feature_ro_compat;
+  __u8 s_uuid[16];
+  char s_volume_name[16];
+  char s_last_mounted[64];
+  __le32 s_algorithm_usage_bitmap;
+
+ __u8 s_prealloc_blocks;
+ __u8 s_prealloc_dir_blocks;
+ __le16 s_reserved_gdt_blocks;
+
+  __u8 s_journal_uuid[16];
+  __le32 s_journal_inum;
+ __le32 s_journal_dev;
+ __le32 s_last_orphan;
+ __le32 s_hash_seed[4];
+ __u8 s_def_hash_version;
+ __u8 s_reserved_char_pad;
+ __le16 s_desc_size;
+  __le32 s_default_mount_opts;
+ __le32 s_first_meta_bg;
+ __le32 s_mkfs_time;
+ __le32 s_jnl_blocks[17];
+
+  __le32 s_blocks_count_hi;
+ __le32 s_r_blocks_count_hi;
+ __le32 s_free_blocks_count_hi;
+ __le16 s_min_extra_isize;
+ __le16 s_want_extra_isize;
+ __le32 s_flags;
+ __le16 s_raid_stride;
+ __le16 s_mmp_interval;
+ __le64 s_mmp_block;
+ __le32 s_raid_stripe_width;
+ __u8 s_log_groups_per_flex;
+ __u8 s_reserved_char_pad2;
+ __le16 s_reserved_pad;
+ __le64 s_kbytes_written;
+ __u32 s_reserved[160];
+};
+
+#define EXT4_SB(sb) (sb)
+
+#define NEXT_ORPHAN(inode) EXT4_I(inode)->i_dtime
+
+#define EXT4_OS_LINUX 0
+#define EXT4_OS_HURD 1
+#define EXT4_OS_MASIX 2
+#define EXT4_OS_FREEBSD 3
+#define EXT4_OS_LITES 4
+
+#define EXT4_GOOD_OLD_REV 0  
+#define EXT4_DYNAMIC_REV 1  
+
+#define EXT4_CURRENT_REV EXT4_GOOD_OLD_REV
+#define EXT4_MAX_SUPP_REV EXT4_DYNAMIC_REV
+
+#define EXT4_GOOD_OLD_INODE_SIZE 128
+
+#define EXT4_HAS_COMPAT_FEATURE(sb,mask)   ((EXT4_SB(sb)->s_es->s_feature_compat & cpu_to_le32(mask)) != 0)
+#define EXT4_HAS_RO_COMPAT_FEATURE(sb,mask)   ((EXT4_SB(sb)->s_es->s_feature_ro_compat & cpu_to_le32(mask)) != 0)
+#define EXT4_HAS_INCOMPAT_FEATURE(sb,mask)   ((EXT4_SB(sb)->s_es->s_feature_incompat & cpu_to_le32(mask)) != 0)
+#define EXT4_SET_COMPAT_FEATURE(sb,mask)   EXT4_SB(sb)->s_es->s_feature_compat |= cpu_to_le32(mask)
+#define EXT4_SET_RO_COMPAT_FEATURE(sb,mask)   EXT4_SB(sb)->s_es->s_feature_ro_compat |= cpu_to_le32(mask)
+#define EXT4_SET_INCOMPAT_FEATURE(sb,mask)   EXT4_SB(sb)->s_es->s_feature_incompat |= cpu_to_le32(mask)
+#define EXT4_CLEAR_COMPAT_FEATURE(sb,mask)   EXT4_SB(sb)->s_es->s_feature_compat &= ~cpu_to_le32(mask)
+#define EXT4_CLEAR_RO_COMPAT_FEATURE(sb,mask)   EXT4_SB(sb)->s_es->s_feature_ro_compat &= ~cpu_to_le32(mask)
+#define EXT4_CLEAR_INCOMPAT_FEATURE(sb,mask)   EXT4_SB(sb)->s_es->s_feature_incompat &= ~cpu_to_le32(mask)
+
+#define EXT4_FEATURE_COMPAT_DIR_PREALLOC 0x0001
+#define EXT4_FEATURE_COMPAT_IMAGIC_INODES 0x0002
+#define EXT4_FEATURE_COMPAT_HAS_JOURNAL 0x0004
+#define EXT4_FEATURE_COMPAT_EXT_ATTR 0x0008
+#define EXT4_FEATURE_COMPAT_RESIZE_INODE 0x0010
+#define EXT4_FEATURE_COMPAT_DIR_INDEX 0x0020
+
+#define EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER 0x0001
+#define EXT4_FEATURE_RO_COMPAT_LARGE_FILE 0x0002
+#define EXT4_FEATURE_RO_COMPAT_BTREE_DIR 0x0004
+#define EXT4_FEATURE_RO_COMPAT_HUGE_FILE 0x0008
+#define EXT4_FEATURE_RO_COMPAT_GDT_CSUM 0x0010
+#define EXT4_FEATURE_RO_COMPAT_DIR_NLINK 0x0020
+#define EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE 0x0040
+
+#define EXT4_FEATURE_INCOMPAT_COMPRESSION 0x0001
+#define EXT4_FEATURE_INCOMPAT_FILETYPE 0x0002
+#define EXT4_FEATURE_INCOMPAT_RECOVER 0x0004  
+#define EXT4_FEATURE_INCOMPAT_JOURNAL_DEV 0x0008  
+#define EXT4_FEATURE_INCOMPAT_META_BG 0x0010
+#define EXT4_FEATURE_INCOMPAT_EXTENTS 0x0040  
+#define EXT4_FEATURE_INCOMPAT_64BIT 0x0080
+#define EXT4_FEATURE_INCOMPAT_MMP 0x0100
+#define EXT4_FEATURE_INCOMPAT_FLEX_BG 0x0200
+#define EXT4_FEATURE_INCOMPAT_EA_INODE 0x0400  
+#define EXT4_FEATURE_INCOMPAT_DIRDATA 0x1000  
+
+#define EXT4_FEATURE_COMPAT_SUPP EXT2_FEATURE_COMPAT_EXT_ATTR
+#define EXT4_FEATURE_INCOMPAT_SUPP (EXT4_FEATURE_INCOMPAT_FILETYPE|   EXT4_FEATURE_INCOMPAT_RECOVER|   EXT4_FEATURE_INCOMPAT_META_BG|   EXT4_FEATURE_INCOMPAT_EXTENTS|   EXT4_FEATURE_INCOMPAT_64BIT|   EXT4_FEATURE_INCOMPAT_FLEX_BG)
+#define EXT4_FEATURE_RO_COMPAT_SUPP (EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER|   EXT4_FEATURE_RO_COMPAT_LARGE_FILE|   EXT4_FEATURE_RO_COMPAT_GDT_CSUM|   EXT4_FEATURE_RO_COMPAT_DIR_NLINK |   EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE |   EXT4_FEATURE_RO_COMPAT_BTREE_DIR |  EXT4_FEATURE_RO_COMPAT_HUGE_FILE)
+
+#define EXT4_DEF_RESUID 0
+#define EXT4_DEF_RESGID 0
+
+#define EXT4_DEF_INODE_READAHEAD_BLKS 32
+
+#define EXT4_DEFM_DEBUG 0x0001
+#define EXT4_DEFM_BSDGROUPS 0x0002
+#define EXT4_DEFM_XATTR_USER 0x0004
+#define EXT4_DEFM_ACL 0x0008
+#define EXT4_DEFM_UID16 0x0010
+#define EXT4_DEFM_JMODE 0x0060
+#define EXT4_DEFM_JMODE_DATA 0x0020
+#define EXT4_DEFM_JMODE_ORDERED 0x0040
+#define EXT4_DEFM_JMODE_WBACK 0x0060
+
+#define EXT4_DEF_MIN_BATCH_TIME 0
+#define EXT4_DEF_MAX_BATCH_TIME 15000  
+
+#define EXT4_FLEX_SIZE_DIR_ALLOC_SCHEME 4
+
+#define EXT4_NAME_LEN 255
+
+struct ext4_dir_entry {
+ __le32 inode;
+ __le16 rec_len;
+ __le16 name_len;
+ char name[EXT4_NAME_LEN];
+};
+
+struct ext4_dir_entry_2 {
+ __le32 inode;
+ __le16 rec_len;
+ __u8 name_len;
+ __u8 file_type;
+ char name[EXT4_NAME_LEN];
+};
+
+#define EXT4_FT_UNKNOWN 0
+#define EXT4_FT_REG_FILE 1
+#define EXT4_FT_DIR 2
+#define EXT4_FT_CHRDEV 3
+#define EXT4_FT_BLKDEV 4
+#define EXT4_FT_FIFO 5
+#define EXT4_FT_SOCK 6
+#define EXT4_FT_SYMLINK 7
+
+#define EXT4_FT_MAX 8
+
+#define EXT4_DIR_PAD 4
+#define EXT4_DIR_ROUND (EXT4_DIR_PAD - 1)
+#define EXT4_DIR_REC_LEN(name_len) (((name_len) + 8 + EXT4_DIR_ROUND) &   ~EXT4_DIR_ROUND)
+#define EXT4_MAX_REC_LEN ((1<<16)-1)
+
+#define is_dx(dir) (EXT4_HAS_COMPAT_FEATURE(dir->i_sb,   EXT4_FEATURE_COMPAT_DIR_INDEX) &&   (EXT4_I(dir)->i_flags & EXT4_INDEX_FL))
+#define EXT4_DIR_LINK_MAX(dir) (!is_dx(dir) && (dir)->i_nlink >= EXT4_LINK_MAX)
+#define EXT4_DIR_LINK_EMPTY(dir) ((dir)->i_nlink == 2 || (dir)->i_nlink == 1)
+
+#define DX_HASH_LEGACY 0
+#define DX_HASH_HALF_MD4 1
+#define DX_HASH_TEA 2
+#define DX_HASH_LEGACY_UNSIGNED 3
+#define DX_HASH_HALF_MD4_UNSIGNED 4
+#define DX_HASH_TEA_UNSIGNED 5
+
+#endif
+
diff --git a/extras/ext4_utils/ext4_extents.h b/extras/ext4_utils/ext4_extents.h
new file mode 100644
index 0000000..b1290f4
--- /dev/null
+++ b/extras/ext4_utils/ext4_extents.h
@@ -0,0 +1,88 @@
+/****************************************************************************
+ ****************************************************************************
+ ***
+ ***   This header was automatically generated from a Linux kernel header
+ ***   of the same name, to make information necessary for userspace to
+ ***   call into the kernel available to libc.  It contains only constants,
+ ***   structures, and macros generated from the original header, and thus,
+ ***   contains no copyrightable information.
+ ***
+ ****************************************************************************
+ ****************************************************************************/
+#ifndef _EXT4_EXTENTS
+#define _EXT4_EXTENTS
+
+#include "ext4.h"
+
+#define AGGRESSIVE_TEST_
+
+#define EXTENTS_STATS__
+
+#define CHECK_BINSEARCH__
+
+#define EXT_DEBUG__
+#ifdef EXT_DEBUG
+#define ext_debug(a...) printk(a)
+#else
+#define ext_debug(a...)
+#endif
+
+#define EXT_STATS_
+
+struct ext4_extent {
+ __le32 ee_block;
+ __le16 ee_len;
+ __le16 ee_start_hi;
+ __le32 ee_start_lo;
+};
+
+struct ext4_extent_idx {
+ __le32 ei_block;
+ __le32 ei_leaf_lo;
+ __le16 ei_leaf_hi;
+ __u16 ei_unused;
+};
+
+struct ext4_extent_header {
+ __le16 eh_magic;
+ __le16 eh_entries;
+ __le16 eh_max;
+ __le16 eh_depth;
+ __le32 eh_generation;
+};
+
+#define EXT4_EXT_MAGIC 0xf30a
+
+struct ext4_ext_path {
+ ext4_fsblk_t p_block;
+ __u16 p_depth;
+ struct ext4_extent *p_ext;
+ struct ext4_extent_idx *p_idx;
+ struct ext4_extent_header *p_hdr;
+ struct buffer_head *p_bh;
+};
+
+#define EXT4_EXT_CACHE_NO 0
+#define EXT4_EXT_CACHE_GAP 1
+#define EXT4_EXT_CACHE_EXTENT 2
+
+#define EXT_CONTINUE 0
+#define EXT_BREAK 1
+#define EXT_REPEAT 2
+
+#define EXT_MAX_BLOCK 0xffffffff
+
+#define EXT_INIT_MAX_LEN (1UL << 15)
+#define EXT_UNINIT_MAX_LEN (EXT_INIT_MAX_LEN - 1)
+
+#define EXT_FIRST_EXTENT(__hdr__)   ((struct ext4_extent *) (((char *) (__hdr__)) +   sizeof(struct ext4_extent_header)))
+#define EXT_FIRST_INDEX(__hdr__)   ((struct ext4_extent_idx *) (((char *) (__hdr__)) +   sizeof(struct ext4_extent_header)))
+#define EXT_HAS_FREE_INDEX(__path__)   (le16_to_cpu((__path__)->p_hdr->eh_entries)   < le16_to_cpu((__path__)->p_hdr->eh_max))
+#define EXT_LAST_EXTENT(__hdr__)   (EXT_FIRST_EXTENT((__hdr__)) + le16_to_cpu((__hdr__)->eh_entries) - 1)
+#define EXT_LAST_INDEX(__hdr__)   (EXT_FIRST_INDEX((__hdr__)) + le16_to_cpu((__hdr__)->eh_entries) - 1)
+#define EXT_MAX_EXTENT(__hdr__)   (EXT_FIRST_EXTENT((__hdr__)) + le16_to_cpu((__hdr__)->eh_max) - 1)
+#define EXT_MAX_INDEX(__hdr__)   (EXT_FIRST_INDEX((__hdr__)) + le16_to_cpu((__hdr__)->eh_max) - 1)
+
+#endif
+
+
diff --git a/extras/ext4_utils/ext4_jbd2.h b/extras/ext4_utils/ext4_jbd2.h
new file mode 100644
index 0000000..d937f5c
--- /dev/null
+++ b/extras/ext4_utils/ext4_jbd2.h
@@ -0,0 +1,53 @@
+/****************************************************************************
+ ****************************************************************************
+ ***
+ ***   This header was automatically generated from a Linux kernel header
+ ***   of the same name, to make information necessary for userspace to
+ ***   call into the kernel available to libc.  It contains only constants,
+ ***   structures, and macros generated from the original header, and thus,
+ ***   contains no copyrightable information.
+ ***
+ ****************************************************************************
+ ****************************************************************************/
+#ifndef _EXT4_JBD2_H
+#define _EXT4_JBD2_H
+
+#include "ext4.h"
+
+#define EXT4_JOURNAL(inode) (EXT4_SB((inode)->i_sb)->s_journal)
+
+#define EXT4_SINGLEDATA_TRANS_BLOCKS(sb)   (EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_EXTENTS)   ? 27U : 8U)
+
+#define EXT4_XATTR_TRANS_BLOCKS 6U
+
+#define EXT4_DATA_TRANS_BLOCKS(sb) (EXT4_SINGLEDATA_TRANS_BLOCKS(sb) +   EXT4_XATTR_TRANS_BLOCKS - 2 +   EXT4_MAXQUOTAS_TRANS_BLOCKS(sb))
+
+#define EXT4_META_TRANS_BLOCKS(sb) (EXT4_XATTR_TRANS_BLOCKS +   EXT4_MAXQUOTAS_TRANS_BLOCKS(sb))
+
+#define EXT4_DELETE_TRANS_BLOCKS(sb) (2 * EXT4_DATA_TRANS_BLOCKS(sb) + 64)
+
+#define EXT4_MAX_TRANS_DATA 64U
+
+#define EXT4_RESERVE_TRANS_BLOCKS 12U
+
+#define EXT4_INDEX_EXTRA_TRANS_BLOCKS 8
+
+#define EXT4_QUOTA_TRANS_BLOCKS(sb) 0
+#define EXT4_QUOTA_INIT_BLOCKS(sb) 0
+#define EXT4_QUOTA_DEL_BLOCKS(sb) 0
+#define EXT4_MAXQUOTAS_TRANS_BLOCKS(sb) (MAXQUOTAS*EXT4_QUOTA_TRANS_BLOCKS(sb))
+#define EXT4_MAXQUOTAS_INIT_BLOCKS(sb) (MAXQUOTAS*EXT4_QUOTA_INIT_BLOCKS(sb))
+#define EXT4_MAXQUOTAS_DEL_BLOCKS(sb) (MAXQUOTAS*EXT4_QUOTA_DEL_BLOCKS(sb))
+
+#define ext4_journal_get_undo_access(handle, bh)   __ext4_journal_get_undo_access(__func__, (handle), (bh))
+#define ext4_journal_get_write_access(handle, bh)   __ext4_journal_get_write_access(__func__, (handle), (bh))
+#define ext4_forget(handle, is_metadata, inode, bh, block_nr)   __ext4_forget(__func__, (handle), (is_metadata), (inode), (bh),  (block_nr))
+#define ext4_journal_get_create_access(handle, bh)   __ext4_journal_get_create_access(__func__, (handle), (bh))
+#define ext4_handle_dirty_metadata(handle, inode, bh)   __ext4_handle_dirty_metadata(__func__, (handle), (inode), (bh))
+
+#define EXT4_NOJOURNAL_MAX_REF_COUNT ((unsigned long) 4096)
+
+#define ext4_journal_stop(handle)   __ext4_journal_stop(__func__, (handle))
+
+#endif
+
diff --git a/extras/ext4_utils/ext4_utils.c b/extras/ext4_utils/ext4_utils.c
new file mode 100644
index 0000000..43b4480
--- /dev/null
+++ b/extras/ext4_utils/ext4_utils.c
@@ -0,0 +1,442 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ext4_utils.h"
+#include "uuid.h"
+#include "allocate.h"
+#include "indirect.h"
+#include "extent.h"
+
+#include <sparse/sparse.h>
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <string.h>
+
+#ifdef USE_MINGW
+#include <winsock2.h>
+#else
+#include <arpa/inet.h>
+#include <sys/ioctl.h>
+#endif
+
+#if defined(__linux__)
+#include <linux/fs.h>
+#elif defined(__APPLE__) && defined(__MACH__)
+#include <sys/disk.h>
+#endif
+
+#include "ext4.h"
+#include "jbd2.h"
+
+int force = 0;
+struct fs_info info;
+struct fs_aux_info aux_info;
+
+jmp_buf setjmp_env;
+
+/* returns 1 if a is a power of b */
+static int is_power_of(int a, int b)
+{
+	while (a > b) {
+		if (a % b)
+			return 0;
+		a /= b;
+	}
+
+	return (a == b) ? 1 : 0;
+}
+
+/* Returns 1 if the bg contains a backup superblock.  On filesystems with
+   the sparse_super feature, only block groups 0, 1, and powers of 3, 5,
+   and 7 have backup superblocks.  Otherwise, all block groups have backup
+   superblocks */
+int ext4_bg_has_super_block(int bg)
+{
+	/* Without sparse_super, every block group has a superblock */
+	if (!(info.feat_ro_compat & EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER))
+		return 1;
+
+	if (bg == 0 || bg == 1)
+		return 1;
+
+	if (is_power_of(bg, 3) || is_power_of(bg, 5) || is_power_of(bg, 7))
+		return 1;
+
+	return 0;
+}
+
+/* Write the filesystem image to a file */
+void write_ext4_image(int fd, int gz, int sparse, int crc)
+{
+	sparse_file_write(info.sparse_file, fd, gz, sparse, crc);
+}
+
+/* Compute the rest of the parameters of the filesystem from the basic info */
+void ext4_create_fs_aux_info()
+{
+	aux_info.first_data_block = (info.block_size > 1024) ? 0 : 1;
+	aux_info.len_blocks = info.len / info.block_size;
+	aux_info.inode_table_blocks = DIV_ROUND_UP(info.inodes_per_group * info.inode_size,
+		info.block_size);
+	aux_info.groups = DIV_ROUND_UP(aux_info.len_blocks - aux_info.first_data_block,
+		info.blocks_per_group);
+	aux_info.blocks_per_ind = info.block_size / sizeof(u32);
+	aux_info.blocks_per_dind = aux_info.blocks_per_ind * aux_info.blocks_per_ind;
+	aux_info.blocks_per_tind = aux_info.blocks_per_dind * aux_info.blocks_per_dind;
+
+	aux_info.bg_desc_blocks =
+		DIV_ROUND_UP(aux_info.groups * sizeof(struct ext2_group_desc),
+			info.block_size);
+
+	aux_info.default_i_flags = EXT4_NOATIME_FL;
+
+	u32 last_group_size = aux_info.len_blocks % info.blocks_per_group;
+	u32 last_header_size = 2 + aux_info.inode_table_blocks;
+	if (ext4_bg_has_super_block(aux_info.groups - 1))
+		last_header_size += 1 + aux_info.bg_desc_blocks +
+			info.bg_desc_reserve_blocks;
+	if (last_group_size > 0 && last_group_size < last_header_size) {
+		aux_info.groups--;
+		aux_info.len_blocks -= last_group_size;
+	}
+
+	aux_info.sb = calloc(info.block_size, 1);
+	/* Alloc an array to hold the pointers to the backup superblocks */
+	aux_info.backup_sb = calloc(aux_info.groups, sizeof(char *));
+
+	if (!aux_info.sb)
+		critical_error_errno("calloc");
+
+	aux_info.bg_desc = calloc(info.block_size, aux_info.bg_desc_blocks);
+	if (!aux_info.bg_desc)
+		critical_error_errno("calloc");
+}
+
+void ext4_free_fs_aux_info()
+{
+	unsigned int i;
+
+	for (i=0; i<aux_info.groups; i++) {
+		if (aux_info.backup_sb[i])
+			free(aux_info.backup_sb[i]);
+	}
+	free(aux_info.sb);
+	free(aux_info.bg_desc);
+}
+
+/* Fill in the superblock memory buffer based on the filesystem parameters */
+void ext4_fill_in_sb()
+{
+	unsigned int i;
+	struct ext4_super_block *sb = aux_info.sb;
+
+	sb->s_inodes_count = info.inodes_per_group * aux_info.groups;
+	sb->s_blocks_count_lo = aux_info.len_blocks;
+	sb->s_r_blocks_count_lo = 0;
+	sb->s_free_blocks_count_lo = 0;
+	sb->s_free_inodes_count = 0;
+	sb->s_first_data_block = aux_info.first_data_block;
+	sb->s_log_block_size = log_2(info.block_size / 1024);
+	sb->s_obso_log_frag_size = log_2(info.block_size / 1024);
+	sb->s_blocks_per_group = info.blocks_per_group;
+	sb->s_obso_frags_per_group = info.blocks_per_group;
+	sb->s_inodes_per_group = info.inodes_per_group;
+	sb->s_mtime = 0;
+	sb->s_wtime = 0;
+	sb->s_mnt_count = 0;
+	sb->s_max_mnt_count = 0xFFFF;
+	sb->s_magic = EXT4_SUPER_MAGIC;
+	sb->s_state = EXT4_VALID_FS;
+	sb->s_errors = EXT4_ERRORS_RO;
+	sb->s_minor_rev_level = 0;
+	sb->s_lastcheck = 0;
+	sb->s_checkinterval = 0;
+	sb->s_creator_os = EXT4_OS_LINUX;
+	sb->s_rev_level = EXT4_DYNAMIC_REV;
+	sb->s_def_resuid = EXT4_DEF_RESUID;
+	sb->s_def_resgid = EXT4_DEF_RESGID;
+
+	sb->s_first_ino = EXT4_GOOD_OLD_FIRST_INO;
+	sb->s_inode_size = info.inode_size;
+	sb->s_block_group_nr = 0;
+	sb->s_feature_compat = info.feat_compat;
+	sb->s_feature_incompat = info.feat_incompat;
+	sb->s_feature_ro_compat = info.feat_ro_compat;
+	generate_uuid("extandroid/make_ext4fs", info.label, sb->s_uuid);
+	memset(sb->s_volume_name, 0, sizeof(sb->s_volume_name));
+	strncpy(sb->s_volume_name, info.label, sizeof(sb->s_volume_name));
+	memset(sb->s_last_mounted, 0, sizeof(sb->s_last_mounted));
+	sb->s_algorithm_usage_bitmap = 0;
+
+	sb->s_reserved_gdt_blocks = info.bg_desc_reserve_blocks;
+	sb->s_prealloc_blocks = 0;
+	sb->s_prealloc_dir_blocks = 0;
+
+	//memcpy(sb->s_journal_uuid, sb->s_uuid, sizeof(sb->s_journal_uuid));
+	if (info.feat_compat & EXT4_FEATURE_COMPAT_HAS_JOURNAL)
+		sb->s_journal_inum = EXT4_JOURNAL_INO;
+	sb->s_journal_dev = 0;
+	sb->s_last_orphan = 0;
+	sb->s_hash_seed[0] = 0; /* FIXME */
+	sb->s_def_hash_version = DX_HASH_TEA;
+	sb->s_reserved_char_pad = EXT4_JNL_BACKUP_BLOCKS;
+	sb->s_desc_size = sizeof(struct ext2_group_desc);
+	sb->s_default_mount_opts = 0; /* FIXME */
+	sb->s_first_meta_bg = 0;
+	sb->s_mkfs_time = 0;
+	//sb->s_jnl_blocks[17]; /* FIXME */
+
+	sb->s_blocks_count_hi = aux_info.len_blocks >> 32;
+	sb->s_r_blocks_count_hi = 0;
+	sb->s_free_blocks_count_hi = 0;
+	sb->s_min_extra_isize = sizeof(struct ext4_inode) -
+		EXT4_GOOD_OLD_INODE_SIZE;
+	sb->s_want_extra_isize = sizeof(struct ext4_inode) -
+		EXT4_GOOD_OLD_INODE_SIZE;
+	sb->s_flags = 0;
+	sb->s_raid_stride = 0;
+	sb->s_mmp_interval = 0;
+	sb->s_mmp_block = 0;
+	sb->s_raid_stripe_width = 0;
+	sb->s_log_groups_per_flex = 0;
+	sb->s_kbytes_written = 0;
+
+	for (i = 0; i < aux_info.groups; i++) {
+		u64 group_start_block = aux_info.first_data_block + i *
+			info.blocks_per_group;
+		u32 header_size = 0;
+		if (ext4_bg_has_super_block(i)) {
+			if (i != 0) {
+				aux_info.backup_sb[i] = calloc(info.block_size, 1);
+				memcpy(aux_info.backup_sb[i], sb, info.block_size);
+				/* Update the block group nr of this backup superblock */
+				aux_info.backup_sb[i]->s_block_group_nr = i;
+				sparse_file_add_data(info.sparse_file, aux_info.backup_sb[i],
+						info.block_size, group_start_block);
+			}
+			sparse_file_add_data(info.sparse_file, aux_info.bg_desc,
+				aux_info.bg_desc_blocks * info.block_size,
+				group_start_block + 1);
+			header_size = 1 + aux_info.bg_desc_blocks + info.bg_desc_reserve_blocks;
+		}
+
+		aux_info.bg_desc[i].bg_block_bitmap = group_start_block + header_size;
+		aux_info.bg_desc[i].bg_inode_bitmap = group_start_block + header_size + 1;
+		aux_info.bg_desc[i].bg_inode_table = group_start_block + header_size + 2;
+
+		aux_info.bg_desc[i].bg_free_blocks_count = sb->s_blocks_per_group;
+		aux_info.bg_desc[i].bg_free_inodes_count = sb->s_inodes_per_group;
+		aux_info.bg_desc[i].bg_used_dirs_count = 0;
+	}
+}
+
+void ext4_queue_sb(void)
+{
+	/* The write_data* functions expect only block aligned calls.
+	 * This is not an issue, except when we write out the super
+	 * block on a system with a block size > 1K.  So, we need to
+	 * deal with that here.
+	 */
+	if (info.block_size > 1024) {
+		u8 *buf = calloc(info.block_size, 1);
+		memcpy(buf + 1024, (u8*)aux_info.sb, 1024);
+		sparse_file_add_data(info.sparse_file, buf, info.block_size, 0);
+	} else {
+		sparse_file_add_data(info.sparse_file, aux_info.sb, 1024, 1);
+	}
+}
+
+void ext4_parse_sb(struct ext4_super_block *sb)
+{
+	if (sb->s_magic != EXT4_SUPER_MAGIC)
+		error("superblock magic incorrect");
+
+	if ((sb->s_state & EXT4_VALID_FS) != EXT4_VALID_FS)
+		error("filesystem state not valid");
+
+	info.block_size = 1024 << sb->s_log_block_size;
+	info.blocks_per_group = sb->s_blocks_per_group;
+	info.inodes_per_group = sb->s_inodes_per_group;
+	info.inode_size = sb->s_inode_size;
+	info.inodes = sb->s_inodes_count;
+	info.feat_ro_compat = sb->s_feature_ro_compat;
+	info.feat_compat = sb->s_feature_compat;
+	info.feat_incompat = sb->s_feature_incompat;
+	info.bg_desc_reserve_blocks = sb->s_reserved_gdt_blocks;
+	info.label = sb->s_volume_name;
+
+	aux_info.len_blocks = ((u64)sb->s_blocks_count_hi << 32) +
+			sb->s_blocks_count_lo;
+	info.len = (u64)info.block_size * aux_info.len_blocks;
+
+	ext4_create_fs_aux_info();
+
+	memcpy(aux_info.sb, sb, sizeof(*sb));
+
+	if (aux_info.first_data_block != sb->s_first_data_block)
+		critical_error("first data block does not match");
+}
+
+void ext4_create_resize_inode()
+{
+	struct block_allocation *reserve_inode_alloc = create_allocation();
+	u32 reserve_inode_len = 0;
+	unsigned int i;
+
+	struct ext4_inode *inode = get_inode(EXT4_RESIZE_INO);
+	if (inode == NULL) {
+		error("failed to get resize inode");
+		return;
+	}
+
+	for (i = 0; i < aux_info.groups; i++) {
+		if (ext4_bg_has_super_block(i)) {
+			u64 group_start_block = aux_info.first_data_block + i *
+				info.blocks_per_group;
+			u32 reserved_block_start = group_start_block + 1 +
+				aux_info.bg_desc_blocks;
+			u32 reserved_block_len = info.bg_desc_reserve_blocks;
+			append_region(reserve_inode_alloc, reserved_block_start,
+				reserved_block_len, i);
+			reserve_inode_len += reserved_block_len;
+		}
+	}
+
+	inode_attach_resize(inode, reserve_inode_alloc);
+
+	inode->i_mode = S_IFREG | S_IRUSR | S_IWUSR;
+	inode->i_links_count = 1;
+
+	free_alloc(reserve_inode_alloc);
+}
+
+/* Allocate the blocks to hold a journal inode and connect them to the
+   reserved journal inode */
+void ext4_create_journal_inode()
+{
+	struct ext4_inode *inode = get_inode(EXT4_JOURNAL_INO);
+	if (inode == NULL) {
+		error("failed to get journal inode");
+		return;
+	}
+
+	u8 *journal_data = inode_allocate_data_extents(inode,
+			info.journal_blocks * info.block_size,
+			info.journal_blocks * info.block_size);
+	if (!journal_data) {
+		error("failed to allocate extents for journal data");
+		return;
+	}
+
+	inode->i_mode = S_IFREG | S_IRUSR | S_IWUSR;
+	inode->i_links_count = 1;
+
+	journal_superblock_t *jsb = (journal_superblock_t *)journal_data;
+	jsb->s_header.h_magic = htonl(JBD2_MAGIC_NUMBER);
+	jsb->s_header.h_blocktype = htonl(JBD2_SUPERBLOCK_V2);
+	jsb->s_blocksize = htonl(info.block_size);
+	jsb->s_maxlen = htonl(info.journal_blocks);
+	jsb->s_nr_users = htonl(1);
+	jsb->s_first = htonl(1);
+	jsb->s_sequence = htonl(1);
+
+	memcpy(aux_info.sb->s_jnl_blocks, &inode->i_block, sizeof(inode->i_block));
+}
+
+/* Update the number of free blocks and inodes in the filesystem and in each
+   block group */
+void ext4_update_free()
+{
+	unsigned int i;
+
+	for (i = 0; i < aux_info.groups; i++) {
+		u32 bg_free_blocks = get_free_blocks(i);
+		u32 bg_free_inodes = get_free_inodes(i);
+
+		aux_info.bg_desc[i].bg_free_blocks_count = bg_free_blocks;
+		aux_info.sb->s_free_blocks_count_lo += bg_free_blocks;
+
+		aux_info.bg_desc[i].bg_free_inodes_count = bg_free_inodes;
+		aux_info.sb->s_free_inodes_count += bg_free_inodes;
+
+		aux_info.bg_desc[i].bg_used_dirs_count += get_directories(i);
+	}
+}
+
+static u64 get_block_device_size(int fd)
+{
+	u64 size = 0;
+	int ret;
+
+#if defined(__linux__)
+	ret = ioctl(fd, BLKGETSIZE64, &size);
+#elif defined(__APPLE__) && defined(__MACH__)
+	ret = ioctl(fd, DKIOCGETBLOCKCOUNT, &size);
+#else
+	close(fd);
+	return 0;
+#endif
+
+	if (ret)
+		return 0;
+
+	return size;
+}
+
+u64 get_file_size(int fd)
+{
+	struct stat buf;
+	int ret;
+	u64 reserve_len = 0;
+	s64 computed_size;
+
+	ret = fstat(fd, &buf);
+	if (ret)
+		return 0;
+
+	if (info.len < 0)
+		reserve_len = -info.len;
+
+	if (S_ISREG(buf.st_mode))
+		computed_size = buf.st_size - reserve_len;
+	else if (S_ISBLK(buf.st_mode))
+		computed_size = get_block_device_size(fd) - reserve_len;
+	else
+		computed_size = 0;
+
+	if (computed_size < 0) {
+		warn("Computed filesystem size less than 0");
+		computed_size = 0;
+	}
+
+	return computed_size;
+}
+
+u64 parse_num(const char *arg)
+{
+	char *endptr;
+	u64 num = strtoull(arg, &endptr, 10);
+	if (*endptr == 'k' || *endptr == 'K')
+		num *= 1024LL;
+	else if (*endptr == 'm' || *endptr == 'M')
+		num *= 1024LL * 1024LL;
+	else if (*endptr == 'g' || *endptr == 'G')
+		num *= 1024LL * 1024LL * 1024LL;
+
+	return num;
+}
diff --git a/extras/ext4_utils/ext4_utils.h b/extras/ext4_utils/ext4_utils.h
new file mode 100644
index 0000000..cad2eae
--- /dev/null
+++ b/extras/ext4_utils/ext4_utils.h
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _EXT4_UTILS_H_
+#define _EXT4_UTILS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#define _FILE_OFFSET_BITS 64
+#define _LARGEFILE64_SOURCE 1
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <sys/types.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <setjmp.h>
+
+#if defined(__APPLE__) && defined(__MACH__)
+#define lseek64 lseek
+#define ftruncate64 ftruncate
+#define mmap64 mmap
+#define off64_t off_t
+#endif
+
+#ifdef __BIONIC__
+extern void*  __mmap2(void *, size_t, int, int, int, off_t);
+static inline void *mmap64(void *addr, size_t length, int prot, int flags,
+        int fd, off64_t offset)
+{
+    return __mmap2(addr, length, prot, flags, fd, offset >> 12);
+}
+#endif
+
+extern int force;
+
+#define warn(fmt, args...) do { fprintf(stderr, "warning: %s: " fmt "\n", __func__, ## args); } while (0)
+#define error(fmt, args...) do { fprintf(stderr, "error: %s: " fmt "\n", __func__, ## args); if (!force) longjmp(setjmp_env, EXIT_FAILURE); } while (0)
+#define error_errno(s, args...) error(s ": %s", ##args, strerror(errno))
+#define critical_error(fmt, args...) do { fprintf(stderr, "critical error: %s: " fmt "\n", __func__, ## args); longjmp(setjmp_env, EXIT_FAILURE); } while (0)
+#define critical_error_errno(s, args...) critical_error(s ": %s", ##args, strerror(errno))
+
+#define EXT4_SUPER_MAGIC 0xEF53
+#define EXT4_JNL_BACKUP_BLOCKS 1
+
+#ifndef min /* already defined by windows.h */
+#define min(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+#define DIV_ROUND_UP(x, y) (((x) + (y) - 1)/(y))
+#define ALIGN(x, y) ((y) * DIV_ROUND_UP((x), (y)))
+
+#define __le64 u64
+#define __le32 u32
+#define __le16 u16
+
+#define __be64 u64
+#define __be32 u32
+#define __be16 u16
+
+#define __u64 u64
+#define __u32 u32
+#define __u16 u16
+#define __u8 u8
+
+typedef unsigned long long u64;
+typedef signed long long s64;
+typedef unsigned int u32;
+typedef unsigned short int u16;
+typedef unsigned char u8;
+
+struct block_group_info;
+
+struct ext2_group_desc {
+	__le32 bg_block_bitmap;
+	__le32 bg_inode_bitmap;
+	__le32 bg_inode_table;
+	__le16 bg_free_blocks_count;
+	__le16 bg_free_inodes_count;
+	__le16 bg_used_dirs_count;
+	__le16 bg_pad;
+	__le32 bg_reserved[3];
+};
+
+struct fs_info {
+	s64 len;	/* If set to 0, ask the block device for the size,
+			 * if less than 0, reserve that much space at the
+			 * end of the partition, else use the size given. */
+	u32 block_size;
+	u32 blocks_per_group;
+	u32 inodes_per_group;
+	u32 inode_size;
+	u32 inodes;
+	u32 journal_blocks;
+	u16 feat_ro_compat;
+	u16 feat_compat;
+	u16 feat_incompat;
+	u32 bg_desc_reserve_blocks;
+	const char *label;
+	u8 no_journal;
+
+	struct sparse_file *sparse_file;
+};
+
+struct fs_aux_info {
+	struct ext4_super_block *sb;
+	struct ext4_super_block **backup_sb;
+	struct ext2_group_desc *bg_desc;
+	struct block_group_info *bgs;
+	u32 first_data_block;
+	u64 len_blocks;
+	u32 inode_table_blocks;
+	u32 groups;
+	u32 bg_desc_blocks;
+	u32 default_i_flags;
+	u32 blocks_per_ind;
+	u32 blocks_per_dind;
+	u32 blocks_per_tind;
+};
+
+extern struct fs_info info;
+extern struct fs_aux_info aux_info;
+
+extern jmp_buf setjmp_env;
+
+static inline int log_2(int j)
+{
+	int i;
+
+	for (i = 0; j > 0; i++)
+		j >>= 1;
+
+	return i - 1;
+}
+
+int ext4_bg_has_super_block(int bg);
+void write_ext4_image(int fd, int gz, int sparse, int crc);
+void ext4_create_fs_aux_info(void);
+void ext4_free_fs_aux_info(void);
+void ext4_fill_in_sb(void);
+void ext4_create_resize_inode(void);
+void ext4_create_journal_inode(void);
+void ext4_update_free(void);
+void ext4_queue_sb(void);
+u64 get_file_size(int fd);
+u64 parse_num(const char *arg);
+void ext4_parse_sb(struct ext4_super_block *sb);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/extras/ext4_utils/ext4fixup.c b/extras/ext4_utils/ext4fixup.c
new file mode 100644
index 0000000..f0124f8
--- /dev/null
+++ b/extras/ext4_utils/ext4fixup.c
@@ -0,0 +1,898 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "ext4_utils.h"
+#include "make_ext4fs.h"
+#include "ext4_extents.h"
+#include "allocate.h"
+#include "ext4fixup.h"
+
+#include <sparse/sparse.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#ifndef USE_MINGW
+#include <sys/mman.h>
+#endif
+
+#if defined(__APPLE__) && defined(__MACH__)
+#define lseek64 lseek
+#define off64_t off_t
+#endif
+
+/* The inode block count for a file/directory is in units of 512 byte blocks,
+ * _NOT_ the filesystem block size!
+ */
+#define INODE_BLOCK_SIZE 512
+
+#define MAX_EXT4_BLOCK_SIZE 4096
+
+/* The two modes the recurse_dir() can be in */
+#define SANITY_CHECK_PASS 1
+#define MARK_INODE_NUMS   2
+#define UPDATE_INODE_NUMS 3
+
+/* Magic numbers to indicate what state the update process is in */
+#define MAGIC_STATE_MARKING_INUMS  0x7000151515565512ll
+#define MAGIC_STATE_UPDATING_INUMS 0x6121131211735123ll
+#define MAGIC_STATE_UPDATING_SB    0x15e1715151558477ll
+
+/* Internal state variables corresponding to the magic numbers */
+#define STATE_UNSET          0
+#define STATE_MARKING_INUMS  1
+#define STATE_UPDATING_INUMS 2
+#define STATE_UPDATING_SB    3
+
+/* Used for automated testing of this programs ability to stop and be restarted wthout error */
+static int bail_phase = 0;
+static int bail_loc = 0;
+static int bail_count = 0;
+static int count = 0;
+
+/* global flags */
+static int verbose = 0;
+static int no_write = 0;
+
+static int new_inodes_per_group = 0;
+
+static int no_write_fixup_state = 0;
+
+static int compute_new_inum(unsigned int old_inum)
+{
+    unsigned int group, offset;
+
+    group = (old_inum - 1) / info.inodes_per_group;
+    offset = (old_inum -1) % info.inodes_per_group;
+
+    return (group * new_inodes_per_group) + offset + 1;
+}
+
+/* Function to read the primary superblock */
+static void read_sb(int fd, struct ext4_super_block *sb)
+{
+    off64_t ret;
+
+    ret = lseek64(fd, 1024, SEEK_SET);
+    if (ret < 0)
+        critical_error_errno("failed to seek to superblock");
+
+    ret = read(fd, sb, sizeof(*sb));
+    if (ret < 0)
+        critical_error_errno("failed to read superblock");
+    if (ret != sizeof(*sb))
+        critical_error("failed to read all of superblock");
+}
+
+/* Function to write a primary or backup superblock at a given offset */
+static void write_sb(int fd, unsigned long long offset, struct ext4_super_block *sb)
+{
+    off64_t ret;
+
+    if (no_write) {
+        return;
+    }
+
+    ret = lseek64(fd, offset, SEEK_SET);
+    if (ret < 0)
+        critical_error_errno("failed to seek to superblock");
+
+    ret = write(fd, sb, sizeof(*sb));
+    if (ret < 0)
+        critical_error_errno("failed to write superblock");
+    if (ret != sizeof(*sb))
+        critical_error("failed to write all of superblock");
+}
+
+static int get_fs_fixup_state(int fd)
+{
+    unsigned long long magic;
+    int ret, len;
+
+    if (no_write) {
+        return no_write_fixup_state;
+    }
+
+    lseek64(fd, 0, SEEK_SET);
+    len = read(fd, &magic, sizeof(magic));
+    if (len != sizeof(magic)) {
+        critical_error("cannot read fixup_state\n");
+    }
+
+    switch (magic) {
+        case MAGIC_STATE_MARKING_INUMS:
+            ret = STATE_MARKING_INUMS;
+            break;
+        case MAGIC_STATE_UPDATING_INUMS:
+            ret = STATE_UPDATING_INUMS;
+            break;
+        case MAGIC_STATE_UPDATING_SB:
+            ret = STATE_UPDATING_SB;
+            break;
+        default:
+            ret = STATE_UNSET;
+    }
+    return ret;
+}
+
+static int set_fs_fixup_state(int fd, int state)
+{
+    unsigned long long magic;
+    struct ext4_super_block sb;
+    int len;
+
+    if (no_write) {
+        no_write_fixup_state = state;
+        return 0;
+    }
+
+    switch (state) {
+        case STATE_MARKING_INUMS:
+            magic = MAGIC_STATE_MARKING_INUMS;
+            break;
+        case STATE_UPDATING_INUMS:
+            magic = MAGIC_STATE_UPDATING_INUMS;
+            break;
+        case STATE_UPDATING_SB:
+            magic = MAGIC_STATE_UPDATING_SB;
+            break;
+        case STATE_UNSET:
+        default:
+            magic = 0ll;
+            break;
+    }
+
+    lseek64(fd, 0, SEEK_SET);
+    len = write(fd, &magic, sizeof(magic));
+    if (len != sizeof(magic)) {
+        critical_error("cannot write fixup_state\n");
+    }
+
+    read_sb(fd, &sb);
+    if (magic) {
+        /* If we are in the process of updating the filesystem, make it unmountable */
+        sb.s_desc_size |= 1;
+    } else {
+        /* we are done, so make the filesystem mountable again */
+        sb.s_desc_size &= ~1;
+    }
+    write_sb(fd, 1024, &sb);
+
+    return 0;
+}
+
+static int read_ext(int fd)
+{
+    off64_t ret;
+    struct ext4_super_block sb;
+    unsigned int i;
+
+    read_sb(fd, &sb);
+
+    ext4_parse_sb(&sb);
+
+    if (info.feat_incompat & EXT4_FEATURE_INCOMPAT_RECOVER) {
+        critical_error("Filesystem needs recovery first, mount and unmount to do that\n");
+    }
+
+    /* Clear the low bit which is set while this tool is in progress.
+     * If the tool crashes, it will still be set when we restart.
+     * The low bit is set to make the filesystem unmountable while
+     * it is being fixed up.  Also allow 0, which means the old ext2
+     * size is in use.
+     */
+    if (((sb.s_desc_size & ~1) != sizeof(struct ext2_group_desc)) &&
+        ((sb.s_desc_size & ~1) != 0))
+        critical_error("error: bg_desc_size != sizeof(struct ext2_group_desc)\n");
+
+    ret = lseek64(fd, info.len, SEEK_SET);
+    if (ret < 0)
+        critical_error_errno("failed to seek to end of input image");
+
+    ret = lseek64(fd, info.block_size * (aux_info.first_data_block + 1), SEEK_SET);
+    if (ret < 0)
+        critical_error_errno("failed to seek to block group descriptors");
+
+    ret = read(fd, aux_info.bg_desc, info.block_size * aux_info.bg_desc_blocks);
+    if (ret < 0)
+        critical_error_errno("failed to read block group descriptors");
+    if (ret != (int)info.block_size * (int)aux_info.bg_desc_blocks)
+        critical_error("failed to read all of block group descriptors");
+
+    if (verbose) {
+        printf("Found filesystem with parameters:\n");
+        printf("    Size: %llu\n", info.len);
+        printf("    Block size: %d\n", info.block_size);
+        printf("    Blocks per group: %d\n", info.blocks_per_group);
+        printf("    Inodes per group: %d\n", info.inodes_per_group);
+        printf("    Inode size: %d\n", info.inode_size);
+        printf("    Label: %s\n", info.label);
+        printf("    Blocks: %llu\n", aux_info.len_blocks);
+        printf("    Block groups: %d\n", aux_info.groups);
+        printf("    Reserved block group size: %d\n", info.bg_desc_reserve_blocks);
+        printf("    Used %d/%d inodes and %d/%d blocks\n",
+                aux_info.sb->s_inodes_count - aux_info.sb->s_free_inodes_count,
+                aux_info.sb->s_inodes_count,
+                aux_info.sb->s_blocks_count_lo - aux_info.sb->s_free_blocks_count_lo,
+                aux_info.sb->s_blocks_count_lo);
+    }
+
+    return 0;
+}
+
+static int read_inode(int fd, unsigned int inum, struct ext4_inode *inode)
+{
+    unsigned int bg_num, bg_offset;
+    off64_t inode_offset;
+    int len;
+
+    bg_num = (inum-1) / info.inodes_per_group;
+    bg_offset = (inum-1) % info.inodes_per_group;
+
+    inode_offset = ((unsigned long long)aux_info.bg_desc[bg_num].bg_inode_table * info.block_size) +
+                    (bg_offset * info.inode_size);
+
+    if (lseek64(fd, inode_offset, SEEK_SET) < 0) {
+        critical_error_errno("failed to seek to inode %d\n", inum);
+    }
+
+    len=read(fd, inode, sizeof(*inode));
+    if (len != sizeof(*inode)) {
+        critical_error_errno("failed to read inode %d\n", inum);
+    }
+
+    return 0;
+}
+
+static int read_block(int fd, unsigned long long block_num, void *block)
+{
+    off64_t off;
+    unsigned int len;
+
+    off = block_num * info.block_size;
+
+    if (lseek64(fd, off, SEEK_SET) , 0) {
+        critical_error_errno("failed to seek to block %lld\n", block_num);
+    }
+
+    len=read(fd, block, info.block_size);
+    if (len != info.block_size) {
+        critical_error_errno("failed to read block %lld\n", block_num);
+    }
+
+    return 0;
+}
+
+static int write_block(int fd, unsigned long long block_num, void *block)
+{
+    off64_t off;
+    unsigned int len;
+
+    if (no_write) {
+        return 0;
+    }
+
+    off = block_num * info.block_size;
+
+    if (lseek64(fd, off, SEEK_SET) < 0) {
+        critical_error_errno("failed to seek to block %lld\n", block_num);
+    }
+
+    len=write(fd, block, info.block_size);
+    if (len != info.block_size) {
+        critical_error_errno("failed to write block %lld\n", block_num);
+    }
+
+    return 0;
+}
+
+static int bitmap_get_bit(u8 *bitmap, u32 bit)
+{
+        if (bitmap[bit / 8] & (1 << (bit % 8)))
+                return 1;
+
+        return 0;
+}
+
+static void bitmap_clear_bit(u8 *bitmap, u32 bit)
+{
+        bitmap[bit / 8] &= ~(1 << (bit % 8));
+
+        return;
+}
+
+static void check_inode_bitmap(int fd, unsigned int bg_num)
+{
+    unsigned int inode_bitmap_block_num;
+    unsigned char block[MAX_EXT4_BLOCK_SIZE];
+    int i, bitmap_updated = 0;
+
+    /* Using the bg_num, aux_info.bg_desc[], info.inodes_per_group and
+     * new_inodes_per_group, retrieve the inode bitmap, and make sure
+     * the bits between the old and new size are clear
+     */
+    inode_bitmap_block_num = aux_info.bg_desc[bg_num].bg_inode_bitmap;
+
+    read_block(fd, inode_bitmap_block_num, block);
+
+    for (i = info.inodes_per_group; i < new_inodes_per_group; i++) {
+        if (bitmap_get_bit(block, i)) {
+            bitmap_clear_bit(block, i);
+            bitmap_updated = 1;
+        }
+    }
+
+    if (bitmap_updated) {
+        if (verbose) {
+            printf("Warning: updated inode bitmap for block group %d\n", bg_num);
+        }
+        write_block(fd, inode_bitmap_block_num, block);
+    }
+
+    return;
+}
+
+/* Update the superblock and bgdesc of the specified block group */
+static int update_superblocks_and_bg_desc(int fd, int state)
+{
+    off64_t ret;
+    struct ext4_super_block sb;
+    unsigned int num_block_groups, total_new_inodes;
+    unsigned int i;
+
+
+    read_sb(fd, &sb);
+
+    /* Compute how many more inodes are now available */
+    num_block_groups = DIV_ROUND_UP(aux_info.len_blocks, info.blocks_per_group);
+    total_new_inodes = num_block_groups * (new_inodes_per_group - sb.s_inodes_per_group);
+
+    if (verbose) {
+        printf("created %d additional inodes\n", total_new_inodes);
+    }
+
+    /* Update the free inodes count in each block group descriptor */
+    for (i = 0; i < num_block_groups; i++) {
+       if (state == STATE_UPDATING_SB) {
+           aux_info.bg_desc[i].bg_free_inodes_count += (new_inodes_per_group - sb.s_inodes_per_group);
+       }
+       check_inode_bitmap(fd, i);
+    }
+
+    /* First some sanity checks */
+    if ((sb.s_inodes_count + total_new_inodes) != (new_inodes_per_group * num_block_groups)) {
+        critical_error("Failed sanity check on new inode count\n");
+    }
+    if (new_inodes_per_group % (info.block_size/info.inode_size)) {
+        critical_error("Failed sanity check on new inode per group alignment\n");
+    }
+
+    /* Update the free inodes count in the superblock */
+    sb.s_inodes_count += total_new_inodes;
+    sb.s_free_inodes_count += total_new_inodes;
+    sb.s_inodes_per_group = new_inodes_per_group;
+
+    for (i = 0; i < aux_info.groups; i++) {
+        if (ext4_bg_has_super_block(i)) {
+            unsigned int sb_offset;
+
+            if (i == 0) {
+              /* The first superblock is offset by 1K to leave room for boot sectors */
+              sb_offset = 1024;
+            } else {
+              sb_offset = 0;
+            }
+
+            sb.s_block_group_nr = i;
+            /* Don't write out the backup superblocks with the bit set in the s_desc_size
+             * which prevents the filesystem from mounting.  The bit for the primary
+             * superblock will be cleared on the final call to set_fs_fixup_state() */
+            if (i != 0) {
+                sb.s_desc_size &= ~1;
+            }
+
+            write_sb(fd, (unsigned long long)i * info.blocks_per_group * info.block_size + sb_offset, &sb);
+
+            ret = lseek64(fd, ((unsigned long long)i * info.blocks_per_group * info.block_size) +
+                              (info.block_size * (aux_info.first_data_block + 1)), SEEK_SET);
+            if (ret < 0)
+                critical_error_errno("failed to seek to block group descriptors");
+
+            if (!no_write) {
+                ret = write(fd, aux_info.bg_desc, info.block_size * aux_info.bg_desc_blocks);
+                if (ret < 0)
+                    critical_error_errno("failed to write block group descriptors");
+                if (ret != (int)info.block_size * (int)aux_info.bg_desc_blocks)
+                    critical_error("failed to write all of block group descriptors");
+            }
+        }
+        if ((bail_phase == 4) && ((unsigned int)bail_count == i)) {
+            critical_error("bailing at phase 4\n");
+        }
+    }
+
+    return 0;
+}
+
+
+static int get_direct_blocks(struct ext4_inode *inode, unsigned long long *block_list,
+                                                       unsigned int *count)
+{
+    unsigned int i = 0;
+    unsigned int ret = 0;
+    unsigned int sectors_per_block;
+
+    sectors_per_block = info.block_size / INODE_BLOCK_SIZE;
+    while ((i < (inode->i_blocks_lo / sectors_per_block)) && (i < EXT4_NDIR_BLOCKS)) {
+        block_list[i] = inode->i_block[i];
+        i++;
+    }
+
+    *count += i;
+
+    if ((inode->i_blocks_lo / sectors_per_block) > EXT4_NDIR_BLOCKS) {
+        ret = 1;
+    }
+
+    return ret;
+}
+
+static int get_indirect_blocks(int fd, struct ext4_inode *inode,
+                               unsigned long long *block_list, unsigned int *count)
+{
+    unsigned int i;
+    unsigned int *indirect_block;
+    unsigned int sectors_per_block;
+
+    sectors_per_block = info.block_size / INODE_BLOCK_SIZE;
+
+    indirect_block = (unsigned int *)malloc(info.block_size);
+    if (indirect_block == 0) {
+        critical_error("failed to allocate memory for indirect_block\n");
+    }
+
+    read_block(fd, inode->i_block[EXT4_NDIR_BLOCKS], indirect_block);
+
+    for(i = 0; i < (inode->i_blocks_lo / sectors_per_block - EXT4_NDIR_BLOCKS); i++) {
+       block_list[EXT4_NDIR_BLOCKS+i] = indirect_block[i];
+    }
+
+    *count += i;
+
+    free(indirect_block);
+
+    return 0;
+}
+
+static int get_block_list_indirect(int fd, struct ext4_inode *inode, unsigned long long *block_list)
+{
+    unsigned int count=0;
+
+    if (get_direct_blocks(inode, block_list, &count)) {
+        get_indirect_blocks(fd, inode, block_list, &count);
+    }
+
+    return count;
+}
+
+static int get_extent_ents(int fd, struct ext4_extent_header *ext_hdr, unsigned long long *block_list)
+{
+    int i, j;
+    struct ext4_extent *extent;
+    off64_t fs_block_num;
+
+    if (ext_hdr->eh_depth != 0) {
+        critical_error("get_extent_ents called with eh_depth != 0\n");
+    }
+
+    /* The extent entries immediately follow the header, so add 1 to the pointer
+     * and cast it to an extent pointer.
+     */
+    extent = (struct ext4_extent *)(ext_hdr + 1);
+
+    for (i = 0; i < ext_hdr->eh_entries; i++) {
+         fs_block_num = ((off64_t)extent->ee_start_hi << 32) | extent->ee_start_lo;
+         for (j = 0; j < extent->ee_len; j++) {
+             block_list[extent->ee_block+j] = fs_block_num+j;
+         }
+         extent++;
+    }
+
+    return 0;
+}
+
+static int get_extent_idx(int fd, struct ext4_extent_header *ext_hdr, unsigned long long *block_list)
+{
+    int i;
+    struct ext4_extent_idx *extent_idx;
+    struct ext4_extent_header *tmp_ext_hdr;
+    off64_t fs_block_num;
+    unsigned char block[MAX_EXT4_BLOCK_SIZE];
+
+    /* Sanity check */
+    if (ext_hdr->eh_depth == 0) {
+        critical_error("get_extent_idx called with eh_depth == 0\n");
+    }
+
+    /* The extent entries immediately follow the header, so add 1 to the pointer
+     * and cast it to an extent pointer.
+     */
+    extent_idx = (struct ext4_extent_idx *)(ext_hdr + 1);
+
+    for (i = 0; i < ext_hdr->eh_entries; i++) {
+         fs_block_num = ((off64_t)extent_idx->ei_leaf_hi << 32) | extent_idx->ei_leaf_lo;
+         read_block(fd, fs_block_num, block);
+         tmp_ext_hdr = (struct ext4_extent_header *)block;
+
+         if (tmp_ext_hdr->eh_depth == 0) {
+             get_extent_ents(fd, tmp_ext_hdr, block_list); /* leaf node, fill in block_list */
+         } else {
+             get_extent_idx(fd, tmp_ext_hdr, block_list); /* recurse down the tree */
+         }
+    }
+
+    return 0;
+}
+
+static int get_block_list_extents(int fd, struct ext4_inode *inode, unsigned long long *block_list)
+{
+    struct ext4_extent_header *extent_hdr;
+
+    extent_hdr = (struct ext4_extent_header *)inode->i_block;
+
+    if (extent_hdr->eh_magic != EXT4_EXT_MAGIC) {
+        critical_error("extent header has unexpected magic value 0x%4.4x\n",
+                       extent_hdr->eh_magic);
+    }
+
+    if (extent_hdr->eh_depth == 0) {
+         get_extent_ents(fd, (struct ext4_extent_header *)inode->i_block, block_list);
+         return 0;
+    }
+
+    get_extent_idx(fd, (struct ext4_extent_header *)inode->i_block, block_list);
+
+    return 0;
+}
+
+static int is_entry_dir(int fd, struct ext4_dir_entry_2 *dirp, int pass)
+{
+    struct ext4_inode inode;
+    int ret = 0;
+
+    if (dirp->file_type == EXT4_FT_DIR) {
+        ret = 1;
+    } else if (dirp->file_type == EXT4_FT_UNKNOWN) {
+        /* Somebody was too lazy to fill in the dir entry,
+         * so we have to go fetch it from the inode. Grrr.
+         */
+        /* if UPDATE_INODE_NUMS pass and the inode high bit is not
+         * set return false so we don't recurse down the tree that is
+         * already updated.  Otherwise, fetch inode, and return answer.
+         */
+        if ((pass == UPDATE_INODE_NUMS) && !(dirp->inode & 0x80000000)) {
+            ret = 0;
+        } else {
+            read_inode(fd, (dirp->inode & 0x7fffffff), &inode);
+            if (S_ISDIR(inode.i_mode)) {
+                ret = 1;
+            }
+        }
+    }
+
+    return ret;
+}
+
+static int recurse_dir(int fd, struct ext4_inode *inode, char *dirbuf, int dirsize, int mode)
+{
+    unsigned long long *block_list;
+    unsigned int num_blocks;
+    struct ext4_dir_entry_2 *dirp, *prev_dirp = 0;
+    char name[256];
+    unsigned int i, leftover_space, is_dir;
+    struct ext4_inode tmp_inode;
+    int tmp_dirsize;
+    char *tmp_dirbuf;
+
+    switch (mode) {
+        case SANITY_CHECK_PASS:
+        case MARK_INODE_NUMS:
+        case UPDATE_INODE_NUMS:
+            break;
+        default:
+            critical_error("recurse_dir() called witn unknown mode!\n");
+    }
+
+    if (dirsize % info.block_size) {
+        critical_error("dirsize %d not a multiple of block_size %d.  This is unexpected!\n",
+                dirsize, info.block_size);
+    }
+
+    num_blocks = dirsize / info.block_size;
+
+    block_list = malloc((num_blocks + 1) * sizeof(*block_list));
+    if (block_list == 0) {
+        critical_error("failed to allocate memory for block_list\n");
+    }
+
+    if (inode->i_flags & EXT4_EXTENTS_FL) {
+        get_block_list_extents(fd, inode, block_list);
+    } else {
+        /* A directory that requires doubly or triply indirect blocks in huge indeed,
+         * and will almost certainly not exist, especially since make_ext4fs only creates
+         * directories with extents, and the kernel will too, but check to make sure the
+         * directory is not that big and give an error if so.  Our limit is 12 direct blocks,
+         * plus block_size/4 singly indirect blocks, which for a filesystem with 4K blocks
+         * is a directory 1036 blocks long, or 4,243,456 bytes long!  Assuming an average
+         * filename length of 20 (which I think is generous) thats 20 + 8 bytes overhead
+         * per entry, or 151,552 entries in the directory!
+         */
+        if (num_blocks > (info.block_size / 4 + EXT4_NDIR_BLOCKS)) {
+            critical_error("Non-extent based directory is too big!\n");
+        }
+        get_block_list_indirect(fd, inode, block_list);
+    }
+
+    /* Read in all the blocks for this directory */
+    for (i = 0; i < num_blocks; i++) {
+        read_block(fd, block_list[i], dirbuf + (i * info.block_size));
+    }
+
+    dirp = (struct ext4_dir_entry_2 *)dirbuf;
+    while (dirp < (struct ext4_dir_entry_2 *)(dirbuf + dirsize)) {
+        count++;
+        leftover_space = (char *)(dirbuf + dirsize) - (char *)dirp;
+        if (((mode == SANITY_CHECK_PASS) || (mode == UPDATE_INODE_NUMS)) &&
+            (leftover_space <= 8) && prev_dirp) {
+            /* This is a bug in an older version of make_ext4fs, where it
+             * didn't properly include the rest of the block in rec_len.
+             * Update rec_len on the previous entry to include the rest of
+             * the block and exit the loop.
+             */
+            if (verbose) {
+                printf("fixing up short rec_len for diretory entry for %s\n", name);
+            }
+            prev_dirp->rec_len += leftover_space;
+            break;
+        }
+
+        if (dirp->inode == 0) {
+            /* This is the last entry in the directory */
+            break;
+        }
+
+        strncpy(name, dirp->name, dirp->name_len);
+        name[dirp->name_len]='\0';
+
+        /* Only recurse on pass UPDATE_INODE_NUMS if the high bit is set.
+         * Otherwise, this inode entry has already been updated
+         * and we'll do the wrong thing.  Also don't recurse on . or ..,
+         * and certainly not on non-directories!
+         */
+        /* Hrm, looks like filesystems made by fastboot on stingray set the file_type
+         * flag, but the lost+found directory has the type set to Unknown, which
+         * seems to imply I need to read the inode and get it.
+         */
+        is_dir = is_entry_dir(fd, dirp, mode);
+        if ( is_dir && (strcmp(name, ".") && strcmp(name, "..")) &&
+            ((mode == SANITY_CHECK_PASS) || (mode == MARK_INODE_NUMS) ||
+              ((mode == UPDATE_INODE_NUMS) && (dirp->inode & 0x80000000))) ) {
+            /* A directory!  Recurse! */
+            read_inode(fd, dirp->inode & 0x7fffffff, &tmp_inode);
+
+            if (!S_ISDIR(tmp_inode.i_mode)) {
+                critical_error("inode %d for name %s does not point to a directory\n",
+                        dirp->inode & 0x7fffffff, name);
+            }
+            if (verbose) {
+                printf("inode %d %s use extents\n", dirp->inode & 0x7fffffff,
+                       (tmp_inode.i_flags & EXT4_EXTENTS_FL) ? "does" : "does not");
+            }
+
+            tmp_dirsize = tmp_inode.i_blocks_lo * INODE_BLOCK_SIZE;
+            if (verbose) {
+                printf("dir size = %d bytes\n", tmp_dirsize);
+            }
+
+            tmp_dirbuf = malloc(tmp_dirsize);
+            if (tmp_dirbuf == 0) {
+                critical_error("failed to allocate memory for tmp_dirbuf\n");
+            }
+
+            recurse_dir(fd, &tmp_inode, tmp_dirbuf, tmp_dirsize, mode);
+
+            free(tmp_dirbuf);
+        }
+
+        if (verbose) {
+            if (is_dir) {
+                printf("Directory %s\n", name);
+            } else {
+                printf("Non-directory %s\n", name);
+            }
+        }
+
+        /* Process entry based on current mode.  Either set high bit or change inode number */
+        if (mode == MARK_INODE_NUMS) {
+            dirp->inode |= 0x80000000;
+        } else if (mode == UPDATE_INODE_NUMS) {
+            if (dirp->inode & 0x80000000) {
+                dirp->inode = compute_new_inum(dirp->inode & 0x7fffffff);
+            }
+        }
+
+        if ((bail_phase == mode) && (bail_loc == 1) && (bail_count == count)) {
+            critical_error("Bailing at phase %d, loc 1 and count %d\n", mode, count);
+        }
+
+        /* Point dirp at the next entry */
+        prev_dirp = dirp;
+        dirp = (struct ext4_dir_entry_2*)((char *)dirp + dirp->rec_len);
+    }
+
+    /* Write out all the blocks for this directory */
+    for (i = 0; i < num_blocks; i++) {
+        write_block(fd, block_list[i], dirbuf + (i * info.block_size));
+        if ((bail_phase == mode) && (bail_loc == 2) && (bail_count <= count)) {
+            critical_error("Bailing at phase %d, loc 2 and count %d\n", mode, count);
+        }
+    }
+
+    free(block_list);
+
+    return 0;
+}
+
+int ext4fixup(char *fsdev)
+{
+    return ext4fixup_internal(fsdev, 0, 0, 0, 0, 0);
+}
+
+int ext4fixup_internal(char *fsdev, int v_flag, int n_flag,
+                       int stop_phase, int stop_loc, int stop_count)
+{
+    int fd;
+    struct ext4_inode root_inode;
+    unsigned int dirsize;
+    char *dirbuf;
+
+    if (setjmp(setjmp_env))
+        return EXIT_FAILURE; /* Handle a call to longjmp() */
+
+    verbose = v_flag;
+    no_write = n_flag;
+
+    bail_phase = stop_phase;
+    bail_loc = stop_loc;
+    bail_count = stop_count;
+
+    fd = open(fsdev, O_RDWR);
+
+    if (fd < 0)
+        critical_error_errno("failed to open filesystem image");
+
+    read_ext(fd);
+
+    if ((info.feat_incompat & EXT4_FEATURE_INCOMPAT_FILETYPE) == 0) {
+        critical_error("Expected filesystem to have filetype flag set\n");
+    }
+
+#if 0 // If we have to fix the directory rec_len issue, we can't use this check
+    /* Check to see if the inodes/group is copacetic */
+    if (info.inodes_per_blockgroup % (info.block_size/info.inode_size) == 0) {
+             /* This filesystem has either already been updated, or was
+              * made correctly.
+              */
+             if (verbose) {
+                 printf("%s: filesystem correct, no work to do\n", me);
+             }
+             exit(0);
+    }
+#endif
+
+    /* Compute what the new value of inodes_per_blockgroup will be when we're done */
+    new_inodes_per_group=ALIGN(info.inodes_per_group,(info.block_size/info.inode_size));
+
+    read_inode(fd, EXT4_ROOT_INO, &root_inode);
+
+    if (!S_ISDIR(root_inode.i_mode)) {
+        critical_error("root inode %d does not point to a directory\n", EXT4_ROOT_INO);
+    }
+    if (verbose) {
+        printf("inode %d %s use extents\n", EXT4_ROOT_INO,
+               (root_inode.i_flags & EXT4_EXTENTS_FL) ? "does" : "does not");
+    }
+
+    dirsize = root_inode.i_blocks_lo * INODE_BLOCK_SIZE;
+    if (verbose) {
+        printf("root dir size = %d bytes\n", dirsize);
+    }
+
+    dirbuf = malloc(dirsize);
+    if (dirbuf == 0) {
+        critical_error("failed to allocate memory for dirbuf\n");
+    }
+
+    /* Perform a sanity check pass first, try to catch any errors that will occur
+     * before we actually change anything, so we don't leave a filesystem in a
+     * corrupted, unrecoverable state.  Set no_write, make it quiet, and do a recurse
+     * pass and a update_superblock pass.  Set flags back to requested state when done.
+     * Only perform sanity check if the state is unset.  If the state is _NOT_ unset,
+     * then the tool has already been run and interrupted, and it presumably ran and
+     * passed sanity checked before it got interrupted.  It is _NOT_ safe to run sanity
+     * check if state is unset because it assumes inodes are to be computed using the
+     * old inodes/group, but some inode numbers may be updated to the new number.
+     */
+    if (get_fs_fixup_state(fd) == STATE_UNSET) {
+        verbose = 0;
+        no_write = 1;
+        recurse_dir(fd, &root_inode, dirbuf, dirsize, SANITY_CHECK_PASS);
+        update_superblocks_and_bg_desc(fd, STATE_UNSET);
+        verbose = v_flag;
+        no_write = n_flag;
+
+        set_fs_fixup_state(fd, STATE_MARKING_INUMS);
+    }
+
+    if (get_fs_fixup_state(fd) == STATE_MARKING_INUMS) {
+        count = 0; /* Reset debugging counter */
+        if (!recurse_dir(fd, &root_inode, dirbuf, dirsize, MARK_INODE_NUMS)) {
+            set_fs_fixup_state(fd, STATE_UPDATING_INUMS);
+        }
+    }
+
+    if (get_fs_fixup_state(fd) == STATE_UPDATING_INUMS) {
+        count = 0; /* Reset debugging counter */
+        if (!recurse_dir(fd, &root_inode, dirbuf, dirsize, UPDATE_INODE_NUMS)) {
+            set_fs_fixup_state(fd, STATE_UPDATING_SB);
+        }
+    }
+
+    if (get_fs_fixup_state(fd) == STATE_UPDATING_SB) {
+        /* set the new inodes/blockgroup number,
+         * and sets the state back to 0.
+         */
+        if (!update_superblocks_and_bg_desc(fd, STATE_UPDATING_SB)) {
+            set_fs_fixup_state(fd, STATE_UNSET);
+        }
+    }
+
+    close(fd);
+
+    return 0;
+}
diff --git a/extras/ext4_utils/ext4fixup.h b/extras/ext4_utils/ext4fixup.h
new file mode 100644
index 0000000..6ea2113
--- /dev/null
+++ b/extras/ext4_utils/ext4fixup.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+int ext4fixup(char *fsdev);
+int ext4fixup_internal(char *fsdev, int v_flag, int n_flag,
+                       int stop_phase, int stop_loc, int stop_count);
+
diff --git a/extras/ext4_utils/ext4fixup_main.c b/extras/ext4_utils/ext4fixup_main.c
new file mode 100644
index 0000000..47c7e65
--- /dev/null
+++ b/extras/ext4_utils/ext4fixup_main.c
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <unistd.h>
+#include <libgen.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "ext4fixup.h"
+
+static void usage(char *me)
+{
+    fprintf(stderr, "%s: usage: %s [-vn] <image or block device>\n", me, me);
+}
+
+int main(int argc, char **argv)
+{
+    int opt;
+    int verbose = 0;
+    int no_write = 0;
+    char *fsdev;
+    char *me;
+    int stop_phase = 0, stop_loc = 0, stop_count = 0;
+
+    me = basename(argv[0]);
+
+    while ((opt = getopt(argc, argv, "vnd:")) != -1) {
+        switch (opt) {
+        case 'v':
+            verbose = 1;
+            break;
+        case 'n':
+            no_write = 1;
+            break;
+        case 'd':
+            sscanf(optarg, "%d,%d,%d", &stop_phase, &stop_loc, &stop_count);
+            break;
+        }
+    }
+
+    if (optind >= argc) {
+        fprintf(stderr, "expected image or block device after options\n");
+        usage(me);
+        exit(EXIT_FAILURE);
+    }
+
+    fsdev = argv[optind++];
+
+    if (optind < argc) {
+        fprintf(stderr, "Unexpected argument: %s\n", argv[optind]);
+        usage(me);
+        exit(EXIT_FAILURE);
+    }
+
+    return ext4fixup_internal(fsdev, verbose, no_write, stop_phase, stop_loc, stop_count);
+}
diff --git a/extras/ext4_utils/extent.c b/extras/ext4_utils/extent.c
new file mode 100644
index 0000000..948bf41
--- /dev/null
+++ b/extras/ext4_utils/extent.c
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ext4_utils.h"
+#include "ext4.h"
+#include "ext4_extents.h"
+#include "extent.h"
+
+#include <sparse/sparse.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+
+
+/* Creates data buffers for the first backing_len bytes of a block allocation
+   and queues them to be written */
+static u8 *extent_create_backing(struct block_allocation *alloc,
+	u64 backing_len)
+{
+	u8 *data = calloc(backing_len, 1);
+	if (!data)
+		critical_error_errno("calloc");
+
+	u8 *ptr = data;
+	for (; alloc != NULL && backing_len > 0; get_next_region(alloc)) {
+		u32 region_block;
+		u32 region_len;
+		u32 len;
+		get_region(alloc, &region_block, &region_len);
+
+		len = min(region_len * info.block_size, backing_len);
+
+		sparse_file_add_data(info.sparse_file, ptr, len, region_block);
+		ptr += len;
+		backing_len -= len;
+	}
+
+	return data;
+}
+
+/* Queues each chunk of a file to be written to contiguous data block
+   regions */
+static void extent_create_backing_file(struct block_allocation *alloc,
+	u64 backing_len, const char *filename)
+{
+	off64_t offset = 0;
+	for (; alloc != NULL && backing_len > 0; get_next_region(alloc)) {
+		u32 region_block;
+		u32 region_len;
+		u32 len;
+		get_region(alloc, &region_block, &region_len);
+
+		len = min(region_len * info.block_size, backing_len);
+
+		sparse_file_add_file(info.sparse_file, filename, offset, len,
+				region_block);
+		offset += len;
+		backing_len -= len;
+	}
+}
+
+static struct block_allocation *do_inode_allocate_extents(
+	struct ext4_inode *inode, u64 len)
+{
+	u32 block_len = DIV_ROUND_UP(len, info.block_size);
+	struct block_allocation *alloc = allocate_blocks(block_len + 1);
+	u32 extent_block = 0;
+	u32 file_block = 0;
+	struct ext4_extent *extent;
+	u64 blocks;
+
+	if (alloc == NULL) {
+		error("Failed to allocate %d blocks\n", block_len + 1);
+		return NULL;
+	}
+
+	int allocation_len = block_allocation_num_regions(alloc);
+	if (allocation_len <= 3) {
+		reduce_allocation(alloc, 1);
+	} else {
+		reserve_oob_blocks(alloc, 1);
+		extent_block = get_oob_block(alloc, 0);
+	}
+
+	if (!extent_block) {
+		struct ext4_extent_header *hdr =
+			(struct ext4_extent_header *)&inode->i_block[0];
+		hdr->eh_magic = EXT4_EXT_MAGIC;
+		hdr->eh_entries = allocation_len;
+		hdr->eh_max = 3;
+		hdr->eh_generation = 0;
+		hdr->eh_depth = 0;
+
+		extent = (struct ext4_extent *)&inode->i_block[3];
+	} else {
+		struct ext4_extent_header *hdr =
+			(struct ext4_extent_header *)&inode->i_block[0];
+		hdr->eh_magic = EXT4_EXT_MAGIC;
+		hdr->eh_entries = 1;
+		hdr->eh_max = 3;
+		hdr->eh_generation = 0;
+		hdr->eh_depth = 1;
+
+		struct ext4_extent_idx *idx =
+			(struct ext4_extent_idx *)&inode->i_block[3];
+		idx->ei_block = 0;
+		idx->ei_leaf_lo = extent_block;
+		idx->ei_leaf_hi = 0;
+		idx->ei_unused = 0;
+
+		u8 *data = calloc(info.block_size, 1);
+		if (!data)
+			critical_error_errno("calloc");
+
+		sparse_file_add_data(info.sparse_file, data, info.block_size,
+				extent_block);
+
+		if (((int)(info.block_size - sizeof(struct ext4_extent_header) /
+				sizeof(struct ext4_extent))) < allocation_len) {
+			error("File size %llu is too big to fit in a single extent block\n",
+					len);
+			return NULL;
+		}
+
+		hdr = (struct ext4_extent_header *)data;
+		hdr->eh_magic = EXT4_EXT_MAGIC;
+		hdr->eh_entries = allocation_len;
+		hdr->eh_max = (info.block_size - sizeof(struct ext4_extent_header)) /
+			sizeof(struct ext4_extent);
+		hdr->eh_generation = 0;
+		hdr->eh_depth = 0;
+
+		extent = (struct ext4_extent *)(data +
+			sizeof(struct ext4_extent_header));
+	}
+
+	for (; !last_region(alloc); extent++, get_next_region(alloc)) {
+		u32 region_block;
+		u32 region_len;
+
+		get_region(alloc, &region_block, &region_len);
+		extent->ee_block = file_block;
+		extent->ee_len = region_len;
+		extent->ee_start_hi = 0;
+		extent->ee_start_lo = region_block;
+		file_block += region_len;
+	}
+
+	if (extent_block)
+		block_len += 1;
+
+	blocks = (u64)block_len * info.block_size / 512;
+
+	inode->i_flags |= EXT4_EXTENTS_FL;
+	inode->i_size_lo = len;
+	inode->i_size_high = len >> 32;
+	inode->i_blocks_lo = blocks;
+	inode->osd2.linux2.l_i_blocks_high = blocks >> 32;
+
+	rewind_alloc(alloc);
+
+	return alloc;
+}
+
+/* Allocates enough blocks to hold len bytes, with backing_len bytes in a data
+   buffer, and connects them to an inode.  Returns a pointer to the data
+   buffer. */
+u8 *inode_allocate_data_extents(struct ext4_inode *inode, u64 len,
+	u64 backing_len)
+{
+	struct block_allocation *alloc;
+	u8 *data = NULL;
+
+	alloc = do_inode_allocate_extents(inode, len);
+	if (alloc == NULL) {
+		error("failed to allocate extents for %llu bytes", len);
+		return NULL;
+	}
+
+	if (backing_len) {
+		data = extent_create_backing(alloc, backing_len);
+		if (!data)
+			error("failed to create backing for %llu bytes", backing_len);
+	}
+
+	free_alloc(alloc);
+
+	return data;
+}
+
+/* Allocates enough blocks to hold len bytes, queues them to be written
+   from a file, and connects them to an inode. */
+void inode_allocate_file_extents(struct ext4_inode *inode, u64 len,
+	const char *filename)
+{
+	struct block_allocation *alloc;
+
+	alloc = do_inode_allocate_extents(inode, len);
+	if (alloc == NULL) {
+		error("failed to allocate extents for %llu bytes", len);
+		return;
+	}
+
+	extent_create_backing_file(alloc, len, filename);
+
+	free_alloc(alloc);
+}
+
+/* Allocates enough blocks to hold len bytes and connects them to an inode */
+void inode_allocate_extents(struct ext4_inode *inode, u64 len)
+{
+	struct block_allocation *alloc;
+
+	alloc = do_inode_allocate_extents(inode, len);
+	if (alloc == NULL) {
+		error("failed to allocate extents for %llu bytes", len);
+		return;
+	}
+
+	free_alloc(alloc);
+}
diff --git a/extras/ext4_utils/extent.h b/extras/ext4_utils/extent.h
new file mode 100644
index 0000000..a1ddeb1
--- /dev/null
+++ b/extras/ext4_utils/extent.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _EXTENT_H_
+#define _EXTENT_H_
+
+#include "allocate.h"
+#include "ext4_utils.h"
+
+void inode_allocate_extents(struct ext4_inode *inode, u64 len);
+void inode_allocate_file_extents(struct ext4_inode *inode, u64 len,
+	const char *filename);
+u8 *inode_allocate_data_extents(struct ext4_inode *inode, u64 len,
+	u64 backing_len);
+void free_extent_blocks();
+
+#endif
diff --git a/extras/ext4_utils/indirect.c b/extras/ext4_utils/indirect.c
new file mode 100644
index 0000000..3d97ec8
--- /dev/null
+++ b/extras/ext4_utils/indirect.c
@@ -0,0 +1,516 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ext4_utils.h"
+#include "ext4.h"
+#include "ext4_extents.h"
+#include "indirect.h"
+#include "allocate.h"
+
+#include <sparse/sparse.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+
+/* Creates data buffers for the first backing_len bytes of a block allocation
+   and queues them to be written */
+static u8 *create_backing(struct block_allocation *alloc,
+		unsigned long backing_len)
+{
+	if (DIV_ROUND_UP(backing_len, info.block_size) > EXT4_NDIR_BLOCKS)
+		critical_error("indirect backing larger than %d blocks", EXT4_NDIR_BLOCKS);
+
+	u8 *data = calloc(backing_len, 1);
+	if (!data)
+		critical_error_errno("calloc");
+
+	u8 *ptr = data;
+	for (; alloc != NULL && backing_len > 0; get_next_region(alloc)) {
+		u32 region_block;
+		u32 region_len;
+		u32 len;
+		get_region(alloc, &region_block, &region_len);
+
+		len = min(region_len * info.block_size, backing_len);
+
+		sparse_file_add_data(info.sparse_file, ptr, len, region_block);
+		ptr += len;
+		backing_len -= len;
+	}
+
+	return data;
+}
+
+static void reserve_indirect_block(struct block_allocation *alloc, int len)
+{
+	if (reserve_oob_blocks(alloc, 1)) {
+		error("failed to reserve oob block");
+		return;
+	}
+
+	if (advance_blocks(alloc, len)) {
+		error("failed to advance %d blocks", len);
+		return;
+	}
+}
+
+static void reserve_dindirect_block(struct block_allocation *alloc, int len)
+{
+	if (reserve_oob_blocks(alloc, 1)) {
+		error("failed to reserve oob block");
+		return;
+	}
+
+	while (len > 0) {
+		int ind_block_len = min((int)aux_info.blocks_per_ind, len);
+
+		reserve_indirect_block(alloc, ind_block_len);
+
+		len -= ind_block_len;
+	}
+
+}
+
+static void reserve_tindirect_block(struct block_allocation *alloc, int len)
+{
+	if (reserve_oob_blocks(alloc, 1)) {
+		error("failed to reserve oob block");
+		return;
+	}
+
+	while (len > 0) {
+		int dind_block_len = min((int)aux_info.blocks_per_dind, len);
+
+		reserve_dindirect_block(alloc, dind_block_len);
+
+		len -= dind_block_len;
+	}
+}
+
+static void fill_indirect_block(u32 *ind_block, int len, struct block_allocation *alloc)
+{
+	int i;
+	for (i = 0; i < len; i++) {
+		ind_block[i] = get_block(alloc, i);
+	}
+}
+
+static void fill_dindirect_block(u32 *dind_block, int len, struct block_allocation *alloc)
+{
+	int i;
+	u32 ind_block;
+
+	for (i = 0; len >  0; i++) {
+		ind_block = get_oob_block(alloc, 0);
+		if (advance_oob_blocks(alloc, 1)) {
+			error("failed to reserve oob block");
+			return;
+		}
+
+		dind_block[i] = ind_block;
+
+		u32 *ind_block_data = calloc(info.block_size, 1);
+		sparse_file_add_data(info.sparse_file, ind_block_data, info.block_size,
+				ind_block);
+		int ind_block_len = min((int)aux_info.blocks_per_ind, len);
+
+		fill_indirect_block(ind_block_data, ind_block_len, alloc);
+
+		if (advance_blocks(alloc, ind_block_len)) {
+			error("failed to advance %d blocks", ind_block_len);
+			return;
+		}
+
+		len -= ind_block_len;
+	}
+}
+
+static void fill_tindirect_block(u32 *tind_block, int len, struct block_allocation *alloc)
+{
+	int i;
+	u32 dind_block;
+
+	for (i = 0; len > 0; i++) {
+		dind_block = get_oob_block(alloc, 0);
+		if (advance_oob_blocks(alloc, 1)) {
+			error("failed to reserve oob block");
+			return;
+		}
+
+		tind_block[i] = dind_block;
+
+		u32 *dind_block_data = calloc(info.block_size, 1);
+		sparse_file_add_data(info.sparse_file, dind_block_data, info.block_size,
+				dind_block);
+		int dind_block_len = min((int)aux_info.blocks_per_dind, len);
+
+		fill_dindirect_block(dind_block_data, dind_block_len, alloc);
+
+		len -= dind_block_len;
+	}
+}
+
+/* Given an allocation, attach as many blocks as possible to direct inode
+   blocks, and return the rest */
+static int inode_attach_direct_blocks(struct ext4_inode *inode,
+		struct block_allocation *alloc, u32 *block_len)
+{
+	int len = min(*block_len, EXT4_NDIR_BLOCKS);
+	int i;
+
+	for (i = 0; i < len; i++) {
+		inode->i_block[i] = get_block(alloc, i);
+	}
+
+	if (advance_blocks(alloc, len)) {
+		error("failed to advance %d blocks", len);
+		return -1;
+	}
+
+	*block_len -= len;
+	return 0;
+}
+
+/* Given an allocation, attach as many blocks as possible to indirect blocks,
+   and return the rest
+   Assumes that the blocks necessary to hold the indirect blocks were included
+   as part of the allocation */
+static int inode_attach_indirect_blocks(struct ext4_inode *inode,
+		struct block_allocation *alloc, u32 *block_len)
+{
+	int len = min(*block_len, aux_info.blocks_per_ind);
+
+	int ind_block = get_oob_block(alloc, 0);
+	inode->i_block[EXT4_IND_BLOCK] = ind_block;
+
+	if (advance_oob_blocks(alloc, 1)) {
+		error("failed to advance oob block");
+		return -1;
+	}
+
+	u32 *ind_block_data = calloc(info.block_size, 1);
+	sparse_file_add_data(info.sparse_file, ind_block_data, info.block_size,
+			ind_block);
+
+	fill_indirect_block(ind_block_data, len, alloc);
+
+	if (advance_blocks(alloc, len)) {
+		error("failed to advance %d blocks", len);
+		return -1;
+	}
+
+	*block_len -= len;
+	return 0;
+}
+
+/* Given an allocation, attach as many blocks as possible to doubly indirect
+   blocks, and return the rest.
+   Assumes that the blocks necessary to hold the indirect and doubly indirect
+   blocks were included as part of the allocation */
+static int inode_attach_dindirect_blocks(struct ext4_inode *inode,
+		struct block_allocation *alloc, u32 *block_len)
+{
+	int len = min(*block_len, aux_info.blocks_per_dind);
+
+	int dind_block = get_oob_block(alloc, 0);
+	inode->i_block[EXT4_DIND_BLOCK] = dind_block;
+
+	if (advance_oob_blocks(alloc, 1)) {
+		error("failed to advance oob block");
+		return -1;
+	}
+
+	u32 *dind_block_data = calloc(info.block_size, 1);
+	sparse_file_add_data(info.sparse_file, dind_block_data, info.block_size,
+			dind_block);
+
+	fill_dindirect_block(dind_block_data, len, alloc);
+
+	if (advance_blocks(alloc, len)) {
+		error("failed to advance %d blocks", len);
+		return -1;
+	}
+
+	*block_len -= len;
+	return 0;
+}
+
+/* Given an allocation, attach as many blocks as possible to triply indirect
+   blocks, and return the rest.
+   Assumes that the blocks necessary to hold the indirect, doubly indirect and
+   triply indirect blocks were included as part of the allocation */
+static int inode_attach_tindirect_blocks(struct ext4_inode *inode,
+		struct block_allocation *alloc, u32 *block_len)
+{
+	int len = min(*block_len, aux_info.blocks_per_tind);
+
+	int tind_block = get_oob_block(alloc, 0);
+	inode->i_block[EXT4_TIND_BLOCK] = tind_block;
+
+	if (advance_oob_blocks(alloc, 1)) {
+		error("failed to advance oob block");
+		return -1;
+	}
+
+	u32 *tind_block_data = calloc(info.block_size, 1);
+	sparse_file_add_data(info.sparse_file, tind_block_data, info.block_size,
+			tind_block);
+
+	fill_tindirect_block(tind_block_data, len, alloc);
+
+	if (advance_blocks(alloc, len)) {
+		error("failed to advance %d blocks", len);
+		return -1;
+	}
+
+	*block_len -= len;
+	return 0;
+}
+
+static void reserve_all_indirect_blocks(struct block_allocation *alloc, u32 len)
+{
+	if (len <= EXT4_NDIR_BLOCKS)
+		return;
+
+	len -= EXT4_NDIR_BLOCKS;
+	advance_blocks(alloc, EXT4_NDIR_BLOCKS);
+
+	u32 ind_block_len = min(aux_info.blocks_per_ind, len);
+	reserve_indirect_block(alloc, ind_block_len);
+
+	len -= ind_block_len;
+	if (len == 0)
+		return;
+
+	u32 dind_block_len = min(aux_info.blocks_per_dind, len);
+	reserve_dindirect_block(alloc, dind_block_len);
+
+	len -= dind_block_len;
+	if (len == 0)
+		return;
+
+	u32 tind_block_len = min(aux_info.blocks_per_tind, len);
+	reserve_tindirect_block(alloc, tind_block_len);
+
+	len -= tind_block_len;
+	if (len == 0)
+		return;
+
+	error("%d blocks remaining", len);
+}
+
+static u32 indirect_blocks_needed(u32 len)
+{
+	u32 ind = 0;
+
+	if (len <= EXT4_NDIR_BLOCKS)
+		return ind;
+
+	len -= EXT4_NDIR_BLOCKS;
+
+	/* We will need an indirect block for the rest of the blocks */
+	ind += DIV_ROUND_UP(len, aux_info.blocks_per_ind);
+
+	if (len <= aux_info.blocks_per_ind)
+		return ind;
+
+	len -= aux_info.blocks_per_ind;
+
+	ind += DIV_ROUND_UP(len, aux_info.blocks_per_dind);
+
+	if (len <= aux_info.blocks_per_dind)
+		return ind;
+
+	len -= aux_info.blocks_per_dind;
+
+	ind += DIV_ROUND_UP(len, aux_info.blocks_per_tind);
+
+	if (len <= aux_info.blocks_per_tind)
+		return ind;
+
+	critical_error("request too large");
+	return 0;
+}
+
+static int do_inode_attach_indirect(struct ext4_inode *inode,
+		struct block_allocation *alloc, u32 block_len)
+{
+	u32 count = block_len;
+
+	if (inode_attach_direct_blocks(inode, alloc, &count)) {
+		error("failed to attach direct blocks to inode");
+		return -1;
+	}
+
+	if (count > 0) {
+		if (inode_attach_indirect_blocks(inode, alloc, &count)) {
+			error("failed to attach indirect blocks to inode");
+			return -1;
+		}
+	}
+
+	if (count > 0) {
+		if (inode_attach_dindirect_blocks(inode, alloc, &count)) {
+			error("failed to attach dindirect blocks to inode");
+			return -1;
+		}
+	}
+
+	if (count > 0) {
+		if (inode_attach_tindirect_blocks(inode, alloc, &count)) {
+			error("failed to attach tindirect blocks to inode");
+			return -1;
+		}
+	}
+
+	if (count) {
+		error("blocks left after triply-indirect allocation");
+		return -1;
+	}
+
+	rewind_alloc(alloc);
+
+	return 0;
+}
+
+static struct block_allocation *do_inode_allocate_indirect(
+		struct ext4_inode *inode, u32 block_len)
+{
+	u32 indirect_len = indirect_blocks_needed(block_len);
+
+	struct block_allocation *alloc = allocate_blocks(block_len + indirect_len);
+
+	if (alloc == NULL) {
+		error("Failed to allocate %d blocks", block_len + indirect_len);
+		return NULL;
+	}
+
+	return alloc;
+}
+
+/* Allocates enough blocks to hold len bytes and connects them to an inode */
+void inode_allocate_indirect(struct ext4_inode *inode, unsigned long len)
+{
+	struct block_allocation *alloc;
+	u32 block_len = DIV_ROUND_UP(len, info.block_size);
+	u32 indirect_len = indirect_blocks_needed(block_len);
+
+	alloc = do_inode_allocate_indirect(inode, block_len);
+	if (alloc == NULL) {
+		error("failed to allocate extents for %lu bytes", len);
+		return;
+	}
+
+	reserve_all_indirect_blocks(alloc, block_len);
+	rewind_alloc(alloc);
+
+	if (do_inode_attach_indirect(inode, alloc, block_len))
+		error("failed to attach blocks to indirect inode");
+
+	inode->i_flags = 0;
+	inode->i_blocks_lo = (block_len + indirect_len) * info.block_size / 512;
+	inode->i_size_lo = len;
+
+	free_alloc(alloc);
+}
+
+void inode_attach_resize(struct ext4_inode *inode,
+		struct block_allocation *alloc)
+{
+	u32 block_len = block_allocation_len(alloc);
+	u32 superblocks = block_len / info.bg_desc_reserve_blocks;
+	u32 i, j;
+	u64 blocks;
+	u64 size;
+
+	if (block_len % info.bg_desc_reserve_blocks)
+		critical_error("reserved blocks not a multiple of %d",
+				info.bg_desc_reserve_blocks);
+
+	append_oob_allocation(alloc, 1);
+	u32 dind_block = get_oob_block(alloc, 0);
+
+	u32 *dind_block_data = calloc(info.block_size, 1);
+	if (!dind_block_data)
+		critical_error_errno("calloc");
+	sparse_file_add_data(info.sparse_file, dind_block_data, info.block_size,
+			dind_block);
+
+	u32 *ind_block_data = calloc(info.block_size, info.bg_desc_reserve_blocks);
+	if (!ind_block_data)
+		critical_error_errno("calloc");
+	sparse_file_add_data(info.sparse_file, ind_block_data,
+			info.block_size * info.bg_desc_reserve_blocks,
+			get_block(alloc, 0));
+
+	for (i = 0; i < info.bg_desc_reserve_blocks; i++) {
+		int r = (i - aux_info.bg_desc_blocks) % info.bg_desc_reserve_blocks;
+		if (r < 0)
+			r += info.bg_desc_reserve_blocks;
+
+		dind_block_data[i] = get_block(alloc, r);
+
+		for (j = 1; j < superblocks; j++) {
+			u32 b = j * info.bg_desc_reserve_blocks + r;
+			ind_block_data[r * aux_info.blocks_per_ind + j - 1] = get_block(alloc, b);
+		}
+	}
+
+	u32 last_block = EXT4_NDIR_BLOCKS + aux_info.blocks_per_ind +
+			aux_info.blocks_per_ind * (info.bg_desc_reserve_blocks - 1) +
+			superblocks - 2;
+
+	blocks = ((u64)block_len + 1) * info.block_size / 512;
+	size = (u64)last_block * info.block_size;
+
+	inode->i_block[EXT4_DIND_BLOCK] = dind_block;
+	inode->i_flags = 0;
+	inode->i_blocks_lo = blocks;
+	inode->osd2.linux2.l_i_blocks_high = blocks >> 32;
+	inode->i_size_lo = size;
+	inode->i_size_high = size >> 32;
+}
+
+/* Allocates enough blocks to hold len bytes, with backing_len bytes in a data
+   buffer, and connects them to an inode.  Returns a pointer to the data
+   buffer. */
+u8 *inode_allocate_data_indirect(struct ext4_inode *inode, unsigned long len,
+		unsigned long backing_len)
+{
+	struct block_allocation *alloc;
+	u32 block_len = DIV_ROUND_UP(len, info.block_size);
+	u8 *data = NULL;
+
+	alloc = do_inode_allocate_indirect(inode, block_len);
+	if (alloc == NULL) {
+		error("failed to allocate extents for %lu bytes", len);
+		return NULL;
+	}
+
+	if (backing_len) {
+		data = create_backing(alloc, backing_len);
+		if (!data)
+			error("failed to create backing for %lu bytes", backing_len);
+	}
+
+	rewind_alloc(alloc);
+	if (do_inode_attach_indirect(inode, alloc, block_len))
+		error("failed to attach blocks to indirect inode");
+
+	free_alloc(alloc);
+
+	return data;
+}
diff --git a/extras/ext4_utils/indirect.h b/extras/ext4_utils/indirect.h
new file mode 100644
index 0000000..cee8979
--- /dev/null
+++ b/extras/ext4_utils/indirect.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _INDIRECT_H_
+#define _INDIRECT_H_
+
+#include "allocate.h"
+
+void inode_allocate_indirect(struct ext4_inode *inode, unsigned long len);
+u8 *inode_allocate_data_indirect(struct ext4_inode *inode, unsigned long len,
+	unsigned long backing_len);
+void inode_attach_resize(struct ext4_inode *inode,
+		struct block_allocation *alloc);
+void free_indirect_blocks();
+
+#endif
diff --git a/extras/ext4_utils/jbd2.h b/extras/ext4_utils/jbd2.h
new file mode 100644
index 0000000..1c81641
--- /dev/null
+++ b/extras/ext4_utils/jbd2.h
@@ -0,0 +1,141 @@
+/****************************************************************************
+ ****************************************************************************
+ ***
+ ***   This header was automatically generated from a Linux kernel header
+ ***   of the same name, to make information necessary for userspace to
+ ***   call into the kernel available to libc.  It contains only constants,
+ ***   structures, and macros generated from the original header, and thus,
+ ***   contains no copyrightable information.
+ ***
+ ****************************************************************************
+ ****************************************************************************/
+#ifndef _LINUX_JBD2_H
+#define _LINUX_JBD2_H
+
+#define JBD2_DEBUG
+#define jfs_debug jbd_debug
+
+#define journal_oom_retry 1
+
+#undef JBD2_PARANOID_IOFAIL
+
+#define JBD2_DEFAULT_MAX_COMMIT_AGE 5
+
+#define jbd_debug(f, a...)  
+
+#define JBD2_MIN_JOURNAL_BLOCKS 1024
+
+#define JBD2_MAGIC_NUMBER 0xc03b3998U  
+
+#define JBD2_DESCRIPTOR_BLOCK 1
+#define JBD2_COMMIT_BLOCK 2
+#define JBD2_SUPERBLOCK_V1 3
+#define JBD2_SUPERBLOCK_V2 4
+#define JBD2_REVOKE_BLOCK 5
+
+typedef struct journal_header_s
+{
+ __be32 h_magic;
+ __be32 h_blocktype;
+ __be32 h_sequence;
+} journal_header_t;
+
+#define JBD2_CRC32_CHKSUM 1
+#define JBD2_MD5_CHKSUM 2
+#define JBD2_SHA1_CHKSUM 3
+
+#define JBD2_CRC32_CHKSUM_SIZE 4
+
+#define JBD2_CHECKSUM_BYTES (32 / sizeof(u32))
+
+struct commit_header {
+ __be32 h_magic;
+ __be32 h_blocktype;
+ __be32 h_sequence;
+ unsigned char h_chksum_type;
+ unsigned char h_chksum_size;
+ unsigned char h_padding[2];
+ __be32 h_chksum[JBD2_CHECKSUM_BYTES];
+ __be64 h_commit_sec;
+ __be32 h_commit_nsec;
+};
+
+typedef struct journal_block_tag_s
+{
+ __be32 t_blocknr;
+ __be32 t_flags;
+ __be32 t_blocknr_high;
+} journal_block_tag_t;
+
+#define JBD2_TAG_SIZE32 (offsetof(journal_block_tag_t, t_blocknr_high))
+#define JBD2_TAG_SIZE64 (sizeof(journal_block_tag_t))
+
+typedef struct jbd2_journal_revoke_header_s
+{
+ journal_header_t r_header;
+ __be32 r_count;
+} jbd2_journal_revoke_header_t;
+
+#define JBD2_FLAG_ESCAPE 1  
+#define JBD2_FLAG_SAME_UUID 2  
+#define JBD2_FLAG_DELETED 4  
+#define JBD2_FLAG_LAST_TAG 8  
+
+typedef struct journal_superblock_s
+{
+
+ journal_header_t s_header;
+
+ __be32 s_blocksize;
+ __be32 s_maxlen;
+ __be32 s_first;
+
+ __be32 s_sequence;
+ __be32 s_start;
+
+ __be32 s_errno;
+
+ __be32 s_feature_compat;
+ __be32 s_feature_incompat;
+ __be32 s_feature_ro_compat;
+
+ __u8 s_uuid[16];
+
+ __be32 s_nr_users;
+
+ __be32 s_dynsuper;
+
+ __be32 s_max_transaction;
+ __be32 s_max_trans_data;
+
+ __u32 s_padding[44];
+
+ __u8 s_users[16*48];
+
+} journal_superblock_t;
+
+#define JBD2_HAS_COMPAT_FEATURE(j,mask)   ((j)->j_format_version >= 2 &&   ((j)->j_superblock->s_feature_compat & cpu_to_be32((mask))))
+#define JBD2_HAS_RO_COMPAT_FEATURE(j,mask)   ((j)->j_format_version >= 2 &&   ((j)->j_superblock->s_feature_ro_compat & cpu_to_be32((mask))))
+#define JBD2_HAS_INCOMPAT_FEATURE(j,mask)   ((j)->j_format_version >= 2 &&   ((j)->j_superblock->s_feature_incompat & cpu_to_be32((mask))))
+
+#define JBD2_FEATURE_COMPAT_CHECKSUM 0x00000001
+
+#define JBD2_FEATURE_INCOMPAT_REVOKE 0x00000001
+#define JBD2_FEATURE_INCOMPAT_64BIT 0x00000002
+#define JBD2_FEATURE_INCOMPAT_ASYNC_COMMIT 0x00000004
+
+#define JBD2_KNOWN_COMPAT_FEATURES JBD2_FEATURE_COMPAT_CHECKSUM
+#define JBD2_KNOWN_ROCOMPAT_FEATURES 0
+#define JBD2_KNOWN_INCOMPAT_FEATURES (JBD2_FEATURE_INCOMPAT_REVOKE |   JBD2_FEATURE_INCOMPAT_64BIT |   JBD2_FEATURE_INCOMPAT_ASYNC_COMMIT)
+
+#define BJ_None 0  
+#define BJ_Metadata 1  
+#define BJ_Forget 2  
+#define BJ_IO 3  
+#define BJ_Shadow 4  
+#define BJ_LogCtl 5  
+#define BJ_Reserved 6  
+#define BJ_Types 7
+
+#endif
+
diff --git a/extras/ext4_utils/make_ext4fs.c b/extras/ext4_utils/make_ext4fs.c
new file mode 100644
index 0000000..b22355a
--- /dev/null
+++ b/extras/ext4_utils/make_ext4fs.c
@@ -0,0 +1,547 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "make_ext4fs.h"
+#include "ext4_utils.h"
+#include "allocate.h"
+#include "contents.h"
+#include "uuid.h"
+#include "wipe.h"
+
+#include <sparse/sparse.h>
+
+#include <assert.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#ifdef USE_MINGW
+
+#include <winsock2.h>
+
+/* These match the Linux definitions of these flags.
+   L_xx is defined to avoid conflicting with the win32 versions.
+*/
+#define L_S_IRUSR 00400
+#define L_S_IWUSR 00200
+#define L_S_IXUSR 00100
+#define S_IRWXU (L_S_IRUSR | L_S_IWUSR | L_S_IXUSR)
+#define S_IRGRP 00040
+#define S_IWGRP 00020
+#define S_IXGRP 00010
+#define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
+#define S_IROTH 00004
+#define S_IWOTH 00002
+#define S_IXOTH 00001
+#define S_IRWXO (S_IROTH | S_IWOTH | S_IXOTH)
+#define S_ISUID 0004000
+#define S_ISGID 0002000
+#define S_ISVTX 0001000
+
+#else
+
+#define O_BINARY 0
+
+#endif
+
+/* TODO: Not implemented:
+   Allocating blocks in the same block group as the file inode
+   Hash or binary tree directories
+   Special files: sockets, devices, fifos
+ */
+
+static int filter_dot(const struct dirent *d)
+{
+	return (strcmp(d->d_name, "..") && strcmp(d->d_name, "."));
+}
+
+static u32 build_default_directory_structure()
+{
+	u32 inode;
+	u32 root_inode;
+	struct dentry dentries = {
+			.filename = "lost+found",
+			.file_type = EXT4_FT_DIR,
+			.mode = S_IRWXU,
+			.uid = 0,
+			.gid = 0,
+			.mtime = 0,
+	};
+	root_inode = make_directory(0, 1, &dentries, 1);
+	inode = make_directory(root_inode, 0, NULL, 0);
+	*dentries.inode = inode;
+	inode_set_permissions(inode, dentries.mode,
+		dentries.uid, dentries.gid, dentries.mtime);
+
+	return root_inode;
+}
+
+#ifndef USE_MINGW
+/* Read a local directory and create the same tree in the generated filesystem.
+   Calls itself recursively with each directory in the given directory.
+   full_path is an absolute or relative path, with a trailing slash, to the
+   directory on disk that should be copied, or NULL if this is a directory
+   that does not exist on disk (e.g. lost+found).
+   dir_path is an absolute path, with trailing slash, to the same directory
+   if the image were mounted at the specified mount point */
+static u32 build_directory_structure(const char *full_path, const char *dir_path,
+		u32 dir_inode, fs_config_func_t fs_config_func,
+		struct selabel_handle *sehnd)
+{
+	int entries = 0;
+	struct dentry *dentries;
+	struct dirent **namelist = NULL;
+	struct stat stat;
+	int ret;
+	int i;
+	u32 inode;
+	u32 entry_inode;
+	u32 dirs = 0;
+	bool needs_lost_and_found = false;
+
+	if (full_path) {
+		entries = scandir(full_path, &namelist, filter_dot, (void*)alphasort);
+		if (entries < 0) {
+			error_errno("scandir");
+			return EXT4_ALLOCATE_FAILED;
+		}
+	}
+
+	if (dir_inode == 0) {
+		/* root directory, check if lost+found already exists */
+		for (i = 0; i < entries; i++)
+			if (strcmp(namelist[i]->d_name, "lost+found") == 0)
+				break;
+		if (i == entries)
+			needs_lost_and_found = true;
+	}
+
+	dentries = calloc(entries, sizeof(struct dentry));
+	if (dentries == NULL)
+		critical_error_errno("malloc");
+
+	for (i = 0; i < entries; i++) {
+		dentries[i].filename = strdup(namelist[i]->d_name);
+		if (dentries[i].filename == NULL)
+			critical_error_errno("strdup");
+
+		asprintf(&dentries[i].path, "%s%s", dir_path, namelist[i]->d_name);
+		asprintf(&dentries[i].full_path, "%s%s", full_path, namelist[i]->d_name);
+
+		free(namelist[i]);
+
+		ret = lstat(dentries[i].full_path, &stat);
+		if (ret < 0) {
+			error_errno("lstat");
+			i--;
+			entries--;
+			continue;
+		}
+
+		dentries[i].size = stat.st_size;
+		dentries[i].mode = stat.st_mode & (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO);
+		dentries[i].mtime = stat.st_mtime;
+		if (fs_config_func != NULL) {
+#ifdef ANDROID
+			unsigned int mode = 0;
+			unsigned int uid = 0;
+			unsigned int gid = 0;
+			uint64_t capabilities = 0;
+			int dir = S_ISDIR(stat.st_mode);
+			fs_config_func(dentries[i].path, dir, /*target_out_path=*/NULL, &uid, &gid, &mode, &capabilities);
+			dentries[i].mode = mode;
+			dentries[i].uid = uid;
+			dentries[i].gid = gid;
+#else
+			error("can't set android permissions - built without android support");
+#endif
+		}
+
+#ifdef HAVE_SELINUX
+		if (sehnd) {
+			if (selabel_lookup(sehnd, &dentries[i].secon, dentries[i].path, stat.st_mode) < 0) {
+				error("cannot lookup security context for %s", dentries[i].path);
+			}
+			if (dentries[i].secon)
+				printf("Labeling %s as %s\n", dentries[i].path, dentries[i].secon);
+		}
+#endif
+
+		if (S_ISREG(stat.st_mode)) {
+			dentries[i].file_type = EXT4_FT_REG_FILE;
+		} else if (S_ISDIR(stat.st_mode)) {
+			dentries[i].file_type = EXT4_FT_DIR;
+			dirs++;
+		} else if (S_ISCHR(stat.st_mode)) {
+			dentries[i].file_type = EXT4_FT_CHRDEV;
+		} else if (S_ISBLK(stat.st_mode)) {
+			dentries[i].file_type = EXT4_FT_BLKDEV;
+		} else if (S_ISFIFO(stat.st_mode)) {
+			dentries[i].file_type = EXT4_FT_FIFO;
+		} else if (S_ISSOCK(stat.st_mode)) {
+			dentries[i].file_type = EXT4_FT_SOCK;
+		} else if (S_ISLNK(stat.st_mode)) {
+			dentries[i].file_type = EXT4_FT_SYMLINK;
+			dentries[i].link = calloc(info.block_size, 1);
+			readlink(dentries[i].full_path, dentries[i].link, info.block_size - 1);
+		} else {
+			error("unknown file type on %s", dentries[i].path);
+			i--;
+			entries--;
+		}
+	}
+	free(namelist);
+
+	if (needs_lost_and_found) {
+		/* insert a lost+found directory at the beginning of the dentries */
+		struct dentry *tmp = calloc(entries + 1, sizeof(struct dentry));
+		memset(tmp, 0, sizeof(struct dentry));
+		memcpy(tmp + 1, dentries, entries * sizeof(struct dentry));
+		dentries = tmp;
+
+		dentries[0].filename = strdup("lost+found");
+		asprintf(&dentries[0].path, "%slost+found", dir_path);
+		dentries[0].full_path = NULL;
+		dentries[0].size = 0;
+		dentries[0].mode = S_IRWXU;
+		dentries[0].file_type = EXT4_FT_DIR;
+		dentries[0].uid = 0;
+		dentries[0].gid = 0;
+#ifdef HAVE_SELINUX
+		if (sehnd) {
+			if (selabel_lookup(sehnd, &dentries[0].secon, dentries[0].path, dentries[0].mode) < 0)
+				error("cannot lookup security context for %s", dentries[0].path);
+		}
+#endif
+		entries++;
+		dirs++;
+	}
+
+	inode = make_directory(dir_inode, entries, dentries, dirs);
+
+	for (i = 0; i < entries; i++) {
+		if (dentries[i].file_type == EXT4_FT_REG_FILE) {
+			entry_inode = make_file(dentries[i].full_path, dentries[i].size);
+		} else if (dentries[i].file_type == EXT4_FT_DIR) {
+			char *subdir_full_path = NULL;
+			char *subdir_dir_path;
+			if (dentries[i].full_path) {
+				ret = asprintf(&subdir_full_path, "%s/", dentries[i].full_path);
+				if (ret < 0)
+					critical_error_errno("asprintf");
+			}
+			ret = asprintf(&subdir_dir_path, "%s/", dentries[i].path);
+			if (ret < 0)
+				critical_error_errno("asprintf");
+			entry_inode = build_directory_structure(subdir_full_path,
+			subdir_dir_path, inode, fs_config_func, sehnd);
+			free(subdir_full_path);
+			free(subdir_dir_path);
+		} else if (dentries[i].file_type == EXT4_FT_SYMLINK) {
+			entry_inode = make_link(dentries[i].full_path, dentries[i].link);
+		} else {
+			error("unknown file type on %s", dentries[i].path);
+			entry_inode = 0;
+		}
+		*dentries[i].inode = entry_inode;
+
+		ret = inode_set_permissions(entry_inode, dentries[i].mode,
+			dentries[i].uid, dentries[i].gid,
+			dentries[i].mtime);
+		if (ret)
+			error("failed to set permissions on %s\n", dentries[i].path);
+		ret = inode_set_selinux(entry_inode, dentries[i].secon);
+		if (ret)
+			error("failed to set SELinux context on %s\n", dentries[i].path);
+
+		free(dentries[i].path);
+		free(dentries[i].full_path);
+		free(dentries[i].link);
+		free((void *)dentries[i].filename);
+		free(dentries[i].secon);
+	}
+
+	free(dentries);
+	return inode;
+}
+#endif
+
+static u32 compute_block_size()
+{
+	return 4096;
+}
+
+static u32 compute_journal_blocks()
+{
+	u32 journal_blocks = DIV_ROUND_UP(info.len, info.block_size) / 64;
+	if (journal_blocks < 1024)
+		journal_blocks = 1024;
+	if (journal_blocks > 32768)
+		journal_blocks = 32768;
+	return journal_blocks;
+}
+
+static u32 compute_blocks_per_group()
+{
+	return info.block_size * 8;
+}
+
+static u32 compute_inodes()
+{
+	return DIV_ROUND_UP(info.len, info.block_size) / 4;
+}
+
+static u32 compute_inodes_per_group()
+{
+	u32 blocks = DIV_ROUND_UP(info.len, info.block_size);
+	u32 block_groups = DIV_ROUND_UP(blocks, info.blocks_per_group);
+	u32 inodes = DIV_ROUND_UP(info.inodes, block_groups);
+	inodes = ALIGN(inodes, (info.block_size / info.inode_size));
+
+	/* After properly rounding up the number of inodes/group,
+	 * make sure to update the total inodes field in the info struct.
+	 */
+	info.inodes = inodes * block_groups;
+
+	return inodes;
+}
+
+static u32 compute_bg_desc_reserve_blocks()
+{
+	u32 blocks = DIV_ROUND_UP(info.len, info.block_size);
+	u32 block_groups = DIV_ROUND_UP(blocks, info.blocks_per_group);
+	u32 bg_desc_blocks = DIV_ROUND_UP(block_groups * sizeof(struct ext2_group_desc),
+			info.block_size);
+
+	u32 bg_desc_reserve_blocks =
+			DIV_ROUND_UP(block_groups * 1024 * sizeof(struct ext2_group_desc),
+					info.block_size) - bg_desc_blocks;
+
+	if (bg_desc_reserve_blocks > info.block_size / sizeof(u32))
+		bg_desc_reserve_blocks = info.block_size / sizeof(u32);
+
+	return bg_desc_reserve_blocks;
+}
+
+void reset_ext4fs_info() {
+    // Reset all the global data structures used by make_ext4fs so it
+    // can be called again.
+    memset(&info, 0, sizeof(info));
+    memset(&aux_info, 0, sizeof(aux_info));
+
+    if (info.sparse_file) {
+        sparse_file_destroy(info.sparse_file);
+        info.sparse_file = NULL;
+    }
+}
+
+int make_ext4fs(const char *filename, s64 len,
+                const char *mountpoint, struct selabel_handle *sehnd)
+{
+	int fd;
+	int status;
+
+	reset_ext4fs_info();
+	info.len = len;
+
+	fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
+	if (fd < 0) {
+		error_errno("open");
+		return EXIT_FAILURE;
+	}
+
+	status = make_ext4fs_internal(fd, NULL, mountpoint, NULL, 0, 0, 0, 1, 0, sehnd);
+	close(fd);
+
+	return status;
+}
+
+int make_ext4fs_internal(int fd, const char *_directory,
+                         const char *_mountpoint, fs_config_func_t fs_config_func, int gzip, int sparse,
+                         int crc, int wipe, int init_itabs, struct selabel_handle *sehnd)
+{
+	u32 root_inode_num;
+	u16 root_mode;
+	char *mountpoint;
+	char *directory = NULL;
+	int ret;
+
+	if (setjmp(setjmp_env))
+		return EXIT_FAILURE; /* Handle a call to longjmp() */
+
+	if (_mountpoint == NULL) {
+		mountpoint = strdup("");
+	} else if (_mountpoint[0] == '\0') {
+		mountpoint = strdup("/");
+	} else {
+		ret = asprintf(&mountpoint, "%s%s%s",
+				_mountpoint[0] == '/' ? "" : "/",
+				_mountpoint,
+				_mountpoint[strlen(_mountpoint) - 1] == '/' ? "" : "/");
+		if (ret < 0)
+			critical_error_errno("asprintf");
+	}
+
+	if (_directory) {
+		ret = asprintf(&directory, "%s%s",
+				_directory,
+				_directory[strlen(_directory) - 1] == '/' ? "" : "/");
+		if (ret < 0)
+			critical_error_errno("asprintf");
+	}
+
+	if (info.len <= 0)
+		info.len = get_file_size(fd);
+
+	if (info.len <= 0) {
+		fprintf(stderr, "Need size of filesystem\n");
+		return EXIT_FAILURE;
+	}
+
+	if (info.block_size <= 0)
+		info.block_size = compute_block_size();
+
+	/* Round down the filesystem length to be a multiple of the block size */
+	info.len &= ~((u64)info.block_size - 1);
+
+	if (info.journal_blocks == 0)
+		info.journal_blocks = compute_journal_blocks();
+
+	if (info.no_journal == 0)
+		info.feat_compat = EXT4_FEATURE_COMPAT_HAS_JOURNAL;
+	else
+		info.journal_blocks = 0;
+
+	if (info.blocks_per_group <= 0)
+		info.blocks_per_group = compute_blocks_per_group();
+
+	if (info.inodes <= 0)
+		info.inodes = compute_inodes();
+
+	if (info.inode_size <= 0)
+		info.inode_size = 256;
+
+	if (info.label == NULL)
+		info.label = "";
+
+	info.inodes_per_group = compute_inodes_per_group();
+
+	info.feat_compat |=
+			EXT4_FEATURE_COMPAT_RESIZE_INODE;
+
+	info.feat_ro_compat |=
+			EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER |
+			EXT4_FEATURE_RO_COMPAT_LARGE_FILE;
+
+	info.feat_incompat |=
+			EXT4_FEATURE_INCOMPAT_EXTENTS |
+			EXT4_FEATURE_INCOMPAT_FILETYPE;
+
+
+	info.bg_desc_reserve_blocks = compute_bg_desc_reserve_blocks();
+
+	printf("Creating filesystem with parameters:\n");
+	printf("    Size: %llu\n", info.len);
+	printf("    Block size: %d\n", info.block_size);
+	printf("    Blocks per group: %d\n", info.blocks_per_group);
+	printf("    Inodes per group: %d\n", info.inodes_per_group);
+	printf("    Inode size: %d\n", info.inode_size);
+	printf("    Journal blocks: %d\n", info.journal_blocks);
+	printf("    Label: %s\n", info.label);
+
+	ext4_create_fs_aux_info();
+
+	printf("    Blocks: %llu\n", aux_info.len_blocks);
+	printf("    Block groups: %d\n", aux_info.groups);
+	printf("    Reserved block group size: %d\n", info.bg_desc_reserve_blocks);
+
+	info.sparse_file = sparse_file_new(info.block_size, info.len);
+
+	block_allocator_init();
+
+	ext4_fill_in_sb();
+
+	if (reserve_inodes(0, 10) == EXT4_ALLOCATE_FAILED)
+		error("failed to reserve first 10 inodes");
+
+	if (info.feat_compat & EXT4_FEATURE_COMPAT_HAS_JOURNAL)
+		ext4_create_journal_inode();
+
+	if (info.feat_compat & EXT4_FEATURE_COMPAT_RESIZE_INODE)
+		ext4_create_resize_inode();
+
+#ifdef USE_MINGW
+	// Windows needs only 'create an empty fs image' functionality
+	assert(!directory);
+	root_inode_num = build_default_directory_structure();
+#else
+	if (directory)
+		root_inode_num = build_directory_structure(directory, mountpoint, 0,
+                        fs_config_func, sehnd);
+	else
+		root_inode_num = build_default_directory_structure();
+#endif
+
+	root_mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
+	inode_set_permissions(root_inode_num, root_mode, 0, 0, 0);
+
+#ifdef HAVE_SELINUX
+	if (sehnd) {
+		char *secontext = NULL;
+
+		if (selabel_lookup(sehnd, &secontext, mountpoint, S_IFDIR) < 0) {
+			error("cannot lookup security context for %s", mountpoint);
+		}
+		if (secontext) {
+			printf("Labeling %s as %s\n", mountpoint, secontext);
+			inode_set_selinux(root_inode_num, secontext);
+		}
+		freecon(secontext);
+	}
+#endif
+
+	ext4_update_free();
+
+	if (init_itabs)
+		init_unused_inode_tables();
+
+	ext4_queue_sb();
+
+	printf("Created filesystem with %d/%d inodes and %d/%d blocks\n",
+			aux_info.sb->s_inodes_count - aux_info.sb->s_free_inodes_count,
+			aux_info.sb->s_inodes_count,
+			aux_info.sb->s_blocks_count_lo - aux_info.sb->s_free_blocks_count_lo,
+			aux_info.sb->s_blocks_count_lo);
+
+	if (wipe)
+		wipe_block_device(fd, info.len);
+
+	write_ext4_image(fd, gzip, sparse, crc);
+
+	sparse_file_destroy(info.sparse_file);
+	info.sparse_file = NULL;
+
+	free(mountpoint);
+	free(directory);
+
+	return 0;
+}
diff --git a/extras/ext4_utils/make_ext4fs.h b/extras/ext4_utils/make_ext4fs.h
new file mode 100644
index 0000000..fb44485
--- /dev/null
+++ b/extras/ext4_utils/make_ext4fs.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _MAKE_EXT4FS_H_
+#define _MAKE_EXT4FS_H_
+
+#include <stdint.h>
+
+#include "ext4_utils.h"
+#include "ext4.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef HAVE_SELINUX
+#include <selinux/selinux.h>
+#include <selinux/label.h>
+#else
+struct selabel_handle;
+#endif
+
+typedef void (*fs_config_func_t)(const char *path, int dir, const char* target_out_path, unsigned *uid, unsigned *gid,
+        unsigned *mode, uint64_t *capabilities);
+
+void reset_ext4fs_info();
+int make_ext4fs(const char *filename, s64 len,
+                const char *mountpoint, struct selabel_handle *sehnd);
+int make_ext4fs_internal(int fd, const char *directory,
+                         const char *mountpoint, fs_config_func_t fs_config_func, int gzip, int sparse,
+                         int crc, int wipe, int init_itabs, struct selabel_handle *sehnd);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/extras/ext4_utils/make_ext4fs_main.c b/extras/ext4_utils/make_ext4fs_main.c
new file mode 100644
index 0000000..48563f4
--- /dev/null
+++ b/extras/ext4_utils/make_ext4fs_main.c
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fcntl.h>
+#include <libgen.h>
+#include <unistd.h>
+
+#if defined(__linux__)
+#include <linux/fs.h>
+#elif defined(__APPLE__) && defined(__MACH__)
+#include <sys/disk.h>
+#endif
+
+#ifdef ANDROID
+#include <private/android_filesystem_config.h>
+#endif
+#include <private/fs_config.h>
+
+#include "make_ext4fs.h"
+
+#ifndef USE_MINGW /* O_BINARY is windows-specific flag */
+#define O_BINARY 0
+#endif
+
+extern struct fs_info info;
+
+
+static void usage(char *path)
+{
+	fprintf(stderr, "%s [ -l <len> ] [ -j <journal size> ] [ -b <block_size> ]\n", basename(path));
+	fprintf(stderr, "    [ -g <blocks per group> ] [ -i <inodes> ] [ -I <inode size> ]\n");
+	fprintf(stderr, "    [ -L <label> ] [ -f ] [ -a <android mountpoint> ]\n");
+	fprintf(stderr, "    [ -S file_contexts ]\n");
+	fprintf(stderr, "    [ -z | -s ] [ -t ] [ -w ] [ -c ] [ -J ] [ -A ]\n");
+	fprintf(stderr, "    <filename> [<directory>]\n");
+}
+
+int main(int argc, char **argv)
+{
+	int opt;
+	const char *filename = NULL;
+	const char *directory = NULL;
+	char *mountpoint = NULL;
+	fs_config_func_t fs_config_func = NULL;
+	int gzip = 0;
+	int sparse = 0;
+	int crc = 0;
+	int wipe = 0;
+	int init_itabs = 0;
+	int set_config = 0;
+	int fd;
+	int exitcode;
+	struct selabel_handle *sehnd = NULL;
+#ifdef HAVE_SELINUX
+	struct selinux_opt seopts[] = { { SELABEL_OPT_PATH, "" } };
+#endif
+
+	while ((opt = getopt(argc, argv, "l:j:b:g:i:I:L:a:fwzJsctS:A")) != -1) {
+		switch (opt) {
+		case 'l':
+			info.len = parse_num(optarg);
+			break;
+		case 'j':
+			info.journal_blocks = parse_num(optarg);
+			break;
+		case 'b':
+			info.block_size = parse_num(optarg);
+			break;
+		case 'g':
+			info.blocks_per_group = parse_num(optarg);
+			break;
+		case 'i':
+			info.inodes = parse_num(optarg);
+			break;
+		case 'I':
+			info.inode_size = parse_num(optarg);
+			break;
+		case 'L':
+			info.label = optarg;
+			break;
+		case 'f':
+			force = 1;
+			break;
+		case 'a':
+#ifdef ANDROID
+			mountpoint = optarg;
+#else
+			fprintf(stderr, "can't set mountpoint - built without android support\n");
+			usage(argv[0]);
+			exit(EXIT_FAILURE);
+#endif
+			break;
+		case 'w':
+			wipe = 1;
+			break;
+		case 'z':
+			gzip = 1;
+			break;
+		case 'J':
+			info.no_journal = 1;
+			break;
+		case 'c':
+			crc = 1;
+			break;
+		case 's':
+			sparse = 1;
+			break;
+		case 't':
+			init_itabs = 1;
+			break;
+		case 'S':
+#ifdef HAVE_SELINUX
+			seopts[0].value = optarg;
+			sehnd = selabel_open(SELABEL_CTX_FILE, seopts, 1);
+			if (!sehnd) {
+				perror(optarg);
+				exit(EXIT_FAILURE);
+			}
+#endif
+			break;
+		case 'A':
+#if defined(ANDROID) || defined(BUILD_EUREKA)
+			set_config = 1; /* use android_filesystem_config */
+#else
+			fprintf(stderr, "can't set android permissions - built without android support\n");
+			usage(argv[0]);
+			exit(EXIT_FAILURE);
+#endif
+			break;
+		default: /* '?' */
+			usage(argv[0]);
+			exit(EXIT_FAILURE);
+		}
+	}
+
+	if (mountpoint || set_config)
+		fs_config_func = fs_config;
+
+	if (wipe && sparse) {
+		fprintf(stderr, "Cannot specifiy both wipe and sparse\n");
+		usage(argv[0]);
+		exit(EXIT_FAILURE);
+	}
+
+	if (wipe && gzip) {
+		fprintf(stderr, "Cannot specifiy both wipe and gzip\n");
+		usage(argv[0]);
+		exit(EXIT_FAILURE);
+	}
+
+	if (optind >= argc) {
+		fprintf(stderr, "Expected filename after options\n");
+		usage(argv[0]);
+		exit(EXIT_FAILURE);
+	}
+
+	filename = argv[optind++];
+
+	if (optind < argc)
+		directory = argv[optind++];
+
+	if (optind < argc) {
+		fprintf(stderr, "Unexpected argument: %s\n", argv[optind]);
+		usage(argv[0]);
+		exit(EXIT_FAILURE);
+	}
+
+	if (strcmp(filename, "-")) {
+		fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
+		if (fd < 0) {
+			error_errno("open");
+			return EXIT_FAILURE;
+		}
+	} else {
+		fd = STDOUT_FILENO;
+	}
+
+	exitcode = make_ext4fs_internal(fd, directory, mountpoint, fs_config_func, gzip,
+			sparse, crc, wipe, init_itabs, sehnd);
+	close(fd);
+
+	return exitcode;
+}
diff --git a/extras/ext4_utils/mkuserimg.sh b/extras/ext4_utils/mkuserimg.sh
new file mode 100755
index 0000000..1136a9e
--- /dev/null
+++ b/extras/ext4_utils/mkuserimg.sh
@@ -0,0 +1,60 @@
+#!/bin/bash -x
+#
+# To call this script, make sure make_ext4fs is somewhere in PATH
+
+function usage() {
+cat<<EOT
+Usage:
+mkuserimg.sh [-s] SRC_DIR OUTPUT_FILE EXT_VARIANT MOUNT_POINT SIZE [FILE_CONTEXTS]
+EOT
+}
+
+echo "in mkuserimg.sh PATH=$PATH"
+
+ENABLE_SPARSE_IMAGE=
+if [ "$1" = "-s" ]; then
+  ENABLE_SPARSE_IMAGE="-s"
+  shift
+fi
+
+if [ $# -ne 4 -a $# -ne 5 -a $# -ne 6 ]; then
+  usage
+  exit 1
+fi
+
+SRC_DIR=$1
+if [ ! -d $SRC_DIR ]; then
+  echo "Can not find directory $SRC_DIR!"
+  exit 2
+fi
+
+OUTPUT_FILE=$2
+EXT_VARIANT=$3
+MOUNT_POINT=$4
+SIZE=$5
+FC=$6
+
+case $EXT_VARIANT in
+  ext4) ;;
+  *) echo "Only ext4 is supported!"; exit 3 ;;
+esac
+
+if [ -z $MOUNT_POINT ]; then
+  echo "Mount point is required"
+  exit 2
+fi
+
+if [ -z $SIZE ]; then
+    SIZE=128M
+fi
+
+if [ -n "$FC" ]; then
+    FCOPT="-S $FC"
+fi
+
+MAKE_EXT4FS_CMD="make_ext4fs $ENABLE_SPARSE_IMAGE $FCOPT -l $SIZE -a $MOUNT_POINT $OUTPUT_FILE $SRC_DIR"
+echo $MAKE_EXT4FS_CMD
+$MAKE_EXT4FS_CMD
+if [ $? -ne 0 ]; then
+  exit 4
+fi
diff --git a/extras/ext4_utils/setup_fs.c b/extras/ext4_utils/setup_fs.c
new file mode 100644
index 0000000..56c563f
--- /dev/null
+++ b/extras/ext4_utils/setup_fs.c
@@ -0,0 +1,85 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/reboot.h>
+#include <sys/wait.h>
+#include <cutils/android_reboot.h>
+#include <cutils/partition_utils.h>
+
+const char *mkfs = "/system/bin/make_ext4fs";
+
+int setup_fs(const char *blockdev)
+{
+    char buf[256], path[128];
+    pid_t child;
+    int status, n;
+    pid_t pid;
+
+        /* we might be looking at an indirect reference */
+    n = readlink(blockdev, path, sizeof(path) - 1);
+    if (n > 0) {
+        path[n] = 0;
+        if (!memcmp(path, "/dev/block/", 11))
+            blockdev = path + 11;
+    }
+
+    if (strchr(blockdev,'/')) {
+        fprintf(stderr,"not a block device name: %s\n", blockdev);
+        return 0;
+    }
+
+    snprintf(buf, sizeof(buf), "/sys/fs/ext4/%s", blockdev);
+    if (access(buf, F_OK) == 0) {
+        fprintf(stderr,"device %s already has a filesystem\n", blockdev);
+        return 0;
+    }
+    snprintf(buf, sizeof(buf), "/dev/block/%s", blockdev);
+
+    if (!partition_wiped(buf)) {
+        fprintf(stderr,"device %s not wiped, probably encrypted, not wiping\n", blockdev);
+        return 0;
+    }
+
+    fprintf(stderr,"+++\n");
+
+    child = fork();
+    if (child < 0) {
+        fprintf(stderr,"error: setup_fs: fork failed\n");
+        return 0;
+    }
+    if (child == 0) {
+        execl(mkfs, mkfs, buf, NULL);
+        exit(-1);
+    }
+
+    while ((pid=waitpid(-1, &status, 0)) != child) {
+        if (pid == -1) {
+            fprintf(stderr, "error: setup_fs: waitpid failed!\n");
+            return 1;
+        }
+    }
+
+    fprintf(stderr,"---\n");
+    return 1;
+}
+
+
+int main(int argc, char **argv)
+{
+    int need_reboot = 0;
+
+    while (argc > 1) {
+        if (strlen(argv[1]) < 128)
+            need_reboot |= setup_fs(argv[1]);
+        argv++;
+        argc--;
+    }
+
+    if (need_reboot) {
+        fprintf(stderr,"REBOOT!\n");
+        android_reboot(ANDROID_RB_RESTART, 0, 0);
+        exit(-1);
+    }
+    return 0;
+}
diff --git a/extras/ext4_utils/sha1.c b/extras/ext4_utils/sha1.c
new file mode 100644
index 0000000..463ec38
--- /dev/null
+++ b/extras/ext4_utils/sha1.c
@@ -0,0 +1,273 @@
+/*	$NetBSD: sha1.c,v 1.1 2005/12/20 20:29:40 christos Exp $	*/
+/*	$OpenBSD: sha1.c,v 1.9 1997/07/23 21:12:32 kstailey Exp $	*/
+
+/*
+ * SHA-1 in C
+ * By Steve Reid <steve@edmweb.com>
+ * 100% Public Domain
+ *
+ * Test Vectors (from FIPS PUB 180-1)
+ * "abc"
+ *   A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
+ * "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
+ *   84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
+ * A million repetitions of "a"
+ *   34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
+ */
+
+#define SHA1HANDSOFF		/* Copies data before messing with it. */
+
+#ifndef USE_MINGW
+#include <sys/cdefs.h>
+#endif
+#include <sys/types.h>
+#include <assert.h>
+#include <string.h>
+
+#include "sha1.h"
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#if !HAVE_SHA1_H
+
+#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
+
+/*
+ * blk0() and blk() perform the initial expand.
+ * I got the idea of expanding during the round function from SSLeay
+ */
+#if BYTE_ORDER == LITTLE_ENDIAN
+# define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \
+    |(rol(block->l[i],8)&0x00FF00FF))
+#else
+# define blk0(i) block->l[i]
+#endif
+#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
+    ^block->l[(i+2)&15]^block->l[i&15],1))
+
+/*
+ * (R0+R1), R2, R3, R4 are the different operations (rounds) used in SHA1
+ */
+#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
+#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
+#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
+
+typedef union {
+    u_char c[64];
+    u_int l[16];
+} CHAR64LONG16;
+
+/* old sparc64 gcc could not compile this */
+#undef SPARC64_GCC_WORKAROUND
+#if defined(__sparc64__) && defined(__GNUC__) && __GNUC__ < 3
+#define SPARC64_GCC_WORKAROUND
+#endif
+
+#ifdef SPARC64_GCC_WORKAROUND
+void do_R01(u_int32_t *a, u_int32_t *b, u_int32_t *c, u_int32_t *d, u_int32_t *e, CHAR64LONG16 *);
+void do_R2(u_int32_t *a, u_int32_t *b, u_int32_t *c, u_int32_t *d, u_int32_t *e, CHAR64LONG16 *);
+void do_R3(u_int32_t *a, u_int32_t *b, u_int32_t *c, u_int32_t *d, u_int32_t *e, CHAR64LONG16 *);
+void do_R4(u_int32_t *a, u_int32_t *b, u_int32_t *c, u_int32_t *d, u_int32_t *e, CHAR64LONG16 *);
+
+#define nR0(v,w,x,y,z,i) R0(*v,*w,*x,*y,*z,i)
+#define nR1(v,w,x,y,z,i) R1(*v,*w,*x,*y,*z,i)
+#define nR2(v,w,x,y,z,i) R2(*v,*w,*x,*y,*z,i)
+#define nR3(v,w,x,y,z,i) R3(*v,*w,*x,*y,*z,i)
+#define nR4(v,w,x,y,z,i) R4(*v,*w,*x,*y,*z,i)
+
+void
+do_R01(u_int32_t *a, u_int32_t *b, u_int32_t *c, u_int32_t *d, u_int32_t *e, CHAR64LONG16 *block)
+{
+    nR0(a,b,c,d,e, 0); nR0(e,a,b,c,d, 1); nR0(d,e,a,b,c, 2); nR0(c,d,e,a,b, 3);
+    nR0(b,c,d,e,a, 4); nR0(a,b,c,d,e, 5); nR0(e,a,b,c,d, 6); nR0(d,e,a,b,c, 7);
+    nR0(c,d,e,a,b, 8); nR0(b,c,d,e,a, 9); nR0(a,b,c,d,e,10); nR0(e,a,b,c,d,11);
+    nR0(d,e,a,b,c,12); nR0(c,d,e,a,b,13); nR0(b,c,d,e,a,14); nR0(a,b,c,d,e,15);
+    nR1(e,a,b,c,d,16); nR1(d,e,a,b,c,17); nR1(c,d,e,a,b,18); nR1(b,c,d,e,a,19);
+}
+
+void
+do_R2(u_int32_t *a, u_int32_t *b, u_int32_t *c, u_int32_t *d, u_int32_t *e, CHAR64LONG16 *block)
+{
+    nR2(a,b,c,d,e,20); nR2(e,a,b,c,d,21); nR2(d,e,a,b,c,22); nR2(c,d,e,a,b,23);
+    nR2(b,c,d,e,a,24); nR2(a,b,c,d,e,25); nR2(e,a,b,c,d,26); nR2(d,e,a,b,c,27);
+    nR2(c,d,e,a,b,28); nR2(b,c,d,e,a,29); nR2(a,b,c,d,e,30); nR2(e,a,b,c,d,31);
+    nR2(d,e,a,b,c,32); nR2(c,d,e,a,b,33); nR2(b,c,d,e,a,34); nR2(a,b,c,d,e,35);
+    nR2(e,a,b,c,d,36); nR2(d,e,a,b,c,37); nR2(c,d,e,a,b,38); nR2(b,c,d,e,a,39);
+}
+
+void
+do_R3(u_int32_t *a, u_int32_t *b, u_int32_t *c, u_int32_t *d, u_int32_t *e, CHAR64LONG16 *block)
+{
+    nR3(a,b,c,d,e,40); nR3(e,a,b,c,d,41); nR3(d,e,a,b,c,42); nR3(c,d,e,a,b,43);
+    nR3(b,c,d,e,a,44); nR3(a,b,c,d,e,45); nR3(e,a,b,c,d,46); nR3(d,e,a,b,c,47);
+    nR3(c,d,e,a,b,48); nR3(b,c,d,e,a,49); nR3(a,b,c,d,e,50); nR3(e,a,b,c,d,51);
+    nR3(d,e,a,b,c,52); nR3(c,d,e,a,b,53); nR3(b,c,d,e,a,54); nR3(a,b,c,d,e,55);
+    nR3(e,a,b,c,d,56); nR3(d,e,a,b,c,57); nR3(c,d,e,a,b,58); nR3(b,c,d,e,a,59);
+}
+
+void
+do_R4(u_int32_t *a, u_int32_t *b, u_int32_t *c, u_int32_t *d, u_int32_t *e, CHAR64LONG16 *block)
+{
+    nR4(a,b,c,d,e,60); nR4(e,a,b,c,d,61); nR4(d,e,a,b,c,62); nR4(c,d,e,a,b,63);
+    nR4(b,c,d,e,a,64); nR4(a,b,c,d,e,65); nR4(e,a,b,c,d,66); nR4(d,e,a,b,c,67);
+    nR4(c,d,e,a,b,68); nR4(b,c,d,e,a,69); nR4(a,b,c,d,e,70); nR4(e,a,b,c,d,71);
+    nR4(d,e,a,b,c,72); nR4(c,d,e,a,b,73); nR4(b,c,d,e,a,74); nR4(a,b,c,d,e,75);
+    nR4(e,a,b,c,d,76); nR4(d,e,a,b,c,77); nR4(c,d,e,a,b,78); nR4(b,c,d,e,a,79);
+}
+#endif
+
+/*
+ * Hash a single 512-bit block. This is the core of the algorithm.
+ */
+void SHA1Transform(state, buffer)
+    u_int32_t state[5];
+    const u_char buffer[64];
+{
+    u_int32_t a, b, c, d, e;
+    CHAR64LONG16 *block;
+
+#ifdef SHA1HANDSOFF
+    CHAR64LONG16 workspace;
+#endif
+
+    assert(buffer != 0);
+    assert(state != 0);
+
+#ifdef SHA1HANDSOFF
+    block = &workspace;
+    (void)memcpy(block, buffer, 64);
+#else
+    block = (CHAR64LONG16 *)(void *)buffer;
+#endif
+
+    /* Copy context->state[] to working vars */
+    a = state[0];
+    b = state[1];
+    c = state[2];
+    d = state[3];
+    e = state[4];
+
+#ifdef SPARC64_GCC_WORKAROUND
+    do_R01(&a, &b, &c, &d, &e, block);
+    do_R2(&a, &b, &c, &d, &e, block);
+    do_R3(&a, &b, &c, &d, &e, block);
+    do_R4(&a, &b, &c, &d, &e, block);
+#else
+    /* 4 rounds of 20 operations each. Loop unrolled. */
+    R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
+    R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
+    R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
+    R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
+    R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
+    R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
+    R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
+    R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
+    R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
+    R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
+    R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
+    R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
+    R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
+    R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
+    R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
+    R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
+    R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
+    R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
+    R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
+    R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
+#endif
+
+    /* Add the working vars back into context.state[] */
+    state[0] += a;
+    state[1] += b;
+    state[2] += c;
+    state[3] += d;
+    state[4] += e;
+
+    /* Wipe variables */
+    a = b = c = d = e = 0;
+}
+
+
+/*
+ * SHA1Init - Initialize new context
+ */
+void SHA1Init(context)
+    SHA1_CTX *context;
+{
+
+    assert(context != 0);
+
+    /* SHA1 initialization constants */
+    context->state[0] = 0x67452301;
+    context->state[1] = 0xEFCDAB89;
+    context->state[2] = 0x98BADCFE;
+    context->state[3] = 0x10325476;
+    context->state[4] = 0xC3D2E1F0;
+    context->count[0] = context->count[1] = 0;
+}
+
+
+/*
+ * Run your data through this.
+ */
+void SHA1Update(context, data, len)
+    SHA1_CTX *context;
+    const u_char *data;
+    u_int len;
+{
+    u_int i, j;
+
+    assert(context != 0);
+    assert(data != 0);
+
+    j = context->count[0];
+    if ((context->count[0] += len << 3) < j)
+	context->count[1] += (len>>29)+1;
+    j = (j >> 3) & 63;
+    if ((j + len) > 63) {
+	(void)memcpy(&context->buffer[j], data, (i = 64-j));
+	SHA1Transform(context->state, context->buffer);
+	for ( ; i + 63 < len; i += 64)
+	    SHA1Transform(context->state, &data[i]);
+	j = 0;
+    } else {
+	i = 0;
+    }
+    (void)memcpy(&context->buffer[j], &data[i], len - i);
+}
+
+
+/*
+ * Add padding and return the message digest.
+ */
+void SHA1Final(digest, context)
+    u_char digest[20];
+    SHA1_CTX* context;
+{
+    u_int i;
+    u_char finalcount[8];
+
+    assert(digest != 0);
+    assert(context != 0);
+
+    for (i = 0; i < 8; i++) {
+	finalcount[i] = (u_char)((context->count[(i >= 4 ? 0 : 1)]
+	 >> ((3-(i & 3)) * 8) ) & 255);	 /* Endian independent */
+    }
+    SHA1Update(context, (const u_char *)"\200", 1);
+    while ((context->count[0] & 504) != 448)
+	SHA1Update(context, (const u_char *)"\0", 1);
+    SHA1Update(context, finalcount, 8);  /* Should cause a SHA1Transform() */
+
+    if (digest) {
+	for (i = 0; i < 20; i++)
+	    digest[i] = (u_char)
+		((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
+    }
+}
+
+#endif /* HAVE_SHA1_H */
diff --git a/extras/ext4_utils/sha1.h b/extras/ext4_utils/sha1.h
new file mode 100644
index 0000000..9a8f7e3
--- /dev/null
+++ b/extras/ext4_utils/sha1.h
@@ -0,0 +1,43 @@
+/*	$NetBSD: sha1.h,v 1.13 2005/12/26 18:41:36 perry Exp $	*/
+
+/*
+ * SHA-1 in C
+ * By Steve Reid <steve@edmweb.com>
+ * 100% Public Domain
+ */
+
+#ifndef _SYS_SHA1_H_
+#define	_SYS_SHA1_H_
+
+#include <sys/types.h>
+#include <stdint.h>
+
+#ifdef USE_MINGW
+typedef unsigned char u_char;
+typedef unsigned int uint32_t;
+typedef unsigned int u_int32_t;
+typedef unsigned int u_int;
+
+#define __BEGIN_DECLS
+#define __END_DECLS
+#else
+#include <sys/cdefs.h>
+#endif
+
+#define SHA1_DIGEST_LENGTH		20
+#define SHA1_DIGEST_STRING_LENGTH	41
+
+typedef struct {
+	uint32_t state[5];
+	uint32_t count[2];
+	u_char buffer[64];
+} SHA1_CTX;
+
+__BEGIN_DECLS
+void	SHA1Transform(uint32_t[5], const u_char[64]);
+void	SHA1Init(SHA1_CTX *);
+void	SHA1Update(SHA1_CTX *, const u_char *, u_int);
+void	SHA1Final(u_char[SHA1_DIGEST_LENGTH], SHA1_CTX *);
+__END_DECLS
+
+#endif /* _SYS_SHA1_H_ */
diff --git a/extras/ext4_utils/test_ext4fixup b/extras/ext4_utils/test_ext4fixup
new file mode 100755
index 0000000..a920a59
--- /dev/null
+++ b/extras/ext4_utils/test_ext4fixup
@@ -0,0 +1,71 @@
+#!/bin/bash
+
+typeset -i I ITERATIONS PHASE LOC COUNT MAXCOUNT
+
+ME=`basename $0`
+
+if [ "$#" -ne 3 ]
+then
+  echo "$ME: Usage: $ME <iterations> <maxcount> <filesystem_image>" >&2
+  exit 1;
+fi
+
+ITERATIONS="$1"
+MAXCOUNT="$2"
+ORIG_FS_IMAGE="$3"
+FIXED_FS_IMAGE="/tmp/fixedfsimage.$$"
+NEW_FS_IMAGE="/tmp/newfsimage.$$"
+
+if [ ! -f "$ORIG_FS_IMAGE" ]
+then
+  echo "$ME: Filesystem image $NEW_FS_IMAGE does not exist" >&2
+  exit 1
+fi
+
+trap "rm -f $NEW_FS_IMAGE $FIXED_FS_IMAGE" 0 1 2 3 15
+
+rm -f "$NEW_FS_IMAGE" "$FIXED_FS_IMAGE"
+
+# Create the fixed image to compare against
+cp "$ORIG_FS_IMAGE" "$FIXED_FS_IMAGE"
+ext4fixup "$FIXED_FS_IMAGE"
+
+if [ "$?" -ne 0 ]
+then
+  echo "$ME: ext4fixup failed!\n"
+  exit 1
+fi
+
+I=0
+while [ "$I" -lt "$ITERATIONS" ]
+do
+  # There is also a phase 4, which is writing out the updated superblocks and
+  # block group descriptors.  Test the with a separate script.
+  let PHASE="$RANDOM"%3         # 0 to 2
+  let PHASE++                   # 1 to 3
+  let LOC="$RANDOM"%2           # 0 to 1
+  let LOC++                     # 1 to 2
+  let COUNT="$RANDOM"%"$MAXCOUNT"
+
+  # Make a copy of the original image to fixup
+  cp "$ORIG_FS_IMAGE" "$NEW_FS_IMAGE"
+
+  # Run the fixup tool, but die partway through to see if we can recover
+  ext4fixup -d "$PHASE,$LOC,$COUNT" "$NEW_FS_IMAGE" 2>/dev/null
+ 
+  # run it again without -d to have it finish the job
+  ext4fixup "$NEW_FS_IMAGE"
+
+  if cmp "$FIXED_FS_IMAGE" "$NEW_FS_IMAGE"
+  then
+    :
+  else
+    echo "$ME: test failed with parameters $PHASE, $LOC, $COUNT"
+    exit 1
+  fi
+
+  rm -f "$NEW_FS_IMAGE"
+
+  let I++
+done
+
diff --git a/extras/ext4_utils/uuid.c b/extras/ext4_utils/uuid.c
new file mode 100644
index 0000000..33d2494
--- /dev/null
+++ b/extras/ext4_utils/uuid.c
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string.h>
+
+#ifdef USE_MINGW
+#include <winsock2.h>
+#else
+#include <arpa/inet.h>
+#endif
+
+#include "ext4_utils.h"
+#include "sha1.h"
+#include "uuid.h"
+
+/* Definition from RFC-4122 */
+struct uuid {
+	u32 time_low;
+	u16 time_mid;
+	u16 time_hi_and_version;
+	u8 clk_seq_hi_res;
+	u8 clk_seq_low;
+	u16 node0_1;
+	u32 node2_5;
+};
+
+static void sha1_hash(const char *namespace, const char *name,
+	unsigned char sha1[SHA1_DIGEST_LENGTH])
+{
+	SHA1_CTX ctx;
+	SHA1Init(&ctx);
+	SHA1Update(&ctx, (const u8*)namespace, strlen(namespace));
+	SHA1Update(&ctx, (const u8*)name, strlen(name));
+	SHA1Final(sha1, &ctx);
+}
+
+void generate_uuid(const char *namespace, const char *name, u8 result[16])
+{
+	unsigned char sha1[SHA1_DIGEST_LENGTH];
+	struct uuid *uuid = (struct uuid *)result;
+
+	sha1_hash(namespace, name, (unsigned char*)sha1);
+	memcpy(uuid, sha1, sizeof(struct uuid));
+
+	uuid->time_low = ntohl(uuid->time_low);
+	uuid->time_mid = ntohs(uuid->time_mid);
+	uuid->time_hi_and_version = ntohs(uuid->time_hi_and_version);
+	uuid->time_hi_and_version &= 0x0FFF;
+	uuid->time_hi_and_version |= (5 << 12);
+	uuid->clk_seq_hi_res &= ~(1 << 6);
+	uuid->clk_seq_hi_res |= 1 << 7;
+}
diff --git a/extras/ext4_utils/uuid.h b/extras/ext4_utils/uuid.h
new file mode 100644
index 0000000..ff1b438
--- /dev/null
+++ b/extras/ext4_utils/uuid.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UUID_H_
+#define _UUID_H_
+
+#include "ext4_utils.h"
+
+void generate_uuid(const char *namespace, const char *name, u8 result[16]);
+
+#endif
diff --git a/extras/ext4_utils/wipe.c b/extras/ext4_utils/wipe.c
new file mode 100644
index 0000000..2b111d6
--- /dev/null
+++ b/extras/ext4_utils/wipe.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ext4_utils.h"
+#include "wipe.h"
+
+#if defined(__linux__)
+
+#include <linux/fs.h>
+#include <sys/ioctl.h>
+
+#ifndef BLKDISCARD
+#define BLKDISCARD _IO(0x12,119)
+#endif
+
+#ifndef BLKSECDISCARD
+#define BLKSECDISCARD _IO(0x12,125)
+#endif
+
+int wipe_block_device(int fd, s64 len)
+{
+	u64 range[2];
+	int ret;
+
+#ifndef BUILD_EUREKA
+	range[0] = 0;
+	range[1] = len;
+	ret = ioctl(fd, BLKSECDISCARD, &range);
+	if (ret >= 0)
+		return 0;
+	warn("Wipe via secure discard failed, try discard instead\n");
+#endif
+	range[0] = 0;
+	range[1] = len;
+	ret = ioctl(fd, BLKDISCARD, &range);
+	if (ret < 0) {
+		warn("Discard failed\n");
+		return 1;
+	}
+	return 0;
+}
+#else
+int wipe_block_device(int fd, s64 len)
+{
+	error("wipe not supported on non-linux platforms");
+	return 1;
+}
+#endif
+
diff --git a/extras/ext4_utils/wipe.h b/extras/ext4_utils/wipe.h
new file mode 100644
index 0000000..814877d
--- /dev/null
+++ b/extras/ext4_utils/wipe.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _WIPE_H_
+#define _WIPE_H_
+
+int wipe_block_device(int fd, s64 len);
+
+#endif
diff --git a/extras/ext4_utils/xattr.h b/extras/ext4_utils/xattr.h
new file mode 100644
index 0000000..2c6d9cc
--- /dev/null
+++ b/extras/ext4_utils/xattr.h
@@ -0,0 +1,23 @@
+#include <sys/types.h>
+
+#define EXT4_XATTR_MAGIC 0xEA020000
+#define EXT4_XATTR_INDEX_SECURITY 6
+
+struct ext4_xattr_entry {
+    __u8 e_name_len;
+    __u8 e_name_index;
+    __le16 e_value_offs;
+    __le32 e_value_block;
+    __le32 e_value_size;
+    __le32 e_hash;
+    char e_name[0];
+};
+
+#define EXT4_XATTR_PAD_BITS 2
+#define EXT4_XATTR_PAD (1<<EXT4_XATTR_PAD_BITS)
+#define EXT4_XATTR_ROUND (EXT4_XATTR_PAD-1)
+#define EXT4_XATTR_LEN(name_len) \
+    (((name_len) + EXT4_XATTR_ROUND + \
+    sizeof(struct ext4_xattr_entry)) & ~EXT4_XATTR_ROUND)
+#define EXT4_XATTR_SIZE(size) \
+    (((size) + EXT4_XATTR_ROUND) & ~EXT4_XATTR_ROUND)
diff --git a/toolbox/Android.mk b/toolbox/Android.mk
new file mode 100644
index 0000000..7cb2620
--- /dev/null
+++ b/toolbox/Android.mk
@@ -0,0 +1,225 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+TOOLS := \
+	ls \
+	mount \
+	cat \
+	ps \
+	kill \
+	ln \
+	insmod \
+	rmmod \
+	lsmod \
+	setconsole \
+	mkdir \
+	rmdir \
+	reboot \
+	sendevent \
+	date \
+	wipe \
+	sync \
+	umount \
+	start \
+	stop \
+	notify \
+	cmp \
+	route \
+	hd \
+	getprop \
+	setprop \
+	watchprops \
+	log \
+	sleep \
+	renice \
+	printenv \
+	smd \
+	chmod \
+	chown \
+	newfs_msdos \
+	netstat \
+	mv \
+	schedtop \
+	top \
+	iftop \
+	id \
+	uptime \
+	vmstat \
+	nandread \
+	ionice \
+	touch \
+	lsof \
+	mknod \
+	sntpd \
+	exists \
+	sudo \
+	devmem \
+	getevent
+
+ifeq ($(HAVE_SELINUX),true)
+
+TOOLS += \
+	getenforce \
+	setenforce \
+	chcon \
+	restorecon \
+	runcon \
+	getsebool \
+	setsebool \
+	load_policy
+
+endif
+
+
+ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
+TOOLS += r
+endif
+
+LOCAL_SRC_FILES:= \
+	dynarray.c \
+	toolbox.c \
+	$(patsubst %,%.c,$(TOOLS))
+
+ifeq ($(TARGET_USE_SYSTEM_LIBS),true)
+LOCAL_CFLAGS += -Dlint
+endif
+
+LOCAL_SHARED_LIBRARIES := libcutils
+
+ifeq ($(BUILD_EUREKA),true)
+LOCAL_CFLAGS += -include $(HOST_OUT_COMMON_INTERMEDIATES)/have_selinux.h
+endif # BUILD_EUREKA
+ifeq ($(HAVE_SELINUX),true)
+LOCAL_CFLAGS += -DHAVE_SELINUX
+LOCAL_SHARED_LIBRARIES += libselinux
+LOCAL_C_INCLUDES += external/libselinux/include
+endif
+
+LOCAL_MODULE := toolbox
+ifdef SBIN_TOOLBOX
+LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT_SBIN)
+endif
+
+# Including this will define $(intermediates).
+#
+include $(BUILD_EXECUTABLE)
+
+$(LOCAL_PATH)/toolbox.c: $(intermediates)/tools.h
+$(intermediates)/tools.h:  $(HOST_OUT_COMMON_INTERMEDIATES)/have_selinux.h
+
+TOOLS_H := $(intermediates)/tools.h
+$(TOOLS_H): PRIVATE_TOOLS := $(TOOLS)
+$(TOOLS_H): PRIVATE_CUSTOM_TOOL = echo "/* file generated automatically */" > $@ ; for t in $(PRIVATE_TOOLS) ; do echo "TOOL($$t)" >> $@ ; done
+$(TOOLS_H): $(LOCAL_PATH)/Android.mk
+$(TOOLS_H):
+	$(transform-generated-source)
+
+# Make #!/system/bin/toolbox launchers for each tool.
+#
+SYMLINKS := $(addprefix $(TARGET_OUT)/bin/,$(TOOLS))
+ifdef SBIN_TOOLBOX
+$(SYMLINKS): TOOLBOX_BINARY := /sbin/$(LOCAL_MODULE)
+else
+$(SYMLINKS): TOOLBOX_BINARY := $(LOCAL_MODULE)
+endif
+$(SYMLINKS): $(LOCAL_INSTALLED_MODULE) $(LOCAL_PATH)/Android.mk
+	@echo "Symlink: $@ -> $(TOOLBOX_BINARY)"
+	@mkdir -p $(dir $@)
+	@rm -rf $@
+	$(hide) ln -sf $(TOOLBOX_BINARY) $@
+
+# Create one module that will wrap all the symlinks.  This module requires the
+# toolbox module, then adds an explicit dependency on all symlinks.
+include $(CLEAR_VARS)
+ifeq ($(BUILD_EUREKA),true)
+LOCAL_CFLAGS += -include $(HOST_OUT_COMMON_INTERMEDIATES)/have_selinux.h
+endif # BUILD_EUREKA
+LOCAL_MODULE := toolbox_symlinks
+LOCAL_MODULE_TAGS := optional
+LOCAL_REQUIRED_MODULES := toolbox
+include $(BUILD_PHONY_PACKAGE)
+$(LOCAL_BUILT_MODULE): $(SYMLINKS)
+
+# create root only toolbox_root
+include $(CLEAR_VARS)
+
+TOOLS_ROOT := \
+	dd
+
+LOCAL_SRC_FILES:= \
+	dynarray.c \
+	toolbox.c \
+	$(patsubst %,%.c,$(TOOLS_ROOT))
+
+LOCAL_MODULE := toolbox_root
+ifdef SBIN_TOOLBOX
+LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT_SBIN)
+endif
+
+ifeq ($(TARGET_USE_SYSTEM_LIBS),true)
+LOCAL_CFLAGS += -Dlint
+endif
+
+LOCAL_SHARED_LIBRARIES := libcutils
+
+ifeq ($(BUILD_EUREKA),true)
+LOCAL_CFLAGS += -include $(HOST_OUT_COMMON_INTERMEDIATES)/have_selinux.h
+endif # BUILD_EUREKA
+ifeq ($(HAVE_SELINUX),true)
+LOCAL_CFLAGS += -DHAVE_SELINUX
+LOCAL_SHARED_LIBRARIES += libselinux
+LOCAL_C_INCLUDES += external/libselinux/include
+endif
+
+include $(BUILD_EXECUTABLE)
+
+$(LOCAL_PATH)/toolbox.c: $(intermediates)/tools.h
+$(intermediates)/tools.h:  $(HOST_OUT_COMMON_INTERMEDIATES)/have_selinux.h
+
+TOOLS_H := $(intermediates)/tools.h
+$(TOOLS_H): PRIVATE_TOOLS := $(TOOLS_ROOT)
+$(TOOLS_H): PRIVATE_CUSTOM_TOOL = echo "/* file generated automatically */" > $@ ; for t in $(PRIVATE_TOOLS) ; do echo "TOOL($$t)" >> $@ ; done
+$(TOOLS_H): $(LOCAL_PATH)/Android.mk
+$(TOOLS_H):
+	$(transform-generated-source)
+
+SYMLINKS_ROOT := $(addprefix $(TARGET_OUT)/bin/,$(TOOLS_ROOT))
+ifdef SBIN_TOOLBOX
+$(SYMLINKS_ROOT): TOOLBOX_BINARY := /sbin/$(LOCAL_MODULE)
+else
+$(SYMLINKS_ROOT): TOOLBOX_BINARY := $(LOCAL_MODULE)
+endif
+$(SYMLINKS_ROOT): $(LOCAL_INSTALLED_MODULE) $(LOCAL_PATH)/Android.mk
+	@echo "Symlink: $@ -> $(TOOLBOX_BINARY)"
+	@mkdir -p $(dir $@)
+	@rm -rf $@
+	$(hide) ln -sf $(TOOLBOX_BINARY) $@
+
+# Create one module that will wrap all the symlinks.  This module requires the
+# toolbox module, then adds an explicit dependency on all symlinks.
+include $(CLEAR_VARS)
+ifeq ($(BUILD_EUREKA),true)
+LOCAL_CFLAGS += -include $(HOST_OUT_COMMON_INTERMEDIATES)/have_selinux.h
+endif # BUILD_EUREKA
+LOCAL_MODULE := toolbox_root_symlinks
+LOCAL_MODULE_TAGS := optional
+LOCAL_REQUIRED_MODULES := toolbox_root
+include $(BUILD_PHONY_PACKAGE)
+$(LOCAL_BUILT_MODULE): $(SYMLINKS_ROOT)
+
+ALL_DEFAULT_INSTALLED_MODULES += toolbox_symlinks toolbox_root_symlinks
+
+# We need this so that the installed files could be picked up based on the
+# local module name
+ALL_MODULES.$(LOCAL_MODULE).INSTALLED := \
+    $(ALL_MODULES.$(LOCAL_MODULE).INSTALLED) toolbox_symlinks toolbox_root_symlinks
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := sntpd_unittest.c
+LOCAL_CFLAGS += -UNDEBUG
+LOCAL_MODULE := sntpd_unittest
+LOCAL_SHARED_LIBRARIES := libcutils
+LOCAL_C_INCLUDES := $(KERNEL_HEADERS)
+LOCAL_MODULE_TAGS := optional
+include $(BUILD_EXECUTABLE)
+
diff --git a/toolbox/MODULE_LICENSE_BSD b/toolbox/MODULE_LICENSE_BSD
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/toolbox/MODULE_LICENSE_BSD
diff --git a/toolbox/NOTICE b/toolbox/NOTICE
new file mode 100644
index 0000000..895b49a
--- /dev/null
+++ b/toolbox/NOTICE
@@ -0,0 +1,193 @@
+
+Copyright (c) 2010, The Android Open Source Project.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+ * Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in
+   the documentation and/or other materials provided with the 
+   distribution.
+ * Neither the name of The Android Open Source Project nor the names
+   of its contributors may be used to endorse or promote products
+   derived from this software without specific prior written
+   permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
+AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+
+Copyright (c) 2009, The Android Open Source Project.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+ * Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in
+   the documentation and/or other materials provided with the 
+   distribution.
+ * Neither the name of The Android Open Source Project nor the names
+   of its contributors may be used to endorse or promote products
+   derived from this software without specific prior written
+   permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
+AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+
+Copyright (c) 2008, The Android Open Source Project.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+ * Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in
+   the documentation and/or other materials provided with the 
+   distribution.
+ * Neither the name of The Android Open Source Project nor the names
+   of its contributors may be used to endorse or promote products
+   derived from this software without specific prior written
+   permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
+AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+
+Copyright (c) 1998 Robert Nordier
+Copyright (c) 1989, 1993
+     The Regents of the University of California.  All rights reserved.
+
+This code is derived from software contributed to Berkeley by
+Kevin Fall.
+This code is derived from software contributed to Berkeley by
+Keith Muller of the University of California, San Diego and Lance
+Visser of Convex Computer Corporation.
+This code is derived from software contributed to Berkeley by
+Mike Muuss.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+3. Neither the name of the University nor the names of its contributors
+   may be used to endorse or promote products derived from this software
+   without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+
+ Copyright (c) 1989, 1993
+	The Regents of the University of California.  All rights reserved.
+
+ This code is derived from software contributed to Berkeley by
+ Kevin Fall.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University nor the names of its contributors
+    may be used to endorse or promote products derived from this software
+    without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ SUCH DAMAGE.
+
+
+ Copyright (c) 1991, 1993, 1994
+	The Regents of the University of California.  All rights reserved.
+
+ This code is derived from software contributed to Berkeley by
+ Keith Muller of the University of California, San Diego and Lance
+ Visser of Convex Computer Corporation.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University nor the names of its contributors
+    may be used to endorse or promote products derived from this software
+    without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ SUCH DAMAGE.
+
diff --git a/toolbox/alarm.c b/toolbox/alarm.c
new file mode 100644
index 0000000..9bd58aa
--- /dev/null
+++ b/toolbox/alarm.c
@@ -0,0 +1,190 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <asm/ioctl.h>
+//#include <linux/rtc.h>
+#include <linux/android_alarm.h>
+
+int alarm_main(int argc, char *argv[])
+{
+	int c;
+    int res;
+	struct tm tm;
+	time_t t;
+	struct timespec ts;
+//	struct rtc_time rtc_time;
+	char strbuf[26];
+	int afd;
+	int nfd;
+//	struct timeval timeout = { 0, 0 };
+    int wait = 0;
+	fd_set rfds;
+	const char wake_lock_id[] = "alarm_test"; 
+	int waitalarmmask = 0;
+
+    int useutc = 0;
+	android_alarm_type_t alarmtype_low = ANDROID_ALARM_RTC_WAKEUP;
+	android_alarm_type_t alarmtype_high = ANDROID_ALARM_RTC_WAKEUP;
+	android_alarm_type_t alarmtype = 0;
+
+    do {
+        //c = getopt(argc, argv, "uw:");
+        c = getopt(argc, argv, "uwat:");
+        if (c == EOF)
+            break;
+        switch (c) {
+        case 'u':
+            useutc = 1;
+            break;
+		case 't':
+			alarmtype_low = alarmtype_high = strtol(optarg, NULL, 0);
+			break;
+		case 'a':
+			alarmtype_low = ANDROID_ALARM_RTC_WAKEUP;
+			alarmtype_high = ANDROID_ALARM_TYPE_COUNT - 1;
+			break;
+        case 'w':
+            //timeout.tv_sec = strtol(optarg, NULL, 0);
+            wait = 1;
+            break;
+        case '?':
+            fprintf(stderr, "%s: invalid option -%c\n",
+                argv[0], optopt);
+            exit(1);
+        }
+    } while (1);
+    if(optind + 2 < argc) {
+        fprintf(stderr,"%s [-uwa] [-t type] [seconds]\n", argv[0]);
+        return 1;
+    }
+
+    afd = open("/dev/alarm", O_RDWR);
+    if(afd < 0) {
+        fprintf(stderr, "Unable to open rtc: %s\n", strerror(errno));
+        return 1;
+    }
+
+    if(optind == argc) {
+		for(alarmtype = alarmtype_low; alarmtype <= alarmtype_high; alarmtype++) {
+			waitalarmmask |= 1U << alarmtype;
+		}
+#if 0
+        res = ioctl(fd, RTC_ALM_READ, &tm);
+        if(res < 0) {
+            fprintf(stderr, "Unable to read alarm: %s\n", strerror(errno));
+			return 1;
+        }
+#endif
+#if 0
+		t = timegm(&tm);
+        if(useutc)
+            gmtime_r(&t, &tm);
+        else
+            localtime_r(&t, &tm);
+#endif
+#if 0
+        asctime_r(&tm, strbuf);
+        printf("%s", strbuf);
+#endif
+    }
+    else if(optind + 1 == argc) {
+#if 0
+        res = ioctl(fd, RTC_RD_TIME, &tm);
+        if(res < 0) {
+            fprintf(stderr, "Unable to set alarm: %s\n", strerror(errno));
+			return 1;
+        }
+        asctime_r(&tm, strbuf);
+        printf("Now: %s", strbuf);
+        time(&tv.tv_sec);
+#endif
+#if 0
+		time(&ts.tv_sec);
+		ts.tv_nsec = 0;
+		
+        //strptime(argv[optind], NULL, &tm);
+        //tv.tv_sec = mktime(&tm);
+        //tv.tv_usec = 0;
+#endif
+		for(alarmtype = alarmtype_low; alarmtype <= alarmtype_high; alarmtype++) {
+			waitalarmmask |= 1U << alarmtype;
+		    res = ioctl(afd, ANDROID_ALARM_GET_TIME(alarmtype), &ts);
+		    if(res < 0) {
+		        fprintf(stderr, "Unable to get current time: %s\n", strerror(errno));
+				return 1;
+		    }
+		    ts.tv_sec += strtol(argv[optind], NULL, 0);
+		    //strtotimeval(argv[optind], &tv);
+			gmtime_r(&ts.tv_sec, &tm);
+		    printf("time %s -> %ld.%09ld\n", argv[optind], ts.tv_sec, ts.tv_nsec);
+		    asctime_r(&tm, strbuf);
+		    printf("Requested %s", strbuf);
+			
+		    res = ioctl(afd, ANDROID_ALARM_SET(alarmtype), &ts);
+		    if(res < 0) {
+		        fprintf(stderr, "Unable to set alarm: %s\n", strerror(errno));
+				return 1;
+		    }
+		}
+#if 0
+        res = ioctl(fd, RTC_ALM_SET, &tm);
+        if(res < 0) {
+            fprintf(stderr, "Unable to set alarm: %s\n", strerror(errno));
+			return 1;
+        }
+        res = ioctl(fd, RTC_AIE_ON);
+        if(res < 0) {
+            fprintf(stderr, "Unable to enable alarm: %s\n", strerror(errno));
+			return 1;
+        }
+#endif
+    }
+    else {
+        fprintf(stderr,"%s [-u] [date]\n", argv[0]);
+        return 1;
+    }
+
+	if(wait) {
+		while(waitalarmmask) {
+			printf("wait for alarm %x\n", waitalarmmask);
+			res = ioctl(afd, ANDROID_ALARM_WAIT);
+			if(res < 0) {
+				fprintf(stderr, "alarm wait failed\n");
+			}
+			printf("got alarm %x\n", res);
+			waitalarmmask &= ~res;
+			nfd = open("/sys/android_power/acquire_full_wake_lock", O_RDWR);
+			write(nfd, wake_lock_id, sizeof(wake_lock_id) - 1);
+			close(nfd);
+			//sleep(5);
+			nfd = open("/sys/android_power/release_wake_lock", O_RDWR);
+			write(nfd, wake_lock_id, sizeof(wake_lock_id) - 1);
+			close(nfd);
+		}
+		printf("done\n");
+	}
+#if 0	
+	FD_ZERO(&rfds);
+	FD_SET(fd, &rfds);
+	res = select(fd + 1, &rfds, NULL, NULL, &timeout);
+    if(res < 0) {
+        fprintf(stderr, "select failed: %s\n", strerror(errno));
+		return 1;
+    }
+	if(res > 0) {
+		int event;
+		read(fd, &event, sizeof(event));
+		fprintf(stderr, "got %x\n", event);
+	}
+	else {
+		fprintf(stderr, "timeout waiting for alarm\n");
+	}
+#endif
+
+    close(afd);
+
+    return 0;
+}
diff --git a/toolbox/cat.c b/toolbox/cat.c
new file mode 100644
index 0000000..6ac31f8
--- /dev/null
+++ b/toolbox/cat.c
@@ -0,0 +1,291 @@
+/* $NetBSD: cat.c,v 1.43 2004/01/04 03:31:28 jschauma Exp $	*/
+
+/*
+ * Copyright (c) 1989, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kevin Fall.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define CAT_BUFSIZ (4096)
+
+static int bflag, eflag, fflag, lflag, nflag, sflag, tflag, vflag;
+static int rval;
+static const char *filename;
+
+static void
+cook_buf(FILE *fp)
+{
+	int ch, gobble, line, prev;
+	int stdout_err = 0;
+
+	line = gobble = 0;
+	for (prev = '\n'; (ch = getc(fp)) != EOF; prev = ch) {
+		if (prev == '\n') {
+			if (ch == '\n') {
+				if (sflag) {
+					if (!gobble && putchar(ch) == EOF)
+						break;
+					gobble = 1;
+					continue;
+				}
+				if (nflag) {
+					if (!bflag) {
+						if (fprintf(stdout,
+						    "%6d\t", ++line) < 0) {
+							stdout_err++;
+							break;
+						}
+					} else if (eflag) {
+						if (fprintf(stdout,
+						    "%6s\t", "") < 0) {
+							stdout_err++;
+							break;
+						}
+					}
+				}
+			} else if (nflag) {
+				if (fprintf(stdout, "%6d\t", ++line) < 0) {
+					stdout_err++;
+					break;
+				}
+			}
+		}
+		gobble = 0;
+		if (ch == '\n') {
+			if (eflag)
+				if (putchar('$') == EOF)
+					break;
+		} else if (ch == '\t') {
+			if (tflag) {
+				if (putchar('^') == EOF || putchar('I') == EOF)
+					break;
+				continue;
+			}
+		} else if (vflag) {
+			if (!isascii(ch)) {
+				if (putchar('M') == EOF || putchar('-') == EOF)
+					break;
+				ch = (ch) & 0x7f;
+			}
+			if (iscntrl(ch)) {
+				if (putchar('^') == EOF ||
+				    putchar(ch == '\177' ? '?' :
+				    ch | 0100) == EOF)
+					break;
+				continue;
+			}
+		}
+		if (putchar(ch) == EOF)
+			break;
+	}
+	if (stdout_err) {
+		perror(filename);
+		rval = 1;
+	}
+}
+
+static void
+cook_args(char **argv)
+{
+	FILE *fp;
+
+	fp = stdin;
+	filename = "stdin";
+	do {
+		if (*argv) {
+			if (!strcmp(*argv, "-"))
+				fp = stdin;
+			else if ((fp = fopen(*argv,
+			    fflag ? "rf" : "r")) == NULL) {
+				perror("fopen");
+				rval = 1;
+				++argv;
+				continue;
+			}
+			filename = *argv++;
+		}
+		cook_buf(fp);
+		if (fp != stdin)
+			fclose(fp);
+	} while (*argv);
+}
+
+static void
+raw_cat(int rfd)
+{
+	static char *buf;
+	static char fb_buf[CAT_BUFSIZ];
+	static size_t bsize;
+
+	struct stat sbuf;
+	ssize_t nr, nw, off;
+	int wfd;
+
+	wfd = fileno(stdout);
+	if (buf == NULL) {
+		if (fstat(wfd, &sbuf) == 0) {
+			bsize = sbuf.st_blksize > CAT_BUFSIZ ?
+			    sbuf.st_blksize : CAT_BUFSIZ;
+			buf = malloc(bsize);
+		}
+		if (buf == NULL) {
+			buf = fb_buf;
+			bsize = CAT_BUFSIZ;
+		}
+	}
+	while ((nr = read(rfd, buf, bsize)) > 0)
+		for (off = 0; nr; nr -= nw, off += nw)
+			if ((nw = write(wfd, buf + off, (size_t)nr)) < 0)
+			{
+				perror("write");
+				exit(EXIT_FAILURE);
+			}
+	if (nr < 0) {
+		fprintf(stderr,"%s: invalid length\n", filename);
+		rval = 1;
+	}
+}
+
+static void
+raw_args(char **argv)
+{
+	int fd;
+
+	fd = fileno(stdin);
+	filename = "stdin";
+	do {
+		if (*argv) {
+			if (!strcmp(*argv, "-"))
+				fd = fileno(stdin);
+			else if (fflag) {
+				struct stat st;
+				fd = open(*argv, O_RDONLY|O_NONBLOCK, 0);
+				if (fd < 0)
+					goto skip;
+
+				if (fstat(fd, &st) == -1) {
+					close(fd);
+					goto skip;
+				}
+				if (!S_ISREG(st.st_mode)) {
+					close(fd);
+					errno = EINVAL;
+					goto skipnomsg;
+				}
+			}
+			else if ((fd = open(*argv, O_RDONLY, 0)) < 0) {
+skip:
+				perror(*argv);
+skipnomsg:
+				rval = 1;
+				++argv;
+				continue;
+			}
+			filename = *argv++;
+		}
+		raw_cat(fd);
+		if (fd != fileno(stdin))
+			close(fd);
+	} while (*argv);
+}
+
+int
+cat_main(int argc, char *argv[])
+{
+	int ch;
+	struct flock stdout_lock;
+
+	while ((ch = getopt(argc, argv, "beflnstv")) != -1)
+		switch (ch) {
+		case 'b':
+			bflag = nflag = 1;	/* -b implies -n */
+			break;
+		case 'e':
+			eflag = vflag = 1;	/* -e implies -v */
+			break;
+		case 'f':
+			fflag = 1;
+			break;
+		case 'l':
+			lflag = 1;
+			break;
+		case 'n':
+			nflag = 1;
+			break;
+		case 's':
+			sflag = 1;
+			break;
+		case 't':
+			tflag = vflag = 1;	/* -t implies -v */
+			break;
+		case 'v':
+			vflag = 1;
+			break;
+		default:
+		case '?':
+			fprintf(stderr,
+				"usage: cat [-beflnstv] [-] [file ...]\n");
+			exit(EXIT_FAILURE);
+		}
+	argv += optind;
+
+	if (lflag) {
+		stdout_lock.l_len = 0;
+		stdout_lock.l_start = 0;
+		stdout_lock.l_type = F_WRLCK;
+		stdout_lock.l_whence = SEEK_SET;
+		if (fcntl(STDOUT_FILENO, F_SETLKW, &stdout_lock) == -1)
+		{
+			perror("fcntl");
+			exit(EXIT_FAILURE);
+		}
+	}
+
+	if (bflag || eflag || nflag || sflag || tflag || vflag)
+		cook_args(argv);
+	else
+		raw_args(argv);
+	if (fclose(stdout))
+	{
+		perror("fclose");
+		exit(EXIT_FAILURE);
+	}
+	exit(rval);
+}
diff --git a/toolbox/chcon.c b/toolbox/chcon.c
new file mode 100644
index 0000000..d594b9b
--- /dev/null
+++ b/toolbox/chcon.c
@@ -0,0 +1,25 @@
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <selinux/selinux.h>
+
+int chcon_main(int argc, char **argv)
+{
+    int rc, i;
+
+    if (argc < 3) {
+        fprintf(stderr, "usage:  %s context path...\n", argv[0]);
+        exit(1);
+    }
+
+    for (i = 2; i < argc; i++) {
+        rc = setfilecon(argv[i], argv[1]);
+        if (rc < 0) {
+            fprintf(stderr, "%s:  Could not label %s with %s:  %s\n",
+                    argv[0], argv[i], argv[1], strerror(errno));
+            exit(2);
+        }
+    }
+    exit(0);
+}
diff --git a/toolbox/chmod.c b/toolbox/chmod.c
new file mode 100644
index 0000000..460ccdd
--- /dev/null
+++ b/toolbox/chmod.c
@@ -0,0 +1,104 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <errno.h>
+#if HAVE_GLIBC
+#include <limits.h>
+#else
+#include <sys/limits.h>
+#endif
+#include <sys/stat.h>
+
+#include <unistd.h>
+#include <time.h>
+
+void recurse_chmod(char* path, int mode)
+{
+    struct dirent *dp;
+    DIR *dir = opendir(path);
+    if (dir == NULL) {
+        // not a directory, carry on
+        return;
+    }
+    char *subpath = malloc(sizeof(char)*PATH_MAX);
+    int pathlen = strlen(path);
+
+    while ((dp = readdir(dir)) != NULL) {
+        if (strcmp(dp->d_name, ".") == 0 ||
+            strcmp(dp->d_name, "..") == 0) continue;
+
+        if (strlen(dp->d_name) + pathlen + 2/*NUL and slash*/ > PATH_MAX) {
+            fprintf(stderr, "Invalid path specified: too long\n");
+            exit(1);
+        }
+
+        strcpy(subpath, path);
+        strcat(subpath, "/");
+        strcat(subpath, dp->d_name);
+
+        if (chmod(subpath, mode) < 0) {
+            fprintf(stderr, "Unable to chmod %s: %s\n", subpath, strerror(errno));
+            exit(1);
+        }
+
+        recurse_chmod(subpath, mode);
+    }
+    free(subpath);
+    closedir(dir);
+}
+
+static int usage()
+{
+    fprintf(stderr, "Usage: chmod [OPTION] <MODE> <FILE>\n");
+    fprintf(stderr, "  -R, --recursive         change files and directories recursively\n");
+    fprintf(stderr, "  --help                  display this help and exit\n");
+
+    return 10;
+}
+
+int chmod_main(int argc, char **argv)
+{
+    int i;
+
+    if (argc < 3 || strcmp(argv[1], "--help") == 0) {
+        return usage();
+    }
+
+    int recursive = (strcmp(argv[1], "-R") == 0 ||
+                     strcmp(argv[1], "--recursive") == 0) ? 1 : 0;
+
+    if (recursive && argc < 4) {
+        return usage();
+    }
+
+    if (recursive) {
+        argc--;
+        argv++;
+    }
+
+    int mode = 0;
+    const char* s = argv[1];
+    while (*s) {
+        if (*s >= '0' && *s <= '7') {
+            mode = (mode<<3) | (*s-'0');
+        }
+        else {
+            fprintf(stderr, "Bad mode\n");
+            return 10;
+        }
+        s++;
+    }
+
+    for (i = 2; i < argc; i++) {
+        if (chmod(argv[i], mode) < 0) {
+            fprintf(stderr, "Unable to chmod %s: %s\n", argv[i], strerror(errno));
+            return 10;
+        }
+        if (recursive) {
+            recurse_chmod(argv[i], mode);
+        }
+    }
+    return 0;
+}
diff --git a/toolbox/chown.c b/toolbox/chown.c
new file mode 100644
index 0000000..92efee6
--- /dev/null
+++ b/toolbox/chown.c
@@ -0,0 +1,73 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <errno.h>
+#include <pwd.h>
+#include <grp.h>
+
+#include <unistd.h>
+#include <time.h>
+
+int chown_main(int argc, char **argv)
+{
+    int i;
+
+    if (argc < 3) {
+        fprintf(stderr, "Usage: chown <USER>[:GROUP] <FILE1> [FILE2] ...\n");
+        return 10;
+    }
+
+    // Copy argv[1] to 'user' so we can truncate it at the period
+    // if a group id specified.
+    char user[32];
+    char *group = NULL;
+    strncpy(user, argv[1], sizeof(user));
+    if ((group = strchr(user, ':')) != NULL) {
+        *group++ = '\0';
+    } else if ((group = strchr(user, '.')) != NULL) {
+        *group++ = '\0';
+    }
+
+    // Lookup uid (and gid if specified)
+    struct passwd *pw;
+    struct group *grp = NULL;
+    uid_t uid;
+    gid_t gid = -1; // passing -1 to chown preserves current group
+
+    pw = getpwnam(user);
+    if (pw != NULL) {
+        uid = pw->pw_uid;
+    } else {
+        char* endptr;
+        uid = (int) strtoul(user, &endptr, 0);
+        if (endptr == user) {  // no conversion
+          fprintf(stderr, "No such user '%s'\n", user);
+          return 10;
+        }
+    }
+
+    if (group != NULL) {
+        grp = getgrnam(group);
+        if (grp != NULL) {
+            gid = grp->gr_gid;
+        } else {
+            char* endptr;
+            gid = (int) strtoul(group, &endptr, 0);
+            if (endptr == group) {  // no conversion
+                fprintf(stderr, "No such group '%s'\n", group);
+                return 10;
+            }
+        }
+    }
+
+    for (i = 2; i < argc; i++) {
+        if (chown(argv[i], uid, gid) < 0) {
+            fprintf(stderr, "Unable to chown %s: %s\n", argv[i], strerror(errno));
+            return 10;
+        }
+    }
+
+    return 0;
+}
diff --git a/toolbox/cmp.c b/toolbox/cmp.c
new file mode 100644
index 0000000..80635ad
--- /dev/null
+++ b/toolbox/cmp.c
@@ -0,0 +1,91 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+
+int cmp_main(int argc, char *argv[])
+{
+    int c;
+    int fd1, fd2;
+	char buf1[4096], buf2[4096];
+    int res, res1, res2;
+	int rv = 0;
+	int i;
+	int filepos = 0;
+
+	int show_byte = 0;
+	int show_all = 0;
+	int limit = 0;
+
+    do {
+        c = getopt(argc, argv, "bln:");
+        if (c == EOF)
+            break;
+        switch (c) {
+        case 'b':
+            show_byte = 1;
+            break;
+        case 'l':
+            show_all = 1;
+            break;
+        case 'n':
+            limit = atoi(optarg);
+            break;
+        case '?':
+            fprintf(stderr, "%s: invalid option -%c\n",
+                argv[0], optopt);
+            exit(1);
+        }
+    } while (1);
+
+    if (optind + 2 != argc) {
+        fprintf(stderr, "Usage: %s [-b] [-l] [-n count] file1 file2\n", argv[0]);
+        exit(1);
+    }
+
+    fd1 = open(argv[optind], O_RDONLY);
+    if(fd1 < 0) {
+        fprintf(stderr, "could not open %s, %s\n", argv[optind], strerror(errno));
+        return 1;
+    }
+
+    fd2 = open(argv[optind+1], O_RDONLY);
+    if(fd2 < 0) {
+        fprintf(stderr, "could not open %s, %s\n", argv[optind+1], strerror(errno));
+        return 1;
+    }
+    
+    while(1) {
+        res1 = read(fd1, &buf1, sizeof(buf1));
+        res2 = read(fd2, &buf2, sizeof(buf2));
+		res = res1 < res2 ? res1 : res2;
+		if(res1 == 0 && res2 == 0) {
+			return rv;
+		}
+		for(i = 0; i < res; i++) {
+			if(buf1[i] != buf2[i]) {
+				printf("%s %s differ byte %d", argv[optind], argv[optind+1], filepos + i);
+				if(show_byte)
+					printf(" 0x%02x 0x%02x", buf1[i], buf2[i]);
+				printf("\n");
+				if(!show_all)
+					return 1;
+				rv = 1;
+			}
+			if(limit) {
+				limit--;
+				if(limit == 0)
+					return rv;
+			}
+		}
+		if(res1 != res2 || res < 0) {
+			printf("%s on %s\n", res < 0 ? "Read error" : "EOF", res1 < res2 ? argv[optind] : argv[optind+1]);
+			return 1;
+		}
+		filepos += res;
+    }
+}
diff --git a/toolbox/date.c b/toolbox/date.c
new file mode 100644
index 0000000..da4ef4a
--- /dev/null
+++ b/toolbox/date.c
@@ -0,0 +1,152 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <linux/android_alarm.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+
+static void usage() {
+    fprintf(stdout, "Usage: date [OPTION]\n\n");
+    fprintf(stdout, "  -u   display time in UTC\n");
+    fprintf(stdout, "  -s   set current time, for example,\n");
+    fprintf(stdout, "       date -s 20140704:120000\n");
+    exit(1);
+}
+
+static void settime(char *s) {
+    struct tm tm;
+    int day = atoi(s);
+    int hour;
+    time_t t;
+    int fd;
+    struct timespec ts;
+
+    while (*s && *s != '.')
+        s++;
+
+    if (*s)
+        s++;
+
+    hour = atoi(s);
+
+    tm.tm_year = day / 10000 - 1900;
+    tm.tm_mon = (day % 10000) / 100 - 1;
+    tm.tm_mday = (day % 100);
+    tm.tm_hour = hour / 10000;
+    tm.tm_min = (hour % 10000) / 100;
+    tm.tm_sec = (hour % 100);
+    tm.tm_isdst = -1;
+
+    t = mktime(&tm);
+
+    fd = open("/dev/alarm", O_RDWR);
+    if (fd != -1) {
+        ts.tv_sec = t;
+        ts.tv_nsec = 0;
+        ioctl(fd, ANDROID_ALARM_SET_RTC, &ts);
+        close(fd);
+        return;
+    }
+
+    // /dev/alarm is unavailable, use settimeofday().
+    struct timeval tv = {
+        .tv_sec = t
+    };
+    if (settimeofday(&tv, NULL)) {
+        fprintf(stderr,"settimeofday failed %s\n", strerror(errno));
+    }
+}
+
+int date_main(int argc, char *argv[])
+{
+	int c;
+    int res;
+	struct tm tm;
+	time_t t;
+	struct timeval tv;
+    struct timespec ts;
+	char strbuf[260];
+    int fd;
+
+    int useutc = 0;
+
+    tzset();
+
+    do {
+        c = getopt(argc, argv, "us:");
+        if (c == EOF)
+            break;
+        switch (c) {
+        case 'u':
+            useutc = 1;
+            break;
+        case 's':
+            settime(optarg);
+            break;
+        case '?':
+            usage();
+        }
+    } while (1);
+    if(optind + 2 < argc) {
+        usage();
+    }
+
+    int hasfmt = argc == optind + 1 && argv[optind][0] == '+';
+    if(optind == argc || hasfmt) {
+        char buf[2000];
+        time(&t);
+        if (useutc) {
+            gmtime_r(&t, &tm);
+            strftime(strbuf, sizeof(strbuf),
+                     (hasfmt ? argv[optind] + 1 : "%a %b %e %H:%M:%S GMT %Y"),
+                     &tm);
+        } else {
+            localtime_r(&t, &tm);
+            strftime(strbuf, sizeof(strbuf),
+                     (hasfmt ? argv[optind] + 1 : "%a %b %e %H:%M:%S %Z %Y"),
+                     &tm);
+        }
+        printf("%s\n", strbuf);
+    }
+    else if(optind + 1 == argc) {
+#if 0
+        struct tm *tmptr;
+        tmptr = getdate(argv[optind]);
+        if(tmptr == NULL) {
+            fprintf(stderr,"getdate_r failed\n");
+            return 1;
+        }
+        tm = *tmptr;
+#if 0
+        if(getdate_r(argv[optind], &tm) < 0) {
+            fprintf(stderr,"getdate_r failed %s\n", strerror(errno));
+            return 1;
+        }
+#endif
+#endif
+        //strptime(argv[optind], NULL, &tm);
+        //tv.tv_sec = mktime(&tm);
+        //tv.tv_usec = 0;
+        strtotimeval(argv[optind], &tv);
+        printf("time %s -> %d.%d\n", argv[optind], tv.tv_sec, tv.tv_usec);
+        fd = open("/dev/alarm", O_RDWR);
+        ts.tv_sec = tv.tv_sec;
+        ts.tv_nsec = tv.tv_usec * 1000;
+        res = ioctl(fd, ANDROID_ALARM_SET_RTC, &ts);
+        //res = settimeofday(&tv, NULL);
+        if(res < 0) {
+            fprintf(stderr,"settimeofday failed %s\n", strerror(errno));
+            return 1;
+        }
+    }
+    else {
+        fprintf(stderr,"%s [-s 20070325.123456] [-u] [date]\n", argv[0]);
+        return 1;
+    }
+
+    return 0;
+}
diff --git a/toolbox/dd.c b/toolbox/dd.c
new file mode 100644
index 0000000..ba29083
--- /dev/null
+++ b/toolbox/dd.c
@@ -0,0 +1,1356 @@
+/*	$NetBSD: dd.c,v 1.37 2004/01/17 21:00:16 dbj Exp $	*/
+
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego and Lance
+ * Visser of Convex Computer Corporation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__COPYRIGHT("@(#) Copyright (c) 1991, 1993, 1994\n\
+	The Regents of the University of California.  All rights reserved.\n");
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)dd.c	8.5 (Berkeley) 4/2/94";
+#else
+__RCSID("$NetBSD: dd.c,v 1.37 2004/01/17 21:00:16 dbj Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "dd.h"
+
+#define NO_CONV
+
+//#include "extern.h"
+void block(void);
+void block_close(void);
+void dd_out(int);
+void def(void);
+void def_close(void);
+void jcl(char **);
+void pos_in(void);
+void pos_out(void);
+void summary(void);
+void summaryx(int);
+void terminate(int);
+void unblock(void);
+void unblock_close(void);
+ssize_t bwrite(int, const void *, size_t);
+
+extern IO		in, out;
+extern STAT		st;
+extern void		(*cfunc)(void);
+extern uint64_t		cpy_cnt;
+extern uint64_t		cbsz;
+extern u_int		ddflags;
+extern u_int		files_cnt;
+extern int		progress;
+extern const u_char	*ctab;
+extern const u_char	a2e_32V[], a2e_POSIX[];
+extern const u_char	e2a_32V[], e2a_POSIX[];
+extern const u_char	a2ibm_32V[], a2ibm_POSIX[];
+extern u_char		casetab[];
+
+
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+
+static void dd_close(void);
+static void dd_in(void);
+static void getfdtype(IO *);
+static int redup_clean_fd(int);
+static void setup(void);
+
+
+IO		in, out;		/* input/output state */
+STAT		st;			/* statistics */
+void		(*cfunc)(void);		/* conversion function */
+uint64_t	cpy_cnt;		/* # of blocks to copy */
+static off_t	pending = 0;		/* pending seek if sparse */
+u_int		ddflags;		/* conversion options */
+uint64_t	cbsz;			/* conversion block size */
+u_int		files_cnt = 1;		/* # of files to copy */
+int		progress = 0;		/* display sign of life */
+const u_char	*ctab;			/* conversion table */
+sigset_t	infoset;		/* a set blocking SIGINFO */
+
+int
+dd_main(int argc, char *argv[])
+{
+	int ch;
+
+	while ((ch = getopt(argc, argv, "")) != -1) {
+		switch (ch) {
+		default:
+			fprintf(stderr, "usage: dd [operand ...]\n");
+			exit(1);
+			/* NOTREACHED */
+		}
+	}
+	argc -= (optind - 1);
+	argv += (optind - 1);
+
+	jcl(argv);
+	setup();
+
+//	(void)signal(SIGINFO, summaryx);
+	(void)signal(SIGINT, terminate);
+	(void)sigemptyset(&infoset);
+//	(void)sigaddset(&infoset, SIGINFO);
+
+	(void)atexit(summary);
+
+	while (files_cnt--)
+		dd_in();
+
+	dd_close();
+	exit(0);
+	/* NOTREACHED */
+}
+
+static void
+setup(void)
+{
+
+	if (in.name == NULL) {
+		in.name = "stdin";
+		in.fd = STDIN_FILENO;
+	} else {
+		in.fd = open(in.name, O_RDONLY, 0);
+		if (in.fd < 0) {
+			fprintf(stderr, "%s: cannot open for read: %s\n",
+				in.name, strerror(errno));
+			exit(1);
+			/* NOTREACHED */
+		}
+
+		/* Ensure in.fd is outside the stdio descriptor range */
+		in.fd = redup_clean_fd(in.fd);
+	}
+
+	getfdtype(&in);
+
+	if (files_cnt > 1 && !(in.flags & ISTAPE)) {
+		fprintf(stderr,
+			"files is not supported for non-tape devices\n");
+		exit(1);
+		/* NOTREACHED */
+	}
+
+	if (out.name == NULL) {
+		/* No way to check for read access here. */
+		out.fd = STDOUT_FILENO;
+		out.name = "stdout";
+	} else {
+#define	OFLAGS \
+    (O_CREAT | (ddflags & (C_SEEK | C_NOTRUNC) ? 0 : O_TRUNC))
+		out.fd = open(out.name, O_RDWR | OFLAGS, DEFFILEMODE);
+		/*
+		 * May not have read access, so try again with write only.
+		 * Without read we may have a problem if output also does
+		 * not support seeks.
+		 */
+		if (out.fd < 0) {
+			out.fd = open(out.name, O_WRONLY | OFLAGS, DEFFILEMODE);
+			out.flags |= NOREAD;
+		}
+		if (out.fd < 0) {
+			fprintf(stderr, "%s: cannot open for write: %s\n",
+				out.name, strerror(errno));
+			exit(1);
+			/* NOTREACHED */
+		}
+
+		/* Ensure out.fd is outside the stdio descriptor range */
+		out.fd = redup_clean_fd(out.fd);
+	}
+
+	getfdtype(&out);
+
+	/*
+	 * Allocate space for the input and output buffers.  If not doing
+	 * record oriented I/O, only need a single buffer.
+	 */
+	if (!(ddflags & (C_BLOCK|C_UNBLOCK))) {
+		if ((in.db = malloc(out.dbsz + in.dbsz - 1)) == NULL) {
+			exit(1);
+			/* NOTREACHED */
+		}
+		out.db = in.db;
+	} else if ((in.db =
+	    malloc((u_int)(MAX(in.dbsz, cbsz) + cbsz))) == NULL ||
+	    (out.db = malloc((u_int)(out.dbsz + cbsz))) == NULL) {
+		exit(1);
+		/* NOTREACHED */
+	}
+	in.dbp = in.db;
+	out.dbp = out.db;
+
+	/* Position the input/output streams. */
+	if (in.offset)
+		pos_in();
+	if (out.offset)
+		pos_out();
+
+	/*
+	 * Truncate the output file; ignore errors because it fails on some
+	 * kinds of output files, tapes, for example.
+	 */
+	if ((ddflags & (C_OF | C_SEEK | C_NOTRUNC)) == (C_OF | C_SEEK))
+		(void)ftruncate(out.fd, (off_t)out.offset * out.dbsz);
+
+	/*
+	 * If converting case at the same time as another conversion, build a
+	 * table that does both at once.  If just converting case, use the
+	 * built-in tables.
+	 */
+	if (ddflags & (C_LCASE|C_UCASE)) {
+#ifdef	NO_CONV
+		/* Should not get here, but just in case... */
+		fprintf(stderr, "case conv and -DNO_CONV\n");
+		exit(1);
+		/* NOTREACHED */
+#else	/* NO_CONV */
+		u_int cnt;
+
+		if (ddflags & C_ASCII || ddflags & C_EBCDIC) {
+			if (ddflags & C_LCASE) {
+				for (cnt = 0; cnt < 0377; ++cnt)
+					casetab[cnt] = tolower(ctab[cnt]);
+			} else {
+				for (cnt = 0; cnt < 0377; ++cnt)
+					casetab[cnt] = toupper(ctab[cnt]);
+			}
+		} else {
+			if (ddflags & C_LCASE) {
+				for (cnt = 0; cnt < 0377; ++cnt)
+					casetab[cnt] = tolower(cnt);
+			} else {
+				for (cnt = 0; cnt < 0377; ++cnt)
+					casetab[cnt] = toupper(cnt);
+			}
+		}
+
+		ctab = casetab;
+#endif	/* NO_CONV */
+	}
+
+	(void)gettimeofday(&st.start, NULL);	/* Statistics timestamp. */
+}
+
+static void
+getfdtype(IO *io)
+{
+//	struct mtget mt;
+	struct stat sb;
+
+	if (fstat(io->fd, &sb)) {
+		fprintf(stderr, "%s: cannot fstat: %s\n",
+			io->name, strerror(errno));
+		exit(1);
+		/* NOTREACHED */
+	}
+	if (S_ISCHR(sb.st_mode))
+		io->flags |= /*ioctl(io->fd, MTIOCGET, &mt) ? ISCHR : ISTAPE; */ ISCHR;
+	else if (lseek(io->fd, (off_t)0, SEEK_CUR) == -1 && errno == ESPIPE)
+		io->flags |= ISPIPE;		/* XXX fixed in 4.4BSD */
+}
+
+/*
+ * Move the parameter file descriptor to a descriptor that is outside the
+ * stdio descriptor range, if necessary.  This is required to avoid
+ * accidentally outputting completion or error messages into the
+ * output file that were intended for the tty.
+ */
+static int
+redup_clean_fd(int fd)
+{
+	int newfd;
+
+	if (fd != STDIN_FILENO && fd != STDOUT_FILENO &&
+	    fd != STDERR_FILENO)
+		/* File descriptor is ok, return immediately. */
+		return fd;
+
+	/*
+	 * 3 is the first descriptor greater than STD*_FILENO.  Any
+	 * free descriptor valued 3 or above is acceptable...
+	 */
+	newfd = fcntl(fd, F_DUPFD, 3);
+	if (newfd < 0) {
+		fprintf(stderr, "dupfd IO: %s\n", strerror(errno));
+		exit(1);
+		/* NOTREACHED */
+	}
+
+	close(fd);
+
+	return newfd;
+}
+
+static void
+dd_in(void)
+{
+	int flags;
+	int64_t n;
+
+	for (flags = ddflags;;) {
+		if (cpy_cnt && (st.in_full + st.in_part) >= cpy_cnt)
+			return;
+
+		/*
+		 * Clear the buffer first if doing "sync" on input.
+		 * If doing block operations use spaces.  This will
+		 * affect not only the C_NOERROR case, but also the
+		 * last partial input block which should be padded
+		 * with zero and not garbage.
+		 */
+		if (flags & C_SYNC) {
+			if (flags & (C_BLOCK|C_UNBLOCK))
+				(void)memset(in.dbp, ' ', in.dbsz);
+			else
+				(void)memset(in.dbp, 0, in.dbsz);
+		}
+
+		n = read(in.fd, in.dbp, in.dbsz);
+		if (n == 0) {
+			in.dbrcnt = 0;
+			return;
+		}
+
+		/* Read error. */
+		if (n < 0) {
+
+			/*
+			 * If noerror not specified, die.  POSIX requires that
+			 * the warning message be followed by an I/O display.
+			 */
+			fprintf(stderr, "%s: read error: %s\n",
+				in.name, strerror(errno));
+			if (!(flags & C_NOERROR)) {
+				exit(1);
+				/* NOTREACHED */
+			}
+			summary();
+
+			/*
+			 * If it's not a tape drive or a pipe, seek past the
+			 * error.  If your OS doesn't do the right thing for
+			 * raw disks this section should be modified to re-read
+			 * in sector size chunks.
+			 */
+			if (!(in.flags & (ISPIPE|ISTAPE)) &&
+			    lseek(in.fd, (off_t)in.dbsz, SEEK_CUR))
+				fprintf(stderr, "%s: seek error: %s\n",
+					in.name, strerror(errno));
+
+			/* If sync not specified, omit block and continue. */
+			if (!(ddflags & C_SYNC))
+				continue;
+
+			/* Read errors count as full blocks. */
+			in.dbcnt += in.dbrcnt = in.dbsz;
+			++st.in_full;
+
+		/* Handle full input blocks. */
+		} else if (n == in.dbsz) {
+			in.dbcnt += in.dbrcnt = n;
+			++st.in_full;
+
+		/* Handle partial input blocks. */
+		} else {
+			/* If sync, use the entire block. */
+			if (ddflags & C_SYNC)
+				in.dbcnt += in.dbrcnt = in.dbsz;
+			else
+				in.dbcnt += in.dbrcnt = n;
+			++st.in_part;
+		}
+
+		/*
+		 * POSIX states that if bs is set and no other conversions
+		 * than noerror, notrunc or sync are specified, the block
+		 * is output without buffering as it is read.
+		 */
+		if (ddflags & C_BS) {
+			out.dbcnt = in.dbcnt;
+			dd_out(1);
+			in.dbcnt = 0;
+			continue;
+		}
+
+/*		if (ddflags & C_SWAB) {
+			if ((n = in.dbrcnt) & 1) {
+				++st.swab;
+				--n;
+			}
+			swab(in.dbp, in.dbp, n);
+		}
+*/
+		in.dbp += in.dbrcnt;
+		(*cfunc)();
+	}
+}
+
+/*
+ * Cleanup any remaining I/O and flush output.  If necesssary, output file
+ * is truncated.
+ */
+static void
+dd_close(void)
+{
+
+	if (cfunc == def)
+		def_close();
+	else if (cfunc == block)
+		block_close();
+	else if (cfunc == unblock)
+		unblock_close();
+	if (ddflags & C_OSYNC && out.dbcnt < out.dbsz) {
+		(void)memset(out.dbp, 0, out.dbsz - out.dbcnt);
+		out.dbcnt = out.dbsz;
+	}
+	/* If there are pending sparse blocks, make sure
+	 * to write out the final block un-sparse
+	 */
+	if ((out.dbcnt == 0) && pending) {
+		memset(out.db, 0, out.dbsz);
+		out.dbcnt = out.dbsz;
+		out.dbp = out.db + out.dbcnt;
+		pending -= out.dbsz;
+	}
+	if (out.dbcnt)
+		dd_out(1);
+
+	/*
+	 * Reporting nfs write error may be defered until next
+	 * write(2) or close(2) system call.  So, we need to do an
+	 * extra check.  If an output is stdout, the file structure
+	 * may be shared among with other processes and close(2) just
+	 * decreases the reference count.
+	 */
+	if (out.fd == STDOUT_FILENO && fsync(out.fd) == -1 && errno != EINVAL) {
+		fprintf(stderr, "fsync stdout: %s\n", strerror(errno));
+		exit(1);
+		/* NOTREACHED */
+	}
+	if (close(out.fd) == -1) {
+		fprintf(stderr, "close: %s\n", strerror(errno));
+		exit(1);
+		/* NOTREACHED */
+	}
+}
+
+void
+dd_out(int force)
+{
+	static int warned;
+	int64_t cnt, n, nw;
+	u_char *outp;
+
+	/*
+	 * Write one or more blocks out.  The common case is writing a full
+	 * output block in a single write; increment the full block stats.
+	 * Otherwise, we're into partial block writes.  If a partial write,
+	 * and it's a character device, just warn.  If a tape device, quit.
+	 *
+	 * The partial writes represent two cases.  1: Where the input block
+	 * was less than expected so the output block was less than expected.
+	 * 2: Where the input block was the right size but we were forced to
+	 * write the block in multiple chunks.  The original versions of dd(1)
+	 * never wrote a block in more than a single write, so the latter case
+	 * never happened.
+	 *
+	 * One special case is if we're forced to do the write -- in that case
+	 * we play games with the buffer size, and it's usually a partial write.
+	 */
+	outp = out.db;
+	for (n = force ? out.dbcnt : out.dbsz;; n = out.dbsz) {
+		for (cnt = n;; cnt -= nw) {
+
+			if (!force && ddflags & C_SPARSE) {
+				int sparse, i;
+				sparse = 1;	/* Is buffer sparse? */
+				for (i = 0; i < cnt; i++)
+					if (outp[i] != 0) {
+						sparse = 0;
+						break;
+					}
+				if (sparse) {
+					pending += cnt;
+					outp += cnt;
+					nw = 0;
+					break;
+				}
+			}
+			if (pending != 0) {
+				if (lseek(out.fd, pending, SEEK_CUR) ==
+				    -1) {
+					fprintf(stderr,
+						"%s: seek error creating "
+						"sparse file: %s\n",
+						out.name, strerror(errno));
+					exit(1);
+				}
+			}
+			nw = bwrite(out.fd, outp, cnt);
+			if (nw <= 0) {
+				if (nw == 0) {
+					fprintf(stderr, "%s: end of device\n",
+						out.name);
+					exit(1);
+					/* NOTREACHED */
+				}
+				if (errno != EINTR) {
+					fprintf(stderr, "%s: write error: %s\n",
+						out.name, strerror(errno));
+					/* NOTREACHED */
+					exit(1);
+				}
+				nw = 0;
+			}
+			if (pending) {
+				st.bytes += pending;
+				st.sparse += pending/out.dbsz;
+				st.out_full += pending/out.dbsz;
+				pending = 0;
+			}
+			outp += nw;
+			st.bytes += nw;
+			if (nw == n) {
+				if (n != out.dbsz)
+					++st.out_part;
+				else
+					++st.out_full;
+				break;
+			}
+			++st.out_part;
+			if (nw == cnt)
+				break;
+			if (out.flags & ISCHR && !warned) {
+				warned = 1;
+				fprintf(stderr, "%s: short write on character "
+					"device\n", out.name);
+			}
+			if (out.flags & ISTAPE) {
+				fprintf(stderr,
+					"%s: short write on tape device",
+					out.name);
+				exit(1);
+				/* NOTREACHED */
+			}
+		}
+		if ((out.dbcnt -= n) < out.dbsz)
+			break;
+	}
+
+	/* Reassemble the output block. */
+	if (out.dbcnt)
+		(void)memmove(out.db, out.dbp - out.dbcnt, out.dbcnt);
+	out.dbp = out.db + out.dbcnt;
+
+	if (progress)
+		(void)write(STDERR_FILENO, ".", 1);
+}
+
+/*
+ * A protected against SIGINFO write
+ */
+ssize_t
+bwrite(int fd, const void *buf, size_t len)
+{
+	sigset_t oset;
+	ssize_t rv;
+	int oerrno;
+
+	(void)sigprocmask(SIG_BLOCK, &infoset, &oset);
+	rv = write(fd, buf, len);
+	oerrno = errno;
+	(void)sigprocmask(SIG_SETMASK, &oset, NULL);
+	errno = oerrno;
+	return (rv);
+}
+
+/*
+ * Position input/output data streams before starting the copy.  Device type
+ * dependent.  Seekable devices use lseek, and the rest position by reading.
+ * Seeking past the end of file can cause null blocks to be written to the
+ * output.
+ */
+void
+pos_in(void)
+{
+	int bcnt, cnt, nr, warned;
+
+	/* If not a pipe or tape device, try to seek on it. */
+	if (!(in.flags & (ISPIPE|ISTAPE))) {
+		if (lseek(in.fd,
+		    (off_t)in.offset * (off_t)in.dbsz, SEEK_CUR) == -1) {
+			fprintf(stderr, "%s: seek error: %s",
+				in.name, strerror(errno));
+			exit(1);
+			/* NOTREACHED */
+		}
+		return;
+		/* NOTREACHED */
+	}
+
+	/*
+	 * Read the data.  If a pipe, read until satisfy the number of bytes
+	 * being skipped.  No differentiation for reading complete and partial
+	 * blocks for other devices.
+	 */
+	for (bcnt = in.dbsz, cnt = in.offset, warned = 0; cnt;) {
+		if ((nr = read(in.fd, in.db, bcnt)) > 0) {
+			if (in.flags & ISPIPE) {
+				if (!(bcnt -= nr)) {
+					bcnt = in.dbsz;
+					--cnt;
+				}
+			} else
+				--cnt;
+			continue;
+		}
+
+		if (nr == 0) {
+			if (files_cnt > 1) {
+				--files_cnt;
+				continue;
+			}
+			fprintf(stderr, "skip reached end of input\n");
+			exit(1);
+			/* NOTREACHED */
+		}
+
+		/*
+		 * Input error -- either EOF with no more files, or I/O error.
+		 * If noerror not set die.  POSIX requires that the warning
+		 * message be followed by an I/O display.
+		 */
+		if (ddflags & C_NOERROR) {
+			if (!warned) {
+
+				fprintf(stderr, "%s: error occurred\n",
+					in.name);
+				warned = 1;
+				summary();
+			}
+			continue;
+		}
+		fprintf(stderr, "%s: read error: %s", in.name, strerror(errno));
+		exit(1);
+		/* NOTREACHED */
+	}
+}
+
+void
+pos_out(void)
+{
+//	struct mtop t_op;
+	int cnt, n;
+
+	/*
+	 * If not a tape, try seeking on the file.  Seeking on a pipe is
+	 * going to fail, but don't protect the user -- they shouldn't
+	 * have specified the seek operand.
+	 */
+	if (!(out.flags & ISTAPE)) {
+		if (lseek(out.fd,
+		    (off_t)out.offset * (off_t)out.dbsz, SEEK_SET) == -1) {
+			fprintf(stderr, "%s: seek error: %s\n",
+				out.name, strerror(errno));
+			exit(1);
+			/* NOTREACHED */
+		}
+		return;
+	}
+
+	/* If no read access, try using mtio. */
+	if (out.flags & NOREAD) {
+/*		t_op.mt_op = MTFSR;
+		t_op.mt_count = out.offset;
+
+		if (ioctl(out.fd, MTIOCTOP, &t_op) < 0)*/
+			fprintf(stderr, "%s: cannot read", out.name);
+			exit(1);
+			/* NOTREACHED */
+		return;
+	}
+
+	/* Read it. */
+	for (cnt = 0; cnt < out.offset; ++cnt) {
+		if ((n = read(out.fd, out.db, out.dbsz)) > 0)
+			continue;
+
+		if (n < 0) {
+			fprintf(stderr, "%s: cannot position by reading: %s\n",
+				out.name, strerror(errno));
+			exit(1);
+			/* NOTREACHED */
+		}
+
+		/*
+		 * If reach EOF, fill with NUL characters; first, back up over
+		 * the EOF mark.  Note, cnt has not yet been incremented, so
+		 * the EOF read does not count as a seek'd block.
+		 */
+/*		t_op.mt_op = MTBSR;
+		t_op.mt_count = 1;
+		if (ioctl(out.fd, MTIOCTOP, &t_op) == -1) */ {
+			fprintf(stderr, "%s: cannot position\n", out.name);
+			exit(1);
+			/* NOTREACHED */
+		}
+
+		while (cnt++ < out.offset)
+			if ((n = bwrite(out.fd, out.db, out.dbsz)) != out.dbsz) {
+				fprintf(stderr, "%s: cannot position "
+					"by writing: %s\n",
+					out.name, strerror(errno));
+				exit(1);
+				/* NOTREACHED */
+			}
+		break;
+	}
+}
+
+/*
+ * def --
+ * Copy input to output.  Input is buffered until reaches obs, and then
+ * output until less than obs remains.  Only a single buffer is used.
+ * Worst case buffer calculation is (ibs + obs - 1).
+ */
+void
+def(void)
+{
+	uint64_t cnt;
+	u_char *inp;
+	const u_char *t;
+
+	if ((t = ctab) != NULL)
+		for (inp = in.dbp - (cnt = in.dbrcnt); cnt--; ++inp)
+			*inp = t[*inp];
+
+	/* Make the output buffer look right. */
+	out.dbp = in.dbp;
+	out.dbcnt = in.dbcnt;
+
+	if (in.dbcnt >= out.dbsz) {
+		/* If the output buffer is full, write it. */
+		dd_out(0);
+
+		/*
+		 * Ddout copies the leftover output to the beginning of
+		 * the buffer and resets the output buffer.  Reset the
+		 * input buffer to match it.
+	 	 */
+		in.dbp = out.dbp;
+		in.dbcnt = out.dbcnt;
+	}
+}
+
+void
+def_close(void)
+{
+
+	/* Just update the count, everything is already in the buffer. */
+	if (in.dbcnt)
+		out.dbcnt = in.dbcnt;
+}
+
+#ifdef	NO_CONV
+/* Build a smaller version (i.e. for a miniroot) */
+/* These can not be called, but just in case...  */
+static const char no_block[] = "unblock and -DNO_CONV?\n";
+void block(void)		{ fprintf(stderr, "%s", no_block + 2); exit(1); }
+void block_close(void)		{ fprintf(stderr, "%s", no_block + 2); exit(1); }
+void unblock(void)		{ fprintf(stderr, "%s", no_block); exit(1); }
+void unblock_close(void)	{ fprintf(stderr, "%s", no_block); exit(1); }
+#else	/* NO_CONV */
+
+/*
+ * Copy variable length newline terminated records with a max size cbsz
+ * bytes to output.  Records less than cbs are padded with spaces.
+ *
+ * max in buffer:  MAX(ibs, cbsz)
+ * max out buffer: obs + cbsz
+ */
+void
+block(void)
+{
+	static int intrunc;
+	int ch = 0;	/* pacify gcc */
+	uint64_t cnt, maxlen;
+	u_char *inp, *outp;
+	const u_char *t;
+
+	/*
+	 * Record truncation can cross block boundaries.  If currently in a
+	 * truncation state, keep tossing characters until reach a newline.
+	 * Start at the beginning of the buffer, as the input buffer is always
+	 * left empty.
+	 */
+	if (intrunc) {
+		for (inp = in.db, cnt = in.dbrcnt;
+		    cnt && *inp++ != '\n'; --cnt);
+		if (!cnt) {
+			in.dbcnt = 0;
+			in.dbp = in.db;
+			return;
+		}
+		intrunc = 0;
+		/* Adjust the input buffer numbers. */
+		in.dbcnt = cnt - 1;
+		in.dbp = inp + cnt - 1;
+	}
+
+	/*
+	 * Copy records (max cbsz size chunks) into the output buffer.  The
+	 * translation is done as we copy into the output buffer.
+	 */
+	for (inp = in.dbp - in.dbcnt, outp = out.dbp; in.dbcnt;) {
+		maxlen = MIN(cbsz, in.dbcnt);
+		if ((t = ctab) != NULL)
+			for (cnt = 0;
+			    cnt < maxlen && (ch = *inp++) != '\n'; ++cnt)
+				*outp++ = t[ch];
+		else
+			for (cnt = 0;
+			    cnt < maxlen && (ch = *inp++) != '\n'; ++cnt)
+				*outp++ = ch;
+		/*
+		 * Check for short record without a newline.  Reassemble the
+		 * input block.
+		 */
+		if (ch != '\n' && in.dbcnt < cbsz) {
+			(void)memmove(in.db, in.dbp - in.dbcnt, in.dbcnt);
+			break;
+		}
+
+		/* Adjust the input buffer numbers. */
+		in.dbcnt -= cnt;
+		if (ch == '\n')
+			--in.dbcnt;
+
+		/* Pad short records with spaces. */
+		if (cnt < cbsz)
+			(void)memset(outp, ctab ? ctab[' '] : ' ', cbsz - cnt);
+		else {
+			/*
+			 * If the next character wouldn't have ended the
+			 * block, it's a truncation.
+			 */
+			if (!in.dbcnt || *inp != '\n')
+				++st.trunc;
+
+			/* Toss characters to a newline. */
+			for (; in.dbcnt && *inp++ != '\n'; --in.dbcnt);
+			if (!in.dbcnt)
+				intrunc = 1;
+			else
+				--in.dbcnt;
+		}
+
+		/* Adjust output buffer numbers. */
+		out.dbp += cbsz;
+		if ((out.dbcnt += cbsz) >= out.dbsz)
+			dd_out(0);
+		outp = out.dbp;
+	}
+	in.dbp = in.db + in.dbcnt;
+}
+
+void
+block_close(void)
+{
+
+	/*
+	 * Copy any remaining data into the output buffer and pad to a record.
+	 * Don't worry about truncation or translation, the input buffer is
+	 * always empty when truncating, and no characters have been added for
+	 * translation.  The bottom line is that anything left in the input
+	 * buffer is a truncated record.  Anything left in the output buffer
+	 * just wasn't big enough.
+	 */
+	if (in.dbcnt) {
+		++st.trunc;
+		(void)memmove(out.dbp, in.dbp - in.dbcnt, in.dbcnt);
+		(void)memset(out.dbp + in.dbcnt,
+		    ctab ? ctab[' '] : ' ', cbsz - in.dbcnt);
+		out.dbcnt += cbsz;
+	}
+}
+
+/*
+ * Convert fixed length (cbsz) records to variable length.  Deletes any
+ * trailing blanks and appends a newline.
+ *
+ * max in buffer:  MAX(ibs, cbsz) + cbsz
+ * max out buffer: obs + cbsz
+ */
+void
+unblock(void)
+{
+	uint64_t cnt;
+	u_char *inp;
+	const u_char *t;
+
+	/* Translation and case conversion. */
+	if ((t = ctab) != NULL)
+		for (cnt = in.dbrcnt, inp = in.dbp - 1; cnt--; inp--)
+			*inp = t[*inp];
+	/*
+	 * Copy records (max cbsz size chunks) into the output buffer.  The
+	 * translation has to already be done or we might not recognize the
+	 * spaces.
+	 */
+	for (inp = in.db; in.dbcnt >= cbsz; inp += cbsz, in.dbcnt -= cbsz) {
+		for (t = inp + cbsz - 1; t >= inp && *t == ' '; --t);
+		if (t >= inp) {
+			cnt = t - inp + 1;
+			(void)memmove(out.dbp, inp, cnt);
+			out.dbp += cnt;
+			out.dbcnt += cnt;
+		}
+		++out.dbcnt;
+		*out.dbp++ = '\n';
+		if (out.dbcnt >= out.dbsz)
+			dd_out(0);
+	}
+	if (in.dbcnt)
+		(void)memmove(in.db, in.dbp - in.dbcnt, in.dbcnt);
+	in.dbp = in.db + in.dbcnt;
+}
+
+void
+unblock_close(void)
+{
+	uint64_t cnt;
+	u_char *t;
+
+	if (in.dbcnt) {
+		warnx("%s: short input record", in.name);
+		for (t = in.db + in.dbcnt - 1; t >= in.db && *t == ' '; --t);
+		if (t >= in.db) {
+			cnt = t - in.db + 1;
+			(void)memmove(out.dbp, in.db, cnt);
+			out.dbp += cnt;
+			out.dbcnt += cnt;
+		}
+		++out.dbcnt;
+		*out.dbp++ = '\n';
+	}
+}
+
+#endif	/* NO_CONV */
+
+#define	tv2mS(tv) ((tv).tv_sec * 1000LL + ((tv).tv_usec + 500) / 1000)
+
+void
+summary(void)
+{
+	char buf[100];
+	int64_t mS;
+	struct timeval tv;
+
+	if (progress)
+		(void)write(STDERR_FILENO, "\n", 1);
+
+	(void)gettimeofday(&tv, NULL);
+	mS = tv2mS(tv) - tv2mS(st.start);
+	if (mS == 0)
+		mS = 1;
+	/* Use snprintf(3) so that we don't reenter stdio(3). */
+	(void)snprintf(buf, sizeof(buf),
+	    "%llu+%llu records in\n%llu+%llu records out\n",
+	    (unsigned long long)st.in_full,  (unsigned long long)st.in_part,
+	    (unsigned long long)st.out_full, (unsigned long long)st.out_part);
+	(void)write(STDERR_FILENO, buf, strlen(buf));
+	if (st.swab) {
+		(void)snprintf(buf, sizeof(buf), "%llu odd length swab %s\n",
+		    (unsigned long long)st.swab,
+		    (st.swab == 1) ? "block" : "blocks");
+		(void)write(STDERR_FILENO, buf, strlen(buf));
+	}
+	if (st.trunc) {
+		(void)snprintf(buf, sizeof(buf), "%llu truncated %s\n",
+		    (unsigned long long)st.trunc,
+		    (st.trunc == 1) ? "block" : "blocks");
+		(void)write(STDERR_FILENO, buf, strlen(buf));
+	}
+	if (st.sparse) {
+		(void)snprintf(buf, sizeof(buf), "%llu sparse output %s\n",
+		    (unsigned long long)st.sparse,
+		    (st.sparse == 1) ? "block" : "blocks");
+		(void)write(STDERR_FILENO, buf, strlen(buf));
+	}
+	(void)snprintf(buf, sizeof(buf),
+	    "%llu bytes transferred in %lu.%03d secs (%llu bytes/sec)\n",
+	    (unsigned long long) st.bytes,
+	    (long) (mS / 1000),
+	    (int) (mS % 1000),
+	    (unsigned long long) (st.bytes * 1000LL / mS));
+	(void)write(STDERR_FILENO, buf, strlen(buf));
+}
+
+void
+terminate(int notused)
+{
+
+	exit(0);
+	/* NOTREACHED */
+}
+
+static int	c_arg(const void *, const void *);
+#ifndef	NO_CONV
+static int	c_conv(const void *, const void *);
+#endif
+static void	f_bs(char *);
+static void	f_cbs(char *);
+static void	f_conv(char *);
+static void	f_count(char *);
+static void	f_files(char *);
+static void	f_ibs(char *);
+static void	f_if(char *);
+static void	f_obs(char *);
+static void	f_of(char *);
+static void	f_seek(char *);
+static void	f_skip(char *);
+static void	f_progress(char *);
+
+static const struct arg {
+	const char *name;
+	void (*f)(char *);
+	u_int set, noset;
+} args[] = {
+     /* the array needs to be sorted by the first column so
+	bsearch() can be used to find commands quickly */
+	{ "bs",		f_bs,		C_BS,	 C_BS|C_IBS|C_OBS|C_OSYNC },
+	{ "cbs",	f_cbs,		C_CBS,	 C_CBS },
+	{ "conv",	f_conv,		0,	 0 },
+	{ "count",	f_count,	C_COUNT, C_COUNT },
+	{ "files",	f_files,	C_FILES, C_FILES },
+	{ "ibs",	f_ibs,		C_IBS,	 C_BS|C_IBS },
+	{ "if",		f_if,		C_IF,	 C_IF },
+	{ "obs",	f_obs,		C_OBS,	 C_BS|C_OBS },
+	{ "of",		f_of,		C_OF,	 C_OF },
+	{ "progress",	f_progress,	0,	 0 },
+	{ "seek",	f_seek,		C_SEEK,	 C_SEEK },
+	{ "skip",	f_skip,		C_SKIP,	 C_SKIP },
+};
+
+/*
+ * args -- parse JCL syntax of dd.
+ */
+void
+jcl(char **argv)
+{
+	struct arg *ap, tmp;
+	char *oper, *arg;
+
+	in.dbsz = out.dbsz = 512;
+
+	while ((oper = *++argv) != NULL) {
+		if ((arg = strchr(oper, '=')) == NULL) {
+			fprintf(stderr, "unknown operand %s\n", oper);
+			exit(1);
+			/* NOTREACHED */
+		}
+		*arg++ = '\0';
+		if (!*arg) {
+			fprintf(stderr, "no value specified for %s\n", oper);
+			exit(1);
+			/* NOTREACHED */
+		}
+		tmp.name = oper;
+		if (!(ap = (struct arg *)bsearch(&tmp, args,
+		    sizeof(args)/sizeof(struct arg), sizeof(struct arg),
+		    c_arg))) {
+			fprintf(stderr, "unknown operand %s\n", tmp.name);
+			exit(1);
+			/* NOTREACHED */
+		}
+		if (ddflags & ap->noset) {
+			fprintf(stderr,
+			    "%s: illegal argument combination or already set\n",
+			    tmp.name);
+			exit(1);
+			/* NOTREACHED */
+		}
+		ddflags |= ap->set;
+		ap->f(arg);
+	}
+
+	/* Final sanity checks. */
+
+	if (ddflags & C_BS) {
+		/*
+		 * Bs is turned off by any conversion -- we assume the user
+		 * just wanted to set both the input and output block sizes
+		 * and didn't want the bs semantics, so we don't warn.
+		 */
+		if (ddflags & (C_BLOCK | C_LCASE | C_SWAB | C_UCASE |
+		    C_UNBLOCK | C_OSYNC | C_ASCII | C_EBCDIC | C_SPARSE)) {
+			ddflags &= ~C_BS;
+			ddflags |= C_IBS|C_OBS;
+		}
+
+		/* Bs supersedes ibs and obs. */
+		if (ddflags & C_BS && ddflags & (C_IBS|C_OBS))
+			fprintf(stderr, "bs supersedes ibs and obs\n");
+	}
+
+	/*
+	 * Ascii/ebcdic and cbs implies block/unblock.
+	 * Block/unblock requires cbs and vice-versa.
+	 */
+	if (ddflags & (C_BLOCK|C_UNBLOCK)) {
+		if (!(ddflags & C_CBS)) {
+			fprintf(stderr, "record operations require cbs\n");
+			exit(1);
+			/* NOTREACHED */
+		}
+		cfunc = ddflags & C_BLOCK ? block : unblock;
+	} else if (ddflags & C_CBS) {
+		if (ddflags & (C_ASCII|C_EBCDIC)) {
+			if (ddflags & C_ASCII) {
+				ddflags |= C_UNBLOCK;
+				cfunc = unblock;
+			} else {
+				ddflags |= C_BLOCK;
+				cfunc = block;
+			}
+		} else {
+			fprintf(stderr,
+			    "cbs meaningless if not doing record operations\n");
+			exit(1);
+			/* NOTREACHED */
+		}
+	} else
+		cfunc = def;
+
+	/* Read, write and seek calls take off_t as arguments.
+	 *
+	 * The following check is not done because an off_t is a quad
+	 *  for current NetBSD implementations.
+	 *
+	 * if (in.offset > INT_MAX/in.dbsz || out.offset > INT_MAX/out.dbsz)
+	 *	errx(1, "seek offsets cannot be larger than %d", INT_MAX);
+	 */
+}
+
+static int
+c_arg(const void *a, const void *b)
+{
+
+	return (strcmp(((const struct arg *)a)->name,
+	    ((const struct arg *)b)->name));
+}
+
+static long long strsuftoll(const char* name, const char* arg, int def, unsigned int max)
+{
+	long long result;
+	
+	if (sscanf(arg, "%lld", &result) == 0)
+		result = def;
+	return result;
+}
+
+static void
+f_bs(char *arg)
+{
+
+	in.dbsz = out.dbsz = strsuftoll("block size", arg, 1, UINT_MAX);
+}
+
+static void
+f_cbs(char *arg)
+{
+
+	cbsz = strsuftoll("conversion record size", arg, 1, UINT_MAX);
+}
+
+static void
+f_count(char *arg)
+{
+
+	cpy_cnt = strsuftoll("block count", arg, 0, LLONG_MAX);
+	if (!cpy_cnt)
+		terminate(0);
+}
+
+static void
+f_files(char *arg)
+{
+
+	files_cnt = (u_int)strsuftoll("file count", arg, 0, UINT_MAX);
+	if (!files_cnt)
+		terminate(0);
+}
+
+static void
+f_ibs(char *arg)
+{
+
+	if (!(ddflags & C_BS))
+		in.dbsz = strsuftoll("input block size", arg, 1, UINT_MAX);
+}
+
+static void
+f_if(char *arg)
+{
+
+	in.name = arg;
+}
+
+static void
+f_obs(char *arg)
+{
+
+	if (!(ddflags & C_BS))
+		out.dbsz = strsuftoll("output block size", arg, 1, UINT_MAX);
+}
+
+static void
+f_of(char *arg)
+{
+
+	out.name = arg;
+}
+
+static void
+f_seek(char *arg)
+{
+
+	out.offset = strsuftoll("seek blocks", arg, 0, LLONG_MAX);
+}
+
+static void
+f_skip(char *arg)
+{
+
+	in.offset = strsuftoll("skip blocks", arg, 0, LLONG_MAX);
+}
+
+static void
+f_progress(char *arg)
+{
+
+	if (*arg != '0')
+		progress = 1;
+}
+
+#ifdef	NO_CONV
+/* Build a small version (i.e. for a ramdisk root) */
+static void
+f_conv(char *arg)
+{
+
+	fprintf(stderr, "conv option disabled\n");
+	exit(1);
+	/* NOTREACHED */
+}
+#else	/* NO_CONV */
+
+static const struct conv {
+	const char *name;
+	u_int set, noset;
+	const u_char *ctab;
+} clist[] = {
+	{ "ascii",	C_ASCII,	C_EBCDIC,	e2a_POSIX },
+	{ "block",	C_BLOCK,	C_UNBLOCK,	NULL },
+	{ "ebcdic",	C_EBCDIC,	C_ASCII,	a2e_POSIX },
+	{ "ibm",	C_EBCDIC,	C_ASCII,	a2ibm_POSIX },
+	{ "lcase",	C_LCASE,	C_UCASE,	NULL },
+	{ "noerror",	C_NOERROR,	0,		NULL },
+	{ "notrunc",	C_NOTRUNC,	0,		NULL },
+	{ "oldascii",	C_ASCII,	C_EBCDIC,	e2a_32V },
+	{ "oldebcdic",	C_EBCDIC,	C_ASCII,	a2e_32V },
+	{ "oldibm",	C_EBCDIC,	C_ASCII,	a2ibm_32V },
+	{ "osync",	C_OSYNC,	C_BS,		NULL },
+	{ "sparse",	C_SPARSE,	0,		NULL },
+	{ "swab",	C_SWAB,		0,		NULL },
+	{ "sync",	C_SYNC,		0,		NULL },
+	{ "ucase",	C_UCASE,	C_LCASE,	NULL },
+	{ "unblock",	C_UNBLOCK,	C_BLOCK,	NULL },
+	/* If you add items to this table, be sure to add the
+	 * conversions to the C_BS check in the jcl routine above.
+	 */
+};
+
+static void
+f_conv(char *arg)
+{
+	struct conv *cp, tmp;
+
+	while (arg != NULL) {
+		tmp.name = strsep(&arg, ",");
+		if (!(cp = (struct conv *)bsearch(&tmp, clist,
+		    sizeof(clist)/sizeof(struct conv), sizeof(struct conv),
+		    c_conv))) {
+			errx(EXIT_FAILURE, "unknown conversion %s", tmp.name);
+			/* NOTREACHED */
+		}
+		if (ddflags & cp->noset) {
+			errx(EXIT_FAILURE, "%s: illegal conversion combination", tmp.name);
+			/* NOTREACHED */
+		}
+		ddflags |= cp->set;
+		if (cp->ctab)
+			ctab = cp->ctab;
+	}
+}
+
+static int
+c_conv(const void *a, const void *b)
+{
+
+	return (strcmp(((const struct conv *)a)->name,
+	    ((const struct conv *)b)->name));
+}
+
+#endif	/* NO_CONV */
diff --git a/toolbox/dd.h b/toolbox/dd.h
new file mode 100644
index 0000000..cca1024
--- /dev/null
+++ b/toolbox/dd.h
@@ -0,0 +1,93 @@
+/*	$NetBSD: dd.h,v 1.12 2004/01/17 20:48:57 dbj Exp $	*/
+
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego and Lance
+ * Visser of Convex Computer Corporation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)dd.h	8.3 (Berkeley) 4/2/94
+ */
+
+#include <stdint.h>
+
+/* Input/output stream state. */
+typedef struct {
+	u_char		*db;		/* buffer address */
+	u_char		*dbp;		/* current buffer I/O address */
+	uint64_t	dbcnt;		/* current buffer byte count */
+	int64_t		dbrcnt;		/* last read byte count */
+	uint64_t	dbsz;		/* buffer size */
+
+#define	ISCHR		0x01		/* character device (warn on short) */
+#define	ISPIPE		0x02		/* pipe (not truncatable) */
+#define	ISTAPE		0x04		/* tape (not seekable) */
+#define	NOREAD		0x08		/* not readable */
+	u_int		flags;
+
+	const char  	*name;		/* name */
+	int		fd;		/* file descriptor */
+	uint64_t	offset;		/* # of blocks to skip */
+} IO;
+
+typedef struct {
+	uint64_t	in_full;	/* # of full input blocks */
+	uint64_t	in_part;	/* # of partial input blocks */
+	uint64_t	out_full;	/* # of full output blocks */
+	uint64_t	out_part;	/* # of partial output blocks */
+	uint64_t	trunc;		/* # of truncated records */
+	uint64_t	swab;		/* # of odd-length swab blocks */
+	uint64_t	sparse;		/* # of sparse output blocks */
+	uint64_t	bytes;		/* # of bytes written */
+	struct timeval	start;		/* start time of dd */
+} STAT;
+
+/* Flags (in ddflags). */
+#define	C_ASCII		0x00001
+#define	C_BLOCK		0x00002
+#define	C_BS		0x00004
+#define	C_CBS		0x00008
+#define	C_COUNT		0x00010
+#define	C_EBCDIC	0x00020
+#define	C_FILES		0x00040
+#define	C_IBS		0x00080
+#define	C_IF		0x00100
+#define	C_LCASE		0x00200
+#define	C_NOERROR	0x00400
+#define	C_NOTRUNC	0x00800
+#define	C_OBS		0x01000
+#define	C_OF		0x02000
+#define	C_SEEK		0x04000
+#define	C_SKIP		0x08000
+#define	C_SWAB		0x10000
+#define	C_SYNC		0x20000
+#define	C_UCASE		0x40000
+#define	C_UNBLOCK	0x80000
+#define	C_OSYNC		0x100000
+#define	C_SPARSE	0x200000
diff --git a/toolbox/devmem.c b/toolbox/devmem.c
new file mode 100644
index 0000000..9f3780b
--- /dev/null
+++ b/toolbox/devmem.c
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+static int usage() {
+    fprintf(stdout, "Usage: devmem ADDRESS [WIDTH [VALUE]]\n");
+    fprintf(stdout, "       read from or write to device memory.\n");
+    fprintf(stdout, "\n");
+    fprintf(stdout, "              ADDRESS --   address of device memory\n");
+    fprintf(stdout, "              WIDTH   --   8, 16, or 32(default). required for write\n");
+    fprintf(stdout, "              VALUE   --   value to write. read if none is specified\n");
+    return -1;
+}
+
+int devmem_main(int argc, char *argv[])
+{
+    if(argc < 2 || argc > 4) {
+        return usage();
+    }
+
+    int is_write = (argc == 4);
+    unsigned long addr = 0, width = 32, value = 0;
+    addr = strtoul(argv[1], NULL, 0);
+    if (argc > 2) {
+        width = strtoul(argv[2], NULL, 0);
+        if (argc > 3) {
+            value = strtoul(argv[3], NULL, 0);
+        }
+    }
+
+    if (width != 8 && width != 16 && width != 32) {
+        fprintf(stderr, "width has to be 8, 16 or 32\n");
+        return usage();
+    }
+
+    if (addr % (width >> 3)) {
+        fprintf(stderr, "address 0x%lx is not %ldbit-aligned\n", addr, width);
+        return usage();
+    }
+
+    int fd = open("/dev/mem", is_write ? (O_RDWR | O_SYNC) :
+                  (O_RDONLY | O_SYNC));
+    if (fd == -1) {
+        fprintf(stderr, "cannot open /dev/mem, error %s\n", strerror(errno));
+        return -1;
+    }
+
+    int page_size = getpagesize();
+    void *base = mmap(NULL, page_size,
+                      is_write ? (PROT_READ | PROT_WRITE) : PROT_READ,
+                      MAP_SHARED, fd, addr & ~(page_size - 1));
+    if (base == MAP_FAILED) {
+        fprintf(stderr, "mmap failed, error %s\n", strerror(errno));
+        close(fd);
+        return -1;
+    }
+
+    void *virt = (char*)base + (addr & (page_size - 1));
+    if (is_write) {
+        switch (width) {
+        case 8:
+            *(uint8_t*)virt = (uint8_t)value;
+            break;
+        case 16:
+            *(uint16_t*)virt = (uint16_t)value;
+            break;
+        case 32:
+            *(uint32_t*)virt = (uint32_t)value;
+            break;
+        default:
+            fprintf(stderr, "not reached\n");
+            break;
+        }
+    } else {
+        switch (width) {
+        case 8:
+            fprintf(stdout, "0x%02"PRIX8"\n", *(uint8_t*)virt);
+            break;
+        case 16:
+            fprintf(stdout, "0x%04"PRIX16"\n", *(uint16_t*)virt);
+            break;
+        case 32:
+            fprintf(stdout, "0x%08"PRIX32"\n", *(uint32_t*)virt);
+            break;
+        default:
+            fprintf(stderr, "not reached\n");
+            break;
+        }
+    }
+    munmap(base, page_size);
+    close(fd);
+    return 0;
+}
diff --git a/toolbox/df.c b/toolbox/df.c
new file mode 100644
index 0000000..63940a1
--- /dev/null
+++ b/toolbox/df.c
@@ -0,0 +1,79 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/statfs.h>
+
+static int ok = EXIT_SUCCESS;
+
+static void printsize(long long n)
+{
+    char unit = 'K';
+    n /= 1024;
+    if (n > 1024) {
+        n /= 1024;
+        unit = 'M';
+    }
+    if (n > 1024) {
+        n /= 1024;
+        unit = 'G';
+    }
+    printf("%4lld%c", n, unit);
+}
+
+static void df(char *s, int always) {
+    struct statfs st;
+
+    if (statfs(s, &st) < 0) {
+        fprintf(stderr, "%s: %s\n", s, strerror(errno));
+        ok = EXIT_FAILURE;
+    } else {
+        if (st.f_blocks == 0 && !always)
+            return;        
+        printf("%-20s  ", s);
+        printsize((long long)st.f_blocks * (long long)st.f_bsize);
+        printf("  ");
+        printsize((long long)(st.f_blocks - (long long)st.f_bfree) * st.f_bsize);
+        printf("  ");
+        printsize((long long)st.f_bfree * (long long)st.f_bsize);
+        printf("   %d\n", (int) st.f_bsize);
+    }
+}
+
+int df_main(int argc, char *argv[]) {
+    printf("Filesystem             Size   Used   Free   Blksize\n");
+    if (argc == 1) {
+        char s[2000];
+        FILE *f = fopen("/proc/mounts", "r");
+
+        while (fgets(s, 2000, f)) {
+            char *c, *e = s;
+
+            for (c = s; *c; c++) {
+                if (*c == ' ') {
+                    e = c + 1;
+                    break;
+                }
+            }
+
+            for (c = e; *c; c++) {
+                if (*c == ' ') {
+                    *c = '\0';
+                    break;
+                }
+            }
+
+            df(e, 0);
+        }
+
+        fclose(f);
+    } else {
+        int i;
+
+        for (i = 1; i < argc; i++) {
+            df(argv[i], 1);
+        }
+    }
+
+    exit(ok);
+}
diff --git a/toolbox/dmesg.c b/toolbox/dmesg.c
new file mode 100644
index 0000000..cf29c6c
--- /dev/null
+++ b/toolbox/dmesg.c
@@ -0,0 +1,49 @@
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/klog.h>
+#include <string.h>
+
+#if HAVE_GLIBC
+/* GoogleTV: these constants are missing from glibc's sys/klog.h */
+#define KLOG_READ_ALL   3
+#define KLOG_READ_CLEAR 4
+#endif
+
+#define KLOG_BUF_SHIFT	17	/* CONFIG_LOG_BUF_SHIFT from our kernel */
+#define KLOG_BUF_LEN	(1 << KLOG_BUF_SHIFT)
+
+int dmesg_main(int argc, char **argv)
+{
+    char buffer[KLOG_BUF_LEN + 1];
+    char *p = buffer;
+    ssize_t ret;
+    int n, op;
+
+    if((argc == 2) && (!strcmp(argv[1],"-c"))) {
+        op = KLOG_READ_CLEAR;
+    } else {
+        op = KLOG_READ_ALL;
+    }
+
+    n = klogctl(op, buffer, KLOG_BUF_LEN);
+    if (n < 0) {
+        perror("klogctl");
+        return EXIT_FAILURE;
+    }
+    buffer[n] = '\0';
+
+    while((ret = write(STDOUT_FILENO, p, n))) {
+        if (ret == -1) {
+	    if (errno == EINTR)
+                continue;
+	    perror("write");
+	    return EXIT_FAILURE;
+	}
+	p += ret;
+	n -= ret;
+    }
+
+    return 0;
+}
diff --git a/toolbox/dynarray.c b/toolbox/dynarray.c
new file mode 100644
index 0000000..e9b7b03
--- /dev/null
+++ b/toolbox/dynarray.c
@@ -0,0 +1,103 @@
+#include "dynarray.h"
+#include <stdlib.h>
+#include <limits.h>
+
+void
+dynarray_init( dynarray_t *a )
+{
+    a->count = a->capacity = 0;
+    a->items = NULL;
+}
+
+
+static void
+dynarray_reserve_more( dynarray_t *a, int count )
+{
+    int old_cap = a->capacity;
+    int new_cap = old_cap;
+    const int max_cap = INT_MAX/sizeof(void*);
+    void** new_items;
+    int new_count = a->count + count;
+
+    if (count <= 0)
+        return;
+
+    if (count > max_cap - a->count)
+        abort();
+
+    new_count = a->count + count;
+
+    while (new_cap < new_count) {
+        old_cap = new_cap;
+        new_cap += (new_cap >> 2) + 4;
+        if (new_cap < old_cap || new_cap > max_cap) {
+            new_cap = max_cap;
+        }
+    }
+    new_items = realloc(a->items, new_cap*sizeof(void*));
+    if (new_items == NULL)
+        abort();
+
+    a->items = new_items;
+    a->capacity = new_cap;
+}
+
+void
+dynarray_append( dynarray_t *a, void* item )
+{
+    if (a->count >= a->capacity)
+        dynarray_reserve_more(a, 1);
+
+    a->items[a->count++] = item;
+}
+
+void
+dynarray_done( dynarray_t *a )
+{
+    free(a->items);
+    a->items = NULL;
+    a->count = a->capacity = 0;
+}
+
+// string arrays
+
+void strlist_init( strlist_t *list )
+{
+    dynarray_init(list);
+}
+
+void strlist_append_b( strlist_t *list, const void* str, size_t  slen )
+{
+    char *copy = malloc(slen+1);
+    memcpy(copy, str, slen);
+    copy[slen] = '\0';
+    dynarray_append(list, copy);
+}
+
+void strlist_append_dup( strlist_t *list, const char *str)
+{
+    strlist_append_b(list, str, strlen(str));
+}
+
+void strlist_done( strlist_t *list )
+{
+    STRLIST_FOREACH(list, string, free(string));
+    dynarray_done(list);
+}
+
+static int strlist_compare_strings(const void* a, const void* b)
+{
+    const char *sa = *(const char **)a;
+    const char *sb = *(const char **)b;
+    return strcmp(sa, sb);
+}
+
+void strlist_sort( strlist_t *list )
+{
+    if (list->count > 0) {
+        qsort(list->items,
+              (size_t)list->count,
+              sizeof(void*),
+              strlist_compare_strings);
+    }
+}
diff --git a/toolbox/dynarray.h b/toolbox/dynarray.h
new file mode 100644
index 0000000..f73fb3b
--- /dev/null
+++ b/toolbox/dynarray.h
@@ -0,0 +1,80 @@
+#ifndef DYNARRAY_H
+#define DYNARRAY_H
+
+#include <stddef.h>
+
+/* simple dynamic array of pointers */
+typedef struct {
+    int count;
+    int capacity;
+    void** items;
+} dynarray_t;
+
+#define DYNARRAY_INITIALIZER  { 0, 0, NULL }
+
+void dynarray_init( dynarray_t *a );
+void dynarray_done( dynarray_t *a );
+
+void dynarray_append( dynarray_t *a, void* item );
+
+/* Used to iterate over a dynarray_t
+ * _array :: pointer to the array
+ * _item_type :: type of objects pointed to by the array
+ * _item      :: name of a local variable defined within the loop
+ *               with type '_item_type'
+ * _stmnt     :: C statement that will be executed in each iteration.
+ *
+ * You case use 'break' and 'continue' within _stmnt
+ *
+ * This macro is only intended for simple uses. I.e. do not add or
+ * remove items from the array during iteration.
+ */
+#define DYNARRAY_FOREACH_TYPE(_array,_item_type,_item,_stmnt) \
+    do { \
+        int _nn_##__LINE__ = 0; \
+        for (;_nn_##__LINE__ < (_array)->count; ++ _nn_##__LINE__) { \
+            _item_type _item = (_item_type)(_array)->items[_nn_##__LINE__]; \
+            _stmnt; \
+        } \
+    } while (0)
+
+#define DYNARRAY_FOREACH(_array,_item,_stmnt) \
+    DYNARRAY_FOREACH_TYPE(_array,void *,_item,_stmnt)
+
+/* Simple dynamic string arrays
+ *
+ * NOTE: A strlist_t owns the strings it references.
+ */
+typedef dynarray_t  strlist_t;
+
+#define  STRLIST_INITIALIZER  DYNARRAY_INITIALIZER
+
+/* Used to iterate over a strlist_t
+ * _list   :: pointer to strlist_t object
+ * _string :: name of local variable name defined within the loop with
+ *            type 'char*'
+ * _stmnt  :: C statement executed in each iteration
+ *
+ * This macro is only intended for simple uses. Do not add or remove items
+ * to/from the list during iteration.
+ */
+#define  STRLIST_FOREACH(_list,_string,_stmnt) \
+    DYNARRAY_FOREACH_TYPE(_list,char *,_string,_stmnt)
+
+void strlist_init( strlist_t *list );
+
+/* note: strlist_done will free all the strings owned by the list */
+void strlist_done( strlist_t *list );
+
+/* append a new string made of the first 'slen' characters from 'str'
+ * followed by a trailing zero.
+ */
+void strlist_append_b( strlist_t *list, const void* str, size_t  slen );
+
+/* append the copy of a given input string to a strlist_t */
+void strlist_append_dup( strlist_t *list, const char *str);
+
+/* sort the strings in a given list (using strcmp) */
+void strlist_sort( strlist_t *list );
+
+#endif /* DYNARRAY_H */
\ No newline at end of file
diff --git a/toolbox/exists.c b/toolbox/exists.c
new file mode 100644
index 0000000..e348668
--- /dev/null
+++ b/toolbox/exists.c
@@ -0,0 +1,16 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+int exists_main(int argc, char *argv[])
+{
+    struct stat s;
+
+    if(argc < 2) return 1;
+
+    if(stat(argv[1], &s)) {
+        return 1;
+    } else {
+        return 0;
+    }
+}
diff --git a/toolbox/getenforce.c b/toolbox/getenforce.c
new file mode 100644
index 0000000..9e7589a
--- /dev/null
+++ b/toolbox/getenforce.c
@@ -0,0 +1,30 @@
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <selinux/selinux.h>
+
+int getenforce_main(int argc, char **argv)
+{
+    int rc;
+
+    rc = is_selinux_enabled();
+    if (rc <= 0) {
+        printf("Disabled\n");
+        return 0;
+    }
+
+    rc = security_getenforce();
+    if (rc < 0) {
+        fprintf(stderr, "Could not get enforcing status:  %s\n",
+                strerror(errno));
+        return 2;
+    }
+
+    if (rc)
+        printf("Enforcing\n");
+    else
+        printf("Permissive\n");
+
+    return 0;
+}
diff --git a/toolbox/getevent.c b/toolbox/getevent.c
new file mode 100644
index 0000000..07b1af9
--- /dev/null
+++ b/toolbox/getevent.c
@@ -0,0 +1,671 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/inotify.h>
+#if HAVE_GLIBC
+#include <limits.h>
+#else
+#include <sys/limits.h>
+#endif
+#include <sys/poll.h>
+#include <linux/input.h>
+#include <errno.h>
+
+#include "getevent.h"
+
+static struct pollfd *ufds;
+static char **device_names;
+static int nfds;
+
+enum {
+    PRINT_DEVICE_ERRORS     = 1U << 0,
+    PRINT_DEVICE            = 1U << 1,
+    PRINT_DEVICE_NAME       = 1U << 2,
+    PRINT_DEVICE_INFO       = 1U << 3,
+    PRINT_VERSION           = 1U << 4,
+    PRINT_POSSIBLE_EVENTS   = 1U << 5,
+    PRINT_INPUT_PROPS       = 1U << 6,
+    PRINT_HID_DESCRIPTOR    = 1U << 7,
+
+    PRINT_ALL_INFO          = (1U << 8) - 1,
+
+    PRINT_LABELS            = 1U << 16,
+};
+
+static const char *get_label(const struct label *labels, int value)
+{
+    while(labels->name && value != labels->value) {
+        labels++;
+    }
+    return labels->name;
+}
+
+static int print_input_props(int fd)
+{
+    uint8_t bits[INPUT_PROP_CNT / 8];
+    int i, j;
+    int res;
+    int count;
+    const char *bit_label;
+
+    printf("  input props:\n");
+    res = ioctl(fd, EVIOCGPROP(sizeof(bits)), bits);
+    if(res < 0) {
+        printf("    <not available\n");
+        return 1;
+    }
+    count = 0;
+    for(i = 0; i < res; i++) {
+        for(j = 0; j < 8; j++) {
+            if (bits[i] & 1 << j) {
+                bit_label = get_label(input_prop_labels, i * 8 + j);
+                if(bit_label)
+                    printf("    %s\n", bit_label);
+                else
+                    printf("    %04x\n", i * 8 + j);
+                count++;
+            }
+        }
+    }
+    if (!count)
+        printf("    <none>\n");
+    return 0;
+}
+
+static int print_possible_events(int fd, int print_flags)
+{
+    uint8_t *bits = NULL;
+    ssize_t bits_size = 0;
+    const char* label;
+    int i, j, k;
+    int res, res2;
+    struct label* bit_labels;
+    const char *bit_label;
+
+    printf("  events:\n");
+    for(i = EV_KEY; i <= EV_MAX; i++) { // skip EV_SYN since we cannot query its available codes
+        int count = 0;
+        while(1) {
+            res = ioctl(fd, EVIOCGBIT(i, bits_size), bits);
+            if(res < bits_size)
+                break;
+            bits_size = res + 16;
+            bits = realloc(bits, bits_size * 2);
+            if(bits == NULL) {
+                fprintf(stderr, "failed to allocate buffer of size %d\n", (int)bits_size);
+                return 1;
+            }
+        }
+        res2 = 0;
+        switch(i) {
+            case EV_KEY:
+                res2 = ioctl(fd, EVIOCGKEY(res), bits + bits_size);
+                label = "KEY";
+                bit_labels = key_labels;
+                break;
+            case EV_REL:
+                label = "REL";
+                bit_labels = rel_labels;
+                break;
+            case EV_ABS:
+                label = "ABS";
+                bit_labels = abs_labels;
+                break;
+            case EV_MSC:
+                label = "MSC";
+                bit_labels = msc_labels;
+                break;
+            case EV_LED:
+                res2 = ioctl(fd, EVIOCGLED(res), bits + bits_size);
+                label = "LED";
+                bit_labels = led_labels;
+                break;
+            case EV_SND:
+                res2 = ioctl(fd, EVIOCGSND(res), bits + bits_size);
+                label = "SND";
+                bit_labels = snd_labels;
+                break;
+            case EV_SW:
+                res2 = ioctl(fd, EVIOCGSW(bits_size), bits + bits_size);
+                label = "SW ";
+                bit_labels = sw_labels;
+                break;
+            case EV_REP:
+                label = "REP";
+                bit_labels = rep_labels;
+                break;
+            case EV_FF:
+                label = "FF ";
+                bit_labels = ff_labels;
+                break;
+            case EV_PWR:
+                label = "PWR";
+                bit_labels = NULL;
+                break;
+            case EV_FF_STATUS:
+                label = "FFS";
+                bit_labels = ff_status_labels;
+                break;
+            default:
+                res2 = 0;
+                label = "???";
+                bit_labels = NULL;
+        }
+        for(j = 0; j < res; j++) {
+            for(k = 0; k < 8; k++)
+                if(bits[j] & 1 << k) {
+                    char down;
+                    if(j < res2 && (bits[j + bits_size] & 1 << k))
+                        down = '*';
+                    else
+                        down = ' ';
+                    if(count == 0)
+                        printf("    %s (%04x):", label, i);
+                    else if((count & (print_flags & PRINT_LABELS ? 0x3 : 0x7)) == 0 || i == EV_ABS)
+                        printf("\n               ");
+                    if(bit_labels && (print_flags & PRINT_LABELS)) {
+                        bit_label = get_label(bit_labels, j * 8 + k);
+                        if(bit_label)
+                            printf(" %.20s%c%*s", bit_label, down, 20 - strlen(bit_label), "");
+                        else
+                            printf(" %04x%c                ", j * 8 + k, down);
+                    } else {
+                        printf(" %04x%c", j * 8 + k, down);
+                    }
+                    if(i == EV_ABS) {
+                        struct input_absinfo abs;
+                        if(ioctl(fd, EVIOCGABS(j * 8 + k), &abs) == 0) {
+                            printf(" : value %d, min %d, max %d, fuzz %d, flat %d, resolution %d",
+                                abs.value, abs.minimum, abs.maximum, abs.fuzz, abs.flat,
+                                abs.resolution);
+                        }
+                    }
+                    count++;
+                }
+        }
+        if(count)
+            printf("\n");
+    }
+    free(bits);
+    return 0;
+}
+
+static void print_event(int type, int code, int value, int print_flags)
+{
+    const char *type_label, *code_label, *value_label;
+
+    if (print_flags & PRINT_LABELS) {
+        type_label = get_label(ev_labels, type);
+        code_label = NULL;
+        value_label = NULL;
+
+        switch(type) {
+            case EV_SYN:
+                code_label = get_label(syn_labels, code);
+                break;
+            case EV_KEY:
+                code_label = get_label(key_labels, code);
+                value_label = get_label(key_value_labels, value);
+                break;
+            case EV_REL:
+                code_label = get_label(rel_labels, code);
+                break;
+            case EV_ABS:
+                code_label = get_label(abs_labels, code);
+                switch(code) {
+                    case ABS_MT_TOOL_TYPE:
+                        value_label = get_label(mt_tool_labels, value);
+                }
+                break;
+            case EV_MSC:
+                code_label = get_label(msc_labels, code);
+                break;
+            case EV_LED:
+                code_label = get_label(led_labels, code);
+                break;
+            case EV_SND:
+                code_label = get_label(snd_labels, code);
+                break;
+            case EV_SW:
+                code_label = get_label(sw_labels, code);
+                break;
+            case EV_REP:
+                code_label = get_label(rep_labels, code);
+                break;
+            case EV_FF:
+                code_label = get_label(ff_labels, code);
+                break;
+            case EV_FF_STATUS:
+                code_label = get_label(ff_status_labels, code);
+                break;
+        }
+
+        if (type_label)
+            printf("%-12.12s", type_label);
+        else
+            printf("%04x        ", type);
+        if (code_label)
+            printf(" %-20.20s", code_label);
+        else
+            printf(" %04x                ", code);
+        if (value_label)
+            printf(" %-20.20s", value_label);
+        else
+            printf(" %08x            ", value);
+    } else {
+        printf("%04x %04x %08x", type, code, value);
+    }
+}
+
+static void print_hid_descriptor(int bus, int vendor, int product)
+{
+    const char *dirname = "/sys/kernel/debug/hid";
+    char prefix[16];
+    DIR *dir;
+    struct dirent *de;
+    char filename[PATH_MAX];
+    FILE *file;
+    char line[2048];
+
+    snprintf(prefix, sizeof(prefix), "%04X:%04X:%04X.", bus, vendor, product);
+
+    dir = opendir(dirname);
+    if(dir == NULL)
+        return;
+    while((de = readdir(dir))) {
+        if (strstr(de->d_name, prefix) == de->d_name) {
+            snprintf(filename, sizeof(filename), "%s/%s/rdesc", dirname, de->d_name);
+
+            file = fopen(filename, "r");
+            if (file) {
+                printf("  HID descriptor: %s\n\n", de->d_name);
+                while (fgets(line, sizeof(line), file)) {
+                    fputs("    ", stdout);
+                    fputs(line, stdout);
+                }
+                fclose(file);
+                puts("");
+            }
+        }
+    }
+    closedir(dir);
+}
+
+static int open_device(const char *device, int print_flags)
+{
+    int version;
+    int fd;
+    struct pollfd *new_ufds;
+    char **new_device_names;
+    char name[80];
+    char location[80];
+    char idstr[80];
+    struct input_id id;
+
+    fd = open(device, O_RDWR);
+    if(fd < 0) {
+        if(print_flags & PRINT_DEVICE_ERRORS)
+            fprintf(stderr, "could not open %s, %s\n", device, strerror(errno));
+        return -1;
+    }
+    
+    if(ioctl(fd, EVIOCGVERSION, &version)) {
+        if(print_flags & PRINT_DEVICE_ERRORS)
+            fprintf(stderr, "could not get driver version for %s, %s\n", device, strerror(errno));
+        return -1;
+    }
+    if(ioctl(fd, EVIOCGID, &id)) {
+        if(print_flags & PRINT_DEVICE_ERRORS)
+            fprintf(stderr, "could not get driver id for %s, %s\n", device, strerror(errno));
+        return -1;
+    }
+    name[sizeof(name) - 1] = '\0';
+    location[sizeof(location) - 1] = '\0';
+    idstr[sizeof(idstr) - 1] = '\0';
+    if(ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) < 1) {
+        //fprintf(stderr, "could not get device name for %s, %s\n", device, strerror(errno));
+        name[0] = '\0';
+    }
+    if(ioctl(fd, EVIOCGPHYS(sizeof(location) - 1), &location) < 1) {
+        //fprintf(stderr, "could not get location for %s, %s\n", device, strerror(errno));
+        location[0] = '\0';
+    }
+    if(ioctl(fd, EVIOCGUNIQ(sizeof(idstr) - 1), &idstr) < 1) {
+        //fprintf(stderr, "could not get idstring for %s, %s\n", device, strerror(errno));
+        idstr[0] = '\0';
+    }
+
+    new_ufds = realloc(ufds, sizeof(ufds[0]) * (nfds + 1));
+    if(new_ufds == NULL) {
+        fprintf(stderr, "out of memory\n");
+        return -1;
+    }
+    ufds = new_ufds;
+    new_device_names = realloc(device_names, sizeof(device_names[0]) * (nfds + 1));
+    if(new_device_names == NULL) {
+        fprintf(stderr, "out of memory\n");
+        return -1;
+    }
+    device_names = new_device_names;
+
+    if(print_flags & PRINT_DEVICE)
+        printf("add device %d: %s\n", nfds, device);
+    if(print_flags & PRINT_DEVICE_INFO)
+        printf("  bus:      %04x\n"
+               "  vendor    %04x\n"
+               "  product   %04x\n"
+               "  version   %04x\n",
+               id.bustype, id.vendor, id.product, id.version);
+    if(print_flags & PRINT_DEVICE_NAME)
+        printf("  name:     \"%s\"\n", name);
+    if(print_flags & PRINT_DEVICE_INFO)
+        printf("  location: \"%s\"\n"
+               "  id:       \"%s\"\n", location, idstr);
+    if(print_flags & PRINT_VERSION)
+        printf("  version:  %d.%d.%d\n",
+               version >> 16, (version >> 8) & 0xff, version & 0xff);
+
+    if(print_flags & PRINT_POSSIBLE_EVENTS) {
+        print_possible_events(fd, print_flags);
+    }
+
+    if(print_flags & PRINT_INPUT_PROPS) {
+        print_input_props(fd);
+    }
+    if(print_flags & PRINT_HID_DESCRIPTOR) {
+        print_hid_descriptor(id.bustype, id.vendor, id.product);
+    }
+
+    ufds[nfds].fd = fd;
+    ufds[nfds].events = POLLIN;
+    device_names[nfds] = strdup(device);
+    nfds++;
+
+    return 0;
+}
+
+int close_device(const char *device, int print_flags)
+{
+    int i;
+    for(i = 1; i < nfds; i++) {
+        if(strcmp(device_names[i], device) == 0) {
+            int count = nfds - i - 1;
+            if(print_flags & PRINT_DEVICE)
+                printf("remove device %d: %s\n", i, device);
+            free(device_names[i]);
+            memmove(device_names + i, device_names + i + 1, sizeof(device_names[0]) * count);
+            memmove(ufds + i, ufds + i + 1, sizeof(ufds[0]) * count);
+            nfds--;
+            return 0;
+        }
+    }
+    if(print_flags & PRINT_DEVICE_ERRORS)
+        fprintf(stderr, "remote device: %s not found\n", device);
+    return -1;
+}
+
+static int read_notify(const char *dirname, int nfd, int print_flags)
+{
+    int res;
+    char devname[PATH_MAX];
+    char *filename;
+    char event_buf[512];
+    int event_size;
+    int event_pos = 0;
+    struct inotify_event *event;
+
+    res = read(nfd, event_buf, sizeof(event_buf));
+    if(res < (int)sizeof(*event)) {
+        if(errno == EINTR)
+            return 0;
+        fprintf(stderr, "could not get event, %s\n", strerror(errno));
+        return 1;
+    }
+    //printf("got %d bytes of event information\n", res);
+
+    strcpy(devname, dirname);
+    filename = devname + strlen(devname);
+    *filename++ = '/';
+
+    while(res >= (int)sizeof(*event)) {
+        event = (struct inotify_event *)(event_buf + event_pos);
+        //printf("%d: %08x \"%s\"\n", event->wd, event->mask, event->len ? event->name : "");
+        if(event->len) {
+            strcpy(filename, event->name);
+            if(event->mask & IN_CREATE) {
+                open_device(devname, print_flags);
+            }
+            else {
+                close_device(devname, print_flags);
+            }
+        }
+        event_size = sizeof(*event) + event->len;
+        res -= event_size;
+        event_pos += event_size;
+    }
+    return 0;
+}
+
+static int scan_dir(const char *dirname, int print_flags)
+{
+    char devname[PATH_MAX];
+    char *filename;
+    DIR *dir;
+    struct dirent *de;
+    dir = opendir(dirname);
+    if(dir == NULL)
+        return -1;
+    strcpy(devname, dirname);
+    filename = devname + strlen(devname);
+    *filename++ = '/';
+    while((de = readdir(dir))) {
+        if(de->d_name[0] == '.' &&
+           (de->d_name[1] == '\0' ||
+            (de->d_name[1] == '.' && de->d_name[2] == '\0')))
+            continue;
+        strcpy(filename, de->d_name);
+        open_device(devname, print_flags);
+    }
+    closedir(dir);
+    return 0;
+}
+
+static void usage(int argc, char *argv[])
+{
+    fprintf(stderr, "Usage: %s [-t] [-n] [-s switchmask] [-S] [-v [mask]] [-d] [-p] [-i] [-l] [-q] [-c count] [-r] [device]\n", argv[0]);
+    fprintf(stderr, "    -t: show time stamps\n");
+    fprintf(stderr, "    -n: don't print newlines\n");
+    fprintf(stderr, "    -s: print switch states for given bits\n");
+    fprintf(stderr, "    -S: print all switch states\n");
+    fprintf(stderr, "    -v: verbosity mask (errs=1, dev=2, name=4, info=8, vers=16, pos. events=32, props=64)\n");
+    fprintf(stderr, "    -d: show HID descriptor, if available\n");
+    fprintf(stderr, "    -p: show possible events (errs, dev, name, pos. events)\n");
+    fprintf(stderr, "    -i: show all device info and possible events\n");
+    fprintf(stderr, "    -l: label event types and names in plain text\n");
+    fprintf(stderr, "    -q: quiet (clear verbosity mask)\n");
+    fprintf(stderr, "    -c: print given number of events then exit\n");
+    fprintf(stderr, "    -r: print rate events are received\n");
+}
+
+int getevent_main(int argc, char *argv[])
+{
+    int c;
+    int i;
+    int res;
+    int pollres;
+    int get_time = 0;
+    int print_device = 0;
+    char *newline = "\n";
+    uint16_t get_switch = 0;
+    struct input_event event;
+    int version;
+    int print_flags = 0;
+    int print_flags_set = 0;
+    int dont_block = -1;
+    int event_count = 0;
+    int sync_rate = 0;
+    int64_t last_sync_time = 0;
+    const char *device = NULL;
+    const char *device_path = "/dev/input";
+
+    opterr = 0;
+    do {
+        c = getopt(argc, argv, "tns:Sv::dpilqc:rh");
+        if (c == EOF)
+            break;
+        switch (c) {
+        case 't':
+            get_time = 1;
+            break;
+        case 'n':
+            newline = "";
+            break;
+        case 's':
+            get_switch = strtoul(optarg, NULL, 0);
+            if(dont_block == -1)
+                dont_block = 1;
+            break;
+        case 'S':
+            get_switch = ~0;
+            if(dont_block == -1)
+                dont_block = 1;
+            break;
+        case 'v':
+            if(optarg)
+                print_flags |= strtoul(optarg, NULL, 0);
+            else
+                print_flags |= PRINT_DEVICE | PRINT_DEVICE_NAME | PRINT_DEVICE_INFO | PRINT_VERSION;
+            print_flags_set = 1;
+            break;
+        case 'd':
+            print_flags |= PRINT_HID_DESCRIPTOR;
+            break;
+        case 'p':
+            print_flags |= PRINT_DEVICE_ERRORS | PRINT_DEVICE
+                    | PRINT_DEVICE_NAME | PRINT_POSSIBLE_EVENTS | PRINT_INPUT_PROPS;
+            print_flags_set = 1;
+            if(dont_block == -1)
+                dont_block = 1;
+            break;
+        case 'i':
+            print_flags |= PRINT_ALL_INFO;
+            print_flags_set = 1;
+            if(dont_block == -1)
+                dont_block = 1;
+            break;
+        case 'l':
+            print_flags |= PRINT_LABELS;
+            break;
+        case 'q':
+            print_flags_set = 1;
+            break;
+        case 'c':
+            event_count = atoi(optarg);
+            dont_block = 0;
+            break;
+        case 'r':
+            sync_rate = 1;
+            break;
+        case '?':
+            fprintf(stderr, "%s: invalid option -%c\n",
+                argv[0], optopt);
+        case 'h':
+            usage(argc, argv);
+            exit(1);
+        }
+    } while (1);
+    if(dont_block == -1)
+        dont_block = 0;
+
+    if (optind + 1 == argc) {
+        device = argv[optind];
+        optind++;
+    }
+    if (optind != argc) {
+        usage(argc, argv);
+        exit(1);
+    }
+    nfds = 1;
+    ufds = calloc(1, sizeof(ufds[0]));
+    ufds[0].fd = inotify_init();
+    ufds[0].events = POLLIN;
+    if(device) {
+        if(!print_flags_set)
+            print_flags |= PRINT_DEVICE_ERRORS;
+        res = open_device(device, print_flags);
+        if(res < 0) {
+            return 1;
+        }
+    } else {
+        if(!print_flags_set)
+            print_flags |= PRINT_DEVICE_ERRORS | PRINT_DEVICE | PRINT_DEVICE_NAME;
+        print_device = 1;
+		res = inotify_add_watch(ufds[0].fd, device_path, IN_DELETE | IN_CREATE);
+        if(res < 0) {
+            fprintf(stderr, "could not add watch for %s, %s\n", device_path, strerror(errno));
+            return 1;
+        }
+        res = scan_dir(device_path, print_flags);
+        if(res < 0) {
+            fprintf(stderr, "scan dir failed for %s\n", device_path);
+            return 1;
+        }
+    }
+
+    if(get_switch) {
+        for(i = 1; i < nfds; i++) {
+            uint16_t sw;
+            res = ioctl(ufds[i].fd, EVIOCGSW(1), &sw);
+            if(res < 0) {
+                fprintf(stderr, "could not get switch state, %s\n", strerror(errno));
+                return 1;
+            }
+            sw &= get_switch;
+            printf("%04x%s", sw, newline);
+        }
+    }
+
+    if(dont_block)
+        return 0;
+
+    while(1) {
+        pollres = poll(ufds, nfds, -1);
+        //printf("poll %d, returned %d\n", nfds, pollres);
+        if(ufds[0].revents & POLLIN) {
+            read_notify(device_path, ufds[0].fd, print_flags);
+        }
+        for(i = 1; i < nfds; i++) {
+            if(ufds[i].revents) {
+                if(ufds[i].revents & POLLIN) {
+                    res = read(ufds[i].fd, &event, sizeof(event));
+                    if(res < (int)sizeof(event)) {
+                        fprintf(stderr, "could not get event\n");
+                        return 1;
+                    }
+                    if(get_time) {
+                        printf("%ld-%ld: ", event.time.tv_sec, event.time.tv_usec);
+                    }
+                    if(print_device)
+                        printf("%s: ", device_names[i]);
+                    print_event(event.type, event.code, event.value, print_flags);
+                    if(sync_rate && event.type == 0 && event.code == 0) {
+                        int64_t now = event.time.tv_sec * 1000000LL + event.time.tv_usec;
+                        if(last_sync_time)
+                            printf(" rate %lld", 1000000LL / (now - last_sync_time));
+                        last_sync_time = now;
+                    }
+                    printf("%s", newline);
+                    if(event_count && --event_count == 0)
+                        return 0;
+                }
+            }
+        }
+    }
+
+    return 0;
+}
diff --git a/toolbox/getevent.h b/toolbox/getevent.h
new file mode 100644
index 0000000..2b76209
--- /dev/null
+++ b/toolbox/getevent.h
@@ -0,0 +1,725 @@
+#include <linux/input.h>
+
+struct label {
+    const char *name;
+    int value;
+};
+
+#define LABEL(constant) { #constant, constant }
+#define LABEL_END { NULL, -1 }
+
+static struct label input_prop_labels[] = {
+        LABEL(INPUT_PROP_POINTER),
+        LABEL(INPUT_PROP_DIRECT),
+        LABEL(INPUT_PROP_BUTTONPAD),
+        LABEL(INPUT_PROP_SEMI_MT),
+        LABEL_END,
+};
+
+static struct label ev_labels[] = {
+        LABEL(EV_SYN),
+        LABEL(EV_KEY),
+        LABEL(EV_REL),
+        LABEL(EV_ABS),
+        LABEL(EV_MSC),
+        LABEL(EV_SW),
+        LABEL(EV_LED),
+        LABEL(EV_SND),
+        LABEL(EV_REP),
+        LABEL(EV_FF),
+        LABEL(EV_PWR),
+        LABEL(EV_FF_STATUS),
+        LABEL_END,
+};
+
+static struct label syn_labels[] = {
+        LABEL(SYN_REPORT),
+        LABEL(SYN_CONFIG),
+        LABEL(SYN_MT_REPORT),
+        LABEL(SYN_DROPPED),
+        LABEL_END,
+};
+
+static struct label key_labels[] = {
+        LABEL(KEY_RESERVED),
+        LABEL(KEY_ESC),
+        LABEL(KEY_1),
+        LABEL(KEY_2),
+        LABEL(KEY_3),
+        LABEL(KEY_4),
+        LABEL(KEY_5),
+        LABEL(KEY_6),
+        LABEL(KEY_7),
+        LABEL(KEY_8),
+        LABEL(KEY_9),
+        LABEL(KEY_0),
+        LABEL(KEY_MINUS),
+        LABEL(KEY_EQUAL),
+        LABEL(KEY_BACKSPACE),
+        LABEL(KEY_TAB),
+        LABEL(KEY_Q),
+        LABEL(KEY_W),
+        LABEL(KEY_E),
+        LABEL(KEY_R),
+        LABEL(KEY_T),
+        LABEL(KEY_Y),
+        LABEL(KEY_U),
+        LABEL(KEY_I),
+        LABEL(KEY_O),
+        LABEL(KEY_P),
+        LABEL(KEY_LEFTBRACE),
+        LABEL(KEY_RIGHTBRACE),
+        LABEL(KEY_ENTER),
+        LABEL(KEY_LEFTCTRL),
+        LABEL(KEY_A),
+        LABEL(KEY_S),
+        LABEL(KEY_D),
+        LABEL(KEY_F),
+        LABEL(KEY_G),
+        LABEL(KEY_H),
+        LABEL(KEY_J),
+        LABEL(KEY_K),
+        LABEL(KEY_L),
+        LABEL(KEY_SEMICOLON),
+        LABEL(KEY_APOSTROPHE),
+        LABEL(KEY_GRAVE),
+        LABEL(KEY_LEFTSHIFT),
+        LABEL(KEY_BACKSLASH),
+        LABEL(KEY_Z),
+        LABEL(KEY_X),
+        LABEL(KEY_C),
+        LABEL(KEY_V),
+        LABEL(KEY_B),
+        LABEL(KEY_N),
+        LABEL(KEY_M),
+        LABEL(KEY_COMMA),
+        LABEL(KEY_DOT),
+        LABEL(KEY_SLASH),
+        LABEL(KEY_RIGHTSHIFT),
+        LABEL(KEY_KPASTERISK),
+        LABEL(KEY_LEFTALT),
+        LABEL(KEY_SPACE),
+        LABEL(KEY_CAPSLOCK),
+        LABEL(KEY_F1),
+        LABEL(KEY_F2),
+        LABEL(KEY_F3),
+        LABEL(KEY_F4),
+        LABEL(KEY_F5),
+        LABEL(KEY_F6),
+        LABEL(KEY_F7),
+        LABEL(KEY_F8),
+        LABEL(KEY_F9),
+        LABEL(KEY_F10),
+        LABEL(KEY_NUMLOCK),
+        LABEL(KEY_SCROLLLOCK),
+        LABEL(KEY_KP7),
+        LABEL(KEY_KP8),
+        LABEL(KEY_KP9),
+        LABEL(KEY_KPMINUS),
+        LABEL(KEY_KP4),
+        LABEL(KEY_KP5),
+        LABEL(KEY_KP6),
+        LABEL(KEY_KPPLUS),
+        LABEL(KEY_KP1),
+        LABEL(KEY_KP2),
+        LABEL(KEY_KP3),
+        LABEL(KEY_KP0),
+        LABEL(KEY_KPDOT),
+        LABEL(KEY_ZENKAKUHANKAKU),
+        LABEL(KEY_102ND),
+        LABEL(KEY_F11),
+        LABEL(KEY_F12),
+        LABEL(KEY_RO),
+        LABEL(KEY_KATAKANA),
+        LABEL(KEY_HIRAGANA),
+        LABEL(KEY_HENKAN),
+        LABEL(KEY_KATAKANAHIRAGANA),
+        LABEL(KEY_MUHENKAN),
+        LABEL(KEY_KPJPCOMMA),
+        LABEL(KEY_KPENTER),
+        LABEL(KEY_RIGHTCTRL),
+        LABEL(KEY_KPSLASH),
+        LABEL(KEY_SYSRQ),
+        LABEL(KEY_RIGHTALT),
+        LABEL(KEY_LINEFEED),
+        LABEL(KEY_HOME),
+        LABEL(KEY_UP),
+        LABEL(KEY_PAGEUP),
+        LABEL(KEY_LEFT),
+        LABEL(KEY_RIGHT),
+        LABEL(KEY_END),
+        LABEL(KEY_DOWN),
+        LABEL(KEY_PAGEDOWN),
+        LABEL(KEY_INSERT),
+        LABEL(KEY_DELETE),
+        LABEL(KEY_MACRO),
+        LABEL(KEY_MUTE),
+        LABEL(KEY_VOLUMEDOWN),
+        LABEL(KEY_VOLUMEUP),
+        LABEL(KEY_POWER),
+        LABEL(KEY_KPEQUAL),
+        LABEL(KEY_KPPLUSMINUS),
+        LABEL(KEY_PAUSE),
+        LABEL(KEY_SCALE),
+        LABEL(KEY_KPCOMMA),
+        LABEL(KEY_HANGEUL),
+        LABEL(KEY_HANGUEL),
+        LABEL(KEY_HANJA),
+        LABEL(KEY_YEN),
+        LABEL(KEY_LEFTMETA),
+        LABEL(KEY_RIGHTMETA),
+        LABEL(KEY_COMPOSE),
+        LABEL(KEY_STOP),
+        LABEL(KEY_AGAIN),
+        LABEL(KEY_PROPS),
+        LABEL(KEY_UNDO),
+        LABEL(KEY_FRONT),
+        LABEL(KEY_COPY),
+        LABEL(KEY_OPEN),
+        LABEL(KEY_PASTE),
+        LABEL(KEY_FIND),
+        LABEL(KEY_CUT),
+        LABEL(KEY_HELP),
+        LABEL(KEY_MENU),
+        LABEL(KEY_CALC),
+        LABEL(KEY_SETUP),
+        LABEL(KEY_SLEEP),
+        LABEL(KEY_WAKEUP),
+        LABEL(KEY_FILE),
+        LABEL(KEY_SENDFILE),
+        LABEL(KEY_DELETEFILE),
+        LABEL(KEY_XFER),
+        LABEL(KEY_PROG1),
+        LABEL(KEY_PROG2),
+        LABEL(KEY_WWW),
+        LABEL(KEY_MSDOS),
+        LABEL(KEY_COFFEE),
+        LABEL(KEY_SCREENLOCK),
+        LABEL(KEY_DIRECTION),
+        LABEL(KEY_CYCLEWINDOWS),
+        LABEL(KEY_MAIL),
+        LABEL(KEY_BOOKMARKS),
+        LABEL(KEY_COMPUTER),
+        LABEL(KEY_BACK),
+        LABEL(KEY_FORWARD),
+        LABEL(KEY_CLOSECD),
+        LABEL(KEY_EJECTCD),
+        LABEL(KEY_EJECTCLOSECD),
+        LABEL(KEY_NEXTSONG),
+        LABEL(KEY_PLAYPAUSE),
+        LABEL(KEY_PREVIOUSSONG),
+        LABEL(KEY_STOPCD),
+        LABEL(KEY_RECORD),
+        LABEL(KEY_REWIND),
+        LABEL(KEY_PHONE),
+        LABEL(KEY_ISO),
+        LABEL(KEY_CONFIG),
+        LABEL(KEY_HOMEPAGE),
+        LABEL(KEY_REFRESH),
+        LABEL(KEY_EXIT),
+        LABEL(KEY_MOVE),
+        LABEL(KEY_EDIT),
+        LABEL(KEY_SCROLLUP),
+        LABEL(KEY_SCROLLDOWN),
+        LABEL(KEY_KPLEFTPAREN),
+        LABEL(KEY_KPRIGHTPAREN),
+        LABEL(KEY_NEW),
+        LABEL(KEY_REDO),
+        LABEL(KEY_F13),
+        LABEL(KEY_F14),
+        LABEL(KEY_F15),
+        LABEL(KEY_F16),
+        LABEL(KEY_F17),
+        LABEL(KEY_F18),
+        LABEL(KEY_F19),
+        LABEL(KEY_F20),
+        LABEL(KEY_F21),
+        LABEL(KEY_F22),
+        LABEL(KEY_F23),
+        LABEL(KEY_F24),
+        LABEL(KEY_PLAYCD),
+        LABEL(KEY_PAUSECD),
+        LABEL(KEY_PROG3),
+        LABEL(KEY_PROG4),
+        LABEL(KEY_DASHBOARD),
+        LABEL(KEY_SUSPEND),
+        LABEL(KEY_CLOSE),
+        LABEL(KEY_PLAY),
+        LABEL(KEY_FASTFORWARD),
+        LABEL(KEY_BASSBOOST),
+        LABEL(KEY_PRINT),
+        LABEL(KEY_HP),
+        LABEL(KEY_CAMERA),
+        LABEL(KEY_SOUND),
+        LABEL(KEY_QUESTION),
+        LABEL(KEY_EMAIL),
+        LABEL(KEY_CHAT),
+        LABEL(KEY_SEARCH),
+        LABEL(KEY_CONNECT),
+        LABEL(KEY_FINANCE),
+        LABEL(KEY_SPORT),
+        LABEL(KEY_SHOP),
+        LABEL(KEY_ALTERASE),
+        LABEL(KEY_CANCEL),
+        LABEL(KEY_BRIGHTNESSDOWN),
+        LABEL(KEY_BRIGHTNESSUP),
+        LABEL(KEY_MEDIA),
+        LABEL(KEY_SWITCHVIDEOMODE),
+        LABEL(KEY_KBDILLUMTOGGLE),
+        LABEL(KEY_KBDILLUMDOWN),
+        LABEL(KEY_KBDILLUMUP),
+        LABEL(KEY_SEND),
+        LABEL(KEY_REPLY),
+        LABEL(KEY_FORWARDMAIL),
+        LABEL(KEY_SAVE),
+        LABEL(KEY_DOCUMENTS),
+        LABEL(KEY_BATTERY),
+        LABEL(KEY_BLUETOOTH),
+        LABEL(KEY_WLAN),
+        LABEL(KEY_UWB),
+        LABEL(KEY_UNKNOWN),
+        LABEL(KEY_VIDEO_NEXT),
+        LABEL(KEY_VIDEO_PREV),
+        LABEL(KEY_BRIGHTNESS_CYCLE),
+        LABEL(KEY_BRIGHTNESS_ZERO),
+        LABEL(KEY_DISPLAY_OFF),
+        LABEL(KEY_WIMAX),
+        LABEL(KEY_RFKILL),
+        LABEL(BTN_0),
+        LABEL(BTN_1),
+        LABEL(BTN_2),
+        LABEL(BTN_3),
+        LABEL(BTN_4),
+        LABEL(BTN_5),
+        LABEL(BTN_6),
+        LABEL(BTN_7),
+        LABEL(BTN_8),
+        LABEL(BTN_9),
+        LABEL(BTN_LEFT),
+        LABEL(BTN_RIGHT),
+        LABEL(BTN_MIDDLE),
+        LABEL(BTN_SIDE),
+        LABEL(BTN_EXTRA),
+        LABEL(BTN_FORWARD),
+        LABEL(BTN_BACK),
+        LABEL(BTN_TASK),
+        LABEL(BTN_JOYSTICK),
+        LABEL(BTN_TRIGGER),
+        LABEL(BTN_THUMB),
+        LABEL(BTN_THUMB2),
+        LABEL(BTN_TOP),
+        LABEL(BTN_TOP2),
+        LABEL(BTN_PINKIE),
+        LABEL(BTN_BASE),
+        LABEL(BTN_BASE2),
+        LABEL(BTN_BASE3),
+        LABEL(BTN_BASE4),
+        LABEL(BTN_BASE5),
+        LABEL(BTN_BASE6),
+        LABEL(BTN_DEAD),
+        LABEL(BTN_A),
+        LABEL(BTN_B),
+        LABEL(BTN_C),
+        LABEL(BTN_X),
+        LABEL(BTN_Y),
+        LABEL(BTN_Z),
+        LABEL(BTN_TL),
+        LABEL(BTN_TR),
+        LABEL(BTN_TL2),
+        LABEL(BTN_TR2),
+        LABEL(BTN_SELECT),
+        LABEL(BTN_START),
+        LABEL(BTN_MODE),
+        LABEL(BTN_THUMBL),
+        LABEL(BTN_THUMBR),
+        LABEL(BTN_TOOL_PEN),
+        LABEL(BTN_TOOL_RUBBER),
+        LABEL(BTN_TOOL_BRUSH),
+        LABEL(BTN_TOOL_PENCIL),
+        LABEL(BTN_TOOL_AIRBRUSH),
+        LABEL(BTN_TOOL_FINGER),
+        LABEL(BTN_TOOL_MOUSE),
+        LABEL(BTN_TOOL_LENS),
+        LABEL(BTN_TOUCH),
+        LABEL(BTN_STYLUS),
+        LABEL(BTN_STYLUS2),
+        LABEL(BTN_TOOL_DOUBLETAP),
+        LABEL(BTN_TOOL_TRIPLETAP),
+        LABEL(BTN_TOOL_QUADTAP),
+        LABEL(BTN_GEAR_DOWN),
+        LABEL(BTN_GEAR_UP),
+        LABEL(KEY_OK),
+        LABEL(KEY_SELECT),
+        LABEL(KEY_GOTO),
+        LABEL(KEY_CLEAR),
+        LABEL(KEY_POWER2),
+        LABEL(KEY_OPTION),
+        LABEL(KEY_INFO),
+        LABEL(KEY_TIME),
+        LABEL(KEY_VENDOR),
+        LABEL(KEY_ARCHIVE),
+        LABEL(KEY_PROGRAM),
+        LABEL(KEY_CHANNEL),
+        LABEL(KEY_FAVORITES),
+        LABEL(KEY_EPG),
+        LABEL(KEY_PVR),
+        LABEL(KEY_MHP),
+        LABEL(KEY_LANGUAGE),
+        LABEL(KEY_TITLE),
+        LABEL(KEY_SUBTITLE),
+        LABEL(KEY_ANGLE),
+        LABEL(KEY_ZOOM),
+        LABEL(KEY_MODE),
+        LABEL(KEY_KEYBOARD),
+        LABEL(KEY_SCREEN),
+        LABEL(KEY_PC),
+        LABEL(KEY_TV),
+        LABEL(KEY_TV2),
+        LABEL(KEY_VCR),
+        LABEL(KEY_VCR2),
+        LABEL(KEY_SAT),
+        LABEL(KEY_SAT2),
+        LABEL(KEY_CD),
+        LABEL(KEY_TAPE),
+        LABEL(KEY_RADIO),
+        LABEL(KEY_TUNER),
+        LABEL(KEY_PLAYER),
+        LABEL(KEY_TEXT),
+        LABEL(KEY_DVD),
+        LABEL(KEY_AUX),
+        LABEL(KEY_MP3),
+        LABEL(KEY_AUDIO),
+        LABEL(KEY_VIDEO),
+        LABEL(KEY_DIRECTORY),
+        LABEL(KEY_LIST),
+        LABEL(KEY_MEMO),
+        LABEL(KEY_CALENDAR),
+        LABEL(KEY_RED),
+        LABEL(KEY_GREEN),
+        LABEL(KEY_YELLOW),
+        LABEL(KEY_BLUE),
+        LABEL(KEY_CHANNELUP),
+        LABEL(KEY_CHANNELDOWN),
+        LABEL(KEY_FIRST),
+        LABEL(KEY_LAST),
+        LABEL(KEY_AB),
+        LABEL(KEY_NEXT),
+        LABEL(KEY_RESTART),
+        LABEL(KEY_SLOW),
+        LABEL(KEY_SHUFFLE),
+        LABEL(KEY_BREAK),
+        LABEL(KEY_PREVIOUS),
+        LABEL(KEY_DIGITS),
+        LABEL(KEY_TEEN),
+        LABEL(KEY_TWEN),
+        LABEL(KEY_VIDEOPHONE),
+        LABEL(KEY_GAMES),
+        LABEL(KEY_ZOOMIN),
+        LABEL(KEY_ZOOMOUT),
+        LABEL(KEY_ZOOMRESET),
+        LABEL(KEY_WORDPROCESSOR),
+        LABEL(KEY_EDITOR),
+        LABEL(KEY_SPREADSHEET),
+        LABEL(KEY_GRAPHICSEDITOR),
+        LABEL(KEY_PRESENTATION),
+        LABEL(KEY_DATABASE),
+        LABEL(KEY_NEWS),
+        LABEL(KEY_VOICEMAIL),
+        LABEL(KEY_ADDRESSBOOK),
+        LABEL(KEY_MESSENGER),
+        LABEL(KEY_DISPLAYTOGGLE),
+        LABEL(KEY_SPELLCHECK),
+        LABEL(KEY_LOGOFF),
+        LABEL(KEY_DOLLAR),
+        LABEL(KEY_EURO),
+        LABEL(KEY_FRAMEBACK),
+        LABEL(KEY_FRAMEFORWARD),
+        LABEL(KEY_CONTEXT_MENU),
+        LABEL(KEY_MEDIA_REPEAT),
+        LABEL(KEY_10CHANNELSUP),
+        LABEL(KEY_10CHANNELSDOWN),
+        LABEL(KEY_IMAGES),
+        LABEL(KEY_DEL_EOL),
+        LABEL(KEY_DEL_EOS),
+        LABEL(KEY_INS_LINE),
+        LABEL(KEY_DEL_LINE),
+        LABEL(KEY_FN),
+        LABEL(KEY_FN_ESC),
+        LABEL(KEY_FN_F1),
+        LABEL(KEY_FN_F2),
+        LABEL(KEY_FN_F3),
+        LABEL(KEY_FN_F4),
+        LABEL(KEY_FN_F5),
+        LABEL(KEY_FN_F6),
+        LABEL(KEY_FN_F7),
+        LABEL(KEY_FN_F8),
+        LABEL(KEY_FN_F9),
+        LABEL(KEY_FN_F10),
+        LABEL(KEY_FN_F11),
+        LABEL(KEY_FN_F12),
+        LABEL(KEY_FN_1),
+        LABEL(KEY_FN_2),
+        LABEL(KEY_FN_D),
+        LABEL(KEY_FN_E),
+        LABEL(KEY_FN_F),
+        LABEL(KEY_FN_S),
+        LABEL(KEY_FN_B),
+        LABEL(KEY_BRL_DOT1),
+        LABEL(KEY_BRL_DOT2),
+        LABEL(KEY_BRL_DOT3),
+        LABEL(KEY_BRL_DOT4),
+        LABEL(KEY_BRL_DOT5),
+        LABEL(KEY_BRL_DOT6),
+        LABEL(KEY_BRL_DOT7),
+        LABEL(KEY_BRL_DOT8),
+        LABEL(KEY_BRL_DOT9),
+        LABEL(KEY_BRL_DOT10),
+        LABEL(KEY_NUMERIC_0),
+        LABEL(KEY_NUMERIC_1),
+        LABEL(KEY_NUMERIC_2),
+        LABEL(KEY_NUMERIC_3),
+        LABEL(KEY_NUMERIC_4),
+        LABEL(KEY_NUMERIC_5),
+        LABEL(KEY_NUMERIC_6),
+        LABEL(KEY_NUMERIC_7),
+        LABEL(KEY_NUMERIC_8),
+        LABEL(KEY_NUMERIC_9),
+        LABEL(KEY_NUMERIC_STAR),
+        LABEL(KEY_NUMERIC_POUND),
+        LABEL(KEY_CAMERA_FOCUS),
+        LABEL(KEY_WPS_BUTTON),
+        LABEL(KEY_TOUCHPAD_TOGGLE),
+        LABEL(KEY_TOUCHPAD_ON),
+        LABEL(KEY_TOUCHPAD_OFF),
+        LABEL(KEY_CAMERA_ZOOMIN),
+        LABEL(KEY_CAMERA_ZOOMOUT),
+        LABEL(KEY_CAMERA_UP),
+        LABEL(KEY_CAMERA_DOWN),
+        LABEL(KEY_CAMERA_LEFT),
+        LABEL(KEY_CAMERA_RIGHT),
+        LABEL(BTN_TRIGGER_HAPPY1),
+        LABEL(BTN_TRIGGER_HAPPY2),
+        LABEL(BTN_TRIGGER_HAPPY3),
+        LABEL(BTN_TRIGGER_HAPPY4),
+        LABEL(BTN_TRIGGER_HAPPY5),
+        LABEL(BTN_TRIGGER_HAPPY6),
+        LABEL(BTN_TRIGGER_HAPPY7),
+        LABEL(BTN_TRIGGER_HAPPY8),
+        LABEL(BTN_TRIGGER_HAPPY9),
+        LABEL(BTN_TRIGGER_HAPPY10),
+        LABEL(BTN_TRIGGER_HAPPY11),
+        LABEL(BTN_TRIGGER_HAPPY12),
+        LABEL(BTN_TRIGGER_HAPPY13),
+        LABEL(BTN_TRIGGER_HAPPY14),
+        LABEL(BTN_TRIGGER_HAPPY15),
+        LABEL(BTN_TRIGGER_HAPPY16),
+        LABEL(BTN_TRIGGER_HAPPY17),
+        LABEL(BTN_TRIGGER_HAPPY18),
+        LABEL(BTN_TRIGGER_HAPPY19),
+        LABEL(BTN_TRIGGER_HAPPY20),
+        LABEL(BTN_TRIGGER_HAPPY21),
+        LABEL(BTN_TRIGGER_HAPPY22),
+        LABEL(BTN_TRIGGER_HAPPY23),
+        LABEL(BTN_TRIGGER_HAPPY24),
+        LABEL(BTN_TRIGGER_HAPPY25),
+        LABEL(BTN_TRIGGER_HAPPY26),
+        LABEL(BTN_TRIGGER_HAPPY27),
+        LABEL(BTN_TRIGGER_HAPPY28),
+        LABEL(BTN_TRIGGER_HAPPY29),
+        LABEL(BTN_TRIGGER_HAPPY30),
+        LABEL(BTN_TRIGGER_HAPPY31),
+        LABEL(BTN_TRIGGER_HAPPY32),
+        LABEL(BTN_TRIGGER_HAPPY33),
+        LABEL(BTN_TRIGGER_HAPPY34),
+        LABEL(BTN_TRIGGER_HAPPY35),
+        LABEL(BTN_TRIGGER_HAPPY36),
+        LABEL(BTN_TRIGGER_HAPPY37),
+        LABEL(BTN_TRIGGER_HAPPY38),
+        LABEL(BTN_TRIGGER_HAPPY39),
+        LABEL(BTN_TRIGGER_HAPPY40),
+        LABEL_END,
+};
+
+static struct label rel_labels[] = {
+        LABEL(REL_X),
+        LABEL(REL_Y),
+        LABEL(REL_Z),
+        LABEL(REL_RX),
+        LABEL(REL_RY),
+        LABEL(REL_RZ),
+        LABEL(REL_HWHEEL),
+        LABEL(REL_DIAL),
+        LABEL(REL_WHEEL),
+        LABEL(REL_MISC),
+        LABEL_END,
+};
+
+static struct label abs_labels[] = {
+        LABEL(ABS_X),
+        LABEL(ABS_Y),
+        LABEL(ABS_Z),
+        LABEL(ABS_RX),
+        LABEL(ABS_RY),
+        LABEL(ABS_RZ),
+        LABEL(ABS_THROTTLE),
+        LABEL(ABS_RUDDER),
+        LABEL(ABS_WHEEL),
+        LABEL(ABS_GAS),
+        LABEL(ABS_BRAKE),
+        LABEL(ABS_HAT0X),
+        LABEL(ABS_HAT0Y),
+        LABEL(ABS_HAT1X),
+        LABEL(ABS_HAT1Y),
+        LABEL(ABS_HAT2X),
+        LABEL(ABS_HAT2Y),
+        LABEL(ABS_HAT3X),
+        LABEL(ABS_HAT3Y),
+        LABEL(ABS_PRESSURE),
+        LABEL(ABS_DISTANCE),
+        LABEL(ABS_TILT_X),
+        LABEL(ABS_TILT_Y),
+        LABEL(ABS_TOOL_WIDTH),
+        LABEL(ABS_VOLUME),
+        LABEL(ABS_MISC),
+        LABEL(ABS_MT_SLOT),
+        LABEL(ABS_MT_TOUCH_MAJOR),
+        LABEL(ABS_MT_TOUCH_MINOR),
+        LABEL(ABS_MT_WIDTH_MAJOR),
+        LABEL(ABS_MT_WIDTH_MINOR),
+        LABEL(ABS_MT_ORIENTATION),
+        LABEL(ABS_MT_POSITION_X),
+        LABEL(ABS_MT_POSITION_Y),
+        LABEL(ABS_MT_TOOL_TYPE),
+        LABEL(ABS_MT_BLOB_ID),
+        LABEL(ABS_MT_TRACKING_ID),
+        LABEL(ABS_MT_PRESSURE),
+        LABEL(ABS_MT_DISTANCE),
+        LABEL_END,
+};
+
+static struct label sw_labels[] = {
+        LABEL(SW_LID),
+        LABEL(SW_TABLET_MODE),
+        LABEL(SW_HEADPHONE_INSERT),
+        LABEL(SW_RFKILL_ALL),
+        LABEL(SW_RADIO),
+        LABEL(SW_MICROPHONE_INSERT),
+        LABEL(SW_DOCK),
+        LABEL(SW_LINEOUT_INSERT),
+        LABEL(SW_JACK_PHYSICAL_INSERT),
+        LABEL(SW_VIDEOOUT_INSERT),
+        LABEL(SW_CAMERA_LENS_COVER),
+        LABEL(SW_KEYPAD_SLIDE),
+        LABEL(SW_FRONT_PROXIMITY),
+        LABEL(SW_ROTATE_LOCK),
+        LABEL_END,
+};
+
+static struct label msc_labels[] = {
+        LABEL(MSC_SERIAL),
+        LABEL(MSC_PULSELED),
+        LABEL(MSC_GESTURE),
+        LABEL(MSC_RAW),
+        LABEL(MSC_SCAN),
+        LABEL_END,
+};
+
+static struct label led_labels[] = {
+        LABEL(LED_NUML),
+        LABEL(LED_CAPSL),
+        LABEL(LED_SCROLLL),
+        LABEL(LED_COMPOSE),
+        LABEL(LED_KANA),
+        LABEL(LED_SLEEP),
+        LABEL(LED_SUSPEND),
+        LABEL(LED_MUTE),
+        LABEL(LED_MISC),
+        LABEL(LED_MAIL),
+        LABEL(LED_CHARGING),
+        LABEL_END,
+};
+
+static struct label rep_labels[] = {
+        LABEL(REP_DELAY),
+        LABEL(REP_PERIOD),
+        LABEL_END,
+};
+
+static struct label snd_labels[] = {
+        LABEL(SND_CLICK),
+        LABEL(SND_BELL),
+        LABEL(SND_TONE),
+        LABEL_END,
+};
+
+static struct label id_labels[] = {
+        LABEL(ID_BUS),
+        LABEL(ID_VENDOR),
+        LABEL(ID_PRODUCT),
+        LABEL(ID_VERSION),
+        LABEL_END,
+};
+
+static struct label bus_labels[] = {
+        LABEL(BUS_PCI),
+        LABEL(BUS_ISAPNP),
+        LABEL(BUS_USB),
+        LABEL(BUS_HIL),
+        LABEL(BUS_BLUETOOTH),
+        LABEL(BUS_VIRTUAL),
+        LABEL(BUS_ISA),
+        LABEL(BUS_I8042),
+        LABEL(BUS_XTKBD),
+        LABEL(BUS_RS232),
+        LABEL(BUS_GAMEPORT),
+        LABEL(BUS_PARPORT),
+        LABEL(BUS_AMIGA),
+        LABEL(BUS_ADB),
+        LABEL(BUS_I2C),
+        LABEL(BUS_HOST),
+        LABEL(BUS_GSC),
+        LABEL(BUS_ATARI),
+        LABEL(BUS_SPI),
+        LABEL_END,
+};
+
+static struct label mt_tool_labels[] = {
+        LABEL(MT_TOOL_FINGER),
+        LABEL(MT_TOOL_PEN),
+        LABEL(MT_TOOL_MAX),
+        LABEL_END,
+};
+
+static struct label ff_status_labels[] = {
+        LABEL(FF_STATUS_STOPPED),
+        LABEL(FF_STATUS_PLAYING),
+        LABEL(FF_STATUS_MAX),
+        LABEL_END,
+};
+
+static struct label ff_labels[] = {
+        LABEL(FF_RUMBLE),
+        LABEL(FF_PERIODIC),
+        LABEL(FF_CONSTANT),
+        LABEL(FF_SPRING),
+        LABEL(FF_FRICTION),
+        LABEL(FF_DAMPER),
+        LABEL(FF_INERTIA),
+        LABEL(FF_RAMP),
+        LABEL(FF_SQUARE),
+        LABEL(FF_TRIANGLE),
+        LABEL(FF_SINE),
+        LABEL(FF_SAW_UP),
+        LABEL(FF_SAW_DOWN),
+        LABEL(FF_CUSTOM),
+        LABEL(FF_GAIN),
+        LABEL(FF_AUTOCENTER),
+        LABEL_END,
+};
+
+static struct label key_value_labels[] = {
+        { "UP", 0 },
+        { "DOWN", 1 },
+        { "REPEAT", 2 },
+        LABEL_END,
+};
diff --git a/toolbox/getprop.c b/toolbox/getprop.c
new file mode 100644
index 0000000..c001fda
--- /dev/null
+++ b/toolbox/getprop.c
@@ -0,0 +1,55 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <cutils/properties.h>
+
+#include <sys/system_properties.h>
+#include "dynarray.h"
+
+static void record_prop(const char* key, const char* name, void* opaque)
+{
+    strlist_t* list = opaque;
+    char temp[PROP_VALUE_MAX + PROP_NAME_MAX + 16];
+    snprintf(temp, sizeof temp, "[%s]: [%s]", key, name);
+    strlist_append_dup(list, temp);
+}
+
+static void list_properties(void)
+{
+    strlist_t  list[1] = { STRLIST_INITIALIZER };
+
+    /* Record properties in the string list */
+    (void)property_list(record_prop, list);
+
+    /* Sort everything */
+    strlist_sort(list);
+
+    /* print everything */
+    STRLIST_FOREACH(list, str, printf("%s\n", str));
+
+    /* voila */
+    strlist_done(list);
+}
+
+int __system_property_wait(prop_info *pi);
+
+int getprop_main(int argc, char *argv[])
+{
+    int n = 0;
+
+    if (argc == 1) {
+        list_properties();
+    } else {
+        char value[PROPERTY_VALUE_MAX];
+        char *default_value;
+        if(argc > 2) {
+            default_value = argv[2];
+        } else {
+            default_value = "";
+        }
+
+        property_get(argv[1], value, default_value);
+        printf("%s\n", value);
+    }
+    return 0;
+}
diff --git a/toolbox/getsebool.c b/toolbox/getsebool.c
new file mode 100644
index 0000000..aab5200
--- /dev/null
+++ b/toolbox/getsebool.c
@@ -0,0 +1,104 @@
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <getopt.h>
+#include <errno.h>
+#include <string.h>
+#include <selinux/selinux.h>
+
+static void usage(const char *progname)
+{
+    fprintf(stderr, "usage:  %s -a or %s boolean...\n", progname, progname);
+    exit(1);
+}
+
+int getsebool_main(int argc, char **argv)
+{
+    int i, get_all = 0, rc = 0, active, pending, len = 0, opt;
+    char **names;
+
+    while ((opt = getopt(argc, argv, "a")) > 0) {
+        switch (opt) {
+        case 'a':
+            if (argc > 2)
+                usage(argv[0]);
+            if (is_selinux_enabled() <= 0) {
+                fprintf(stderr, "%s:  SELinux is disabled\n",
+                        argv[0]);
+                return 1;
+            }
+            errno = 0;
+            rc = security_get_boolean_names(&names, &len);
+            if (rc) {
+                fprintf(stderr,
+                        "%s:  Unable to get boolean names:  %s\n",
+                        argv[0], strerror(errno));
+                return 1;
+            }
+            if (!len) {
+                printf("No booleans\n");
+                return 0;
+            }
+            get_all = 1;
+            break;
+        default:
+            usage(argv[0]);
+        }
+    }
+
+    if (is_selinux_enabled() <= 0) {
+        fprintf(stderr, "%s:  SELinux is disabled\n", argv[0]);
+        return 1;
+    }
+    if (!len) {
+        if (argc < 2)
+            usage(argv[0]);
+        len = argc - 1;
+        names = malloc(sizeof(char *) * len);
+        if (!names) {
+            fprintf(stderr, "%s:  out of memory\n", argv[0]);
+            return 2;
+        }
+        for (i = 0; i < len; i++) {
+            names[i] = strdup(argv[i + 1]);
+            if (!names[i]) {
+                fprintf(stderr, "%s:  out of memory\n",
+                        argv[0]);
+                return 2;
+            }
+        }
+    }
+
+    for (i = 0; i < len; i++) {
+        active = security_get_boolean_active(names[i]);
+        if (active < 0) {
+            if (get_all && errno == EACCES)
+                continue;
+            fprintf(stderr, "Error getting active value for %s\n",
+                    names[i]);
+            rc = -1;
+            goto out;
+        }
+        pending = security_get_boolean_pending(names[i]);
+        if (pending < 0) {
+            fprintf(stderr, "Error getting pending value for %s\n",
+                    names[i]);
+            rc = -1;
+            goto out;
+        }
+        if (pending != active) {
+            printf("%s --> %s pending: %s\n", names[i],
+                   (active ? "on" : "off"),
+                   (pending ? "on" : "off"));
+        } else {
+            printf("%s --> %s\n", names[i],
+                   (active ? "on" : "off"));
+        }
+    }
+
+out:
+    for (i = 0; i < len; i++)
+        free(names[i]);
+    free(names);
+    return rc;
+}
diff --git a/toolbox/hd.c b/toolbox/hd.c
new file mode 100644
index 0000000..da31245
--- /dev/null
+++ b/toolbox/hd.c
@@ -0,0 +1,96 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+
+int hd_main(int argc, char *argv[])
+{
+    int c;
+    int fd;
+	unsigned char buf[4096];
+    int res;
+	int read_len;
+	int rv = 0;
+	int i;
+	int filepos = 0;
+	int sum;
+	int lsum;
+
+	int base = -1;
+	int count = 0;
+	int repeat = 0;
+
+    do {
+        c = getopt(argc, argv, "b:c:r:");
+        if (c == EOF)
+            break;
+        switch (c) {
+        case 'b':
+            base = strtol(optarg, NULL, 0);
+            break;
+        case 'c':
+            count = strtol(optarg, NULL, 0);
+            break;
+		case 'r':
+			repeat = strtol(optarg, NULL, 0);
+			break;
+        case '?':
+            fprintf(stderr, "%s: invalid option -%c\n",
+                argv[0], optopt);
+            exit(1);
+        }
+    } while (1);
+
+    if (optind + 1 != argc) {
+        fprintf(stderr, "Usage: %s [-b base] [-c count] [-r delay] file\n", argv[0]);
+        exit(1);
+    }
+
+    fd = open(argv[optind], O_RDONLY);
+    if(fd < 0) {
+        fprintf(stderr, "could not open %s, %s\n", argv[optind], strerror(errno));
+        return 1;
+    }
+
+	do {
+		if(base >= 0) {
+			lseek(fd, base, SEEK_SET);
+			filepos = base;
+		}
+		sum = 0;
+		lsum = 0;
+	    while(1) {
+			read_len = sizeof(buf);
+			if(count > 0 && base + count - filepos < read_len)
+				read_len = base + count - filepos;
+	        res = read(fd, &buf, read_len);
+			for(i = 0; i < res; i++) {
+				if((i & 15) == 0) {
+					printf("%08x: ", filepos + i);
+				}
+				lsum += buf[i];
+				sum += buf[i];
+				printf("%02x ", buf[i]);
+				if(((i & 15) == 15) || (i == res - 1)) {
+					printf("s %x\n", lsum);
+					lsum = 0;
+				}
+			}
+			if(res <= 0) {
+				printf("Read error on %s, offset %d len %d, %s\n", argv[optind], filepos, read_len, strerror(errno));
+				return 1;
+			}
+			filepos += res;
+			if(filepos == base + count)
+				break;
+	    }
+		printf("sum %x\n", sum);
+		if(repeat)
+			sleep(repeat);
+	} while(repeat);
+	return 0;
+}
diff --git a/toolbox/id.c b/toolbox/id.c
new file mode 100644
index 0000000..bc79288
--- /dev/null
+++ b/toolbox/id.c
@@ -0,0 +1,64 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
+
+#ifdef HAVE_SELINUX
+#include <selinux/selinux.h>
+#endif
+
+static void print_uid(uid_t uid)
+{
+    struct passwd *pw = getpwuid(uid);
+
+    if (pw) {
+        printf("%d(%s)", uid, pw->pw_name);
+    } else {
+        printf("%d",uid);
+    }
+}
+
+static void print_gid(gid_t gid)
+{
+    struct group *gr = getgrgid(gid);
+    if (gr) {
+        printf("%d(%s)", gid, gr->gr_name);
+    } else {
+        printf("%d",gid);
+    }
+}
+
+int id_main(int argc, char **argv)
+{
+    gid_t list[64];
+    int n, max;
+#ifdef HAVE_SELINUX
+    char *secctx;
+#endif
+
+    max = getgroups(64, list);
+    if (max < 0) max = 0;
+
+    printf("uid=");
+    print_uid(getuid());
+    printf(" gid=");
+    print_gid(getgid());
+    if (max) {
+        printf(" groups=");
+        print_gid(list[0]);
+        for(n = 1; n < max; n++) {
+            printf(",");
+            print_gid(list[n]);
+        }
+    }
+#ifdef HAVE_SELINUX
+    if (getcon(&secctx) == 0) {
+        printf(" context=%s", secctx);
+        free(secctx);
+    }
+#endif
+    printf("\n");
+    return 0;
+}
diff --git a/toolbox/ifconfig.c b/toolbox/ifconfig.c
new file mode 100644
index 0000000..80c0e5c
--- /dev/null
+++ b/toolbox/ifconfig.c
@@ -0,0 +1,163 @@
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <errno.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <linux/if.h>
+#include <linux/sockios.h>
+#include <arpa/inet.h>
+
+static void die(const char *s)
+{
+    fprintf(stderr,"error: %s (%s)\n", s, strerror(errno));
+    exit(-1);
+}
+
+static void setflags(int s, struct ifreq *ifr, int set, int clr)
+{
+    if(ioctl(s, SIOCGIFFLAGS, ifr) < 0) die("SIOCGIFFLAGS");
+    ifr->ifr_flags = (ifr->ifr_flags & (~clr)) | set;
+    if(ioctl(s, SIOCSIFFLAGS, ifr) < 0) die("SIOCSIFFLAGS");
+}
+
+static inline void init_sockaddr_in(struct sockaddr_in *sin, const char *addr)
+{
+    sin->sin_family = AF_INET;
+    sin->sin_port = 0;
+    sin->sin_addr.s_addr = inet_addr(addr);
+}
+
+static void setmtu(int s, struct ifreq *ifr, const char *mtu)
+{
+    int m = atoi(mtu);
+    ifr->ifr_mtu = m;
+    if(ioctl(s, SIOCSIFMTU, ifr) < 0) die("SIOCSIFMTU");
+}
+static void setdstaddr(int s, struct ifreq *ifr, const char *addr)
+{
+    init_sockaddr_in((struct sockaddr_in *) &ifr->ifr_dstaddr, addr);
+    if(ioctl(s, SIOCSIFDSTADDR, ifr) < 0) die("SIOCSIFDSTADDR");
+}
+
+static void setnetmask(int s, struct ifreq *ifr, const char *addr)
+{
+    init_sockaddr_in((struct sockaddr_in *) &ifr->ifr_netmask, addr);
+    if(ioctl(s, SIOCSIFNETMASK, ifr) < 0) die("SIOCSIFNETMASK");
+}
+
+static void setaddr(int s, struct ifreq *ifr, const char *addr)
+{
+    init_sockaddr_in((struct sockaddr_in *) &ifr->ifr_addr, addr);
+    if(ioctl(s, SIOCSIFADDR, ifr) < 0) die("SIOCSIFADDR");
+}
+
+int ifconfig_main(int argc, char *argv[])
+{
+    struct ifreq ifr;
+    int s;
+    unsigned int addr, mask, flags;
+    char astring[20];
+    char mstring[20];
+    char *updown, *brdcst, *loopbk, *ppp, *running, *multi;
+    
+    argc--;
+    argv++;
+
+    if(argc == 0) return 0;
+
+    memset(&ifr, 0, sizeof(struct ifreq));
+    strncpy(ifr.ifr_name, argv[0], IFNAMSIZ);
+    ifr.ifr_name[IFNAMSIZ-1] = 0;
+    argc--, argv++;
+
+    if((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+        die("cannot open control socket\n");
+    }
+
+    if (argc == 0) {
+        if (ioctl(s, SIOCGIFADDR, &ifr) < 0) {
+            perror(ifr.ifr_name);
+            return -1;
+        } else
+            addr = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr;
+
+        if (ioctl(s, SIOCGIFNETMASK, &ifr) < 0) {
+            perror(ifr.ifr_name);
+            return -1;
+        } else
+            mask = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr;
+
+        if (ioctl(s, SIOCGIFFLAGS, &ifr) < 0) {
+            perror(ifr.ifr_name);
+            return -1;
+        } else
+            flags = ifr.ifr_flags;
+
+        sprintf(astring, "%d.%d.%d.%d",
+                addr & 0xff,
+                ((addr >> 8) & 0xff),
+                ((addr >> 16) & 0xff),
+                ((addr >> 24) & 0xff));
+        sprintf(mstring, "%d.%d.%d.%d",
+                mask & 0xff,
+                ((mask >> 8) & 0xff),
+                ((mask >> 16) & 0xff),
+                ((mask >> 24) & 0xff));
+        printf("%s: ip %s mask %s flags [", ifr.ifr_name,
+               astring,
+               mstring
+               );
+
+        updown =  (flags & IFF_UP)           ? "up" : "down";
+        brdcst =  (flags & IFF_BROADCAST)    ? " broadcast" : "";
+        loopbk =  (flags & IFF_LOOPBACK)     ? " loopback" : "";
+        ppp =     (flags & IFF_POINTOPOINT)  ? " point-to-point" : "";
+        running = (flags & IFF_RUNNING)      ? " running" : "";
+        multi =   (flags & IFF_MULTICAST)    ? " multicast" : "";
+        printf("%s%s%s%s%s%s]\n", updown, brdcst, loopbk, ppp, running, multi);
+        return 0;
+    }
+    
+    while(argc > 0) {
+        if (!strcmp(argv[0], "up")) {
+            setflags(s, &ifr, IFF_UP, 0);
+        } else if (!strcmp(argv[0], "mtu")) {
+            argc--, argv++;
+            if (!argc) {
+                errno = EINVAL;
+                die("expecting a value for parameter \"mtu\"");
+            }
+            setmtu(s, &ifr, argv[0]);
+        } else if (!strcmp(argv[0], "-pointopoint")) {
+            setflags(s, &ifr, IFF_POINTOPOINT, 1);
+        } else if (!strcmp(argv[0], "pointopoint")) {
+            argc--, argv++;
+            if (!argc) { 
+                errno = EINVAL;
+                die("expecting an IP address for parameter \"pointtopoint\"");
+            }
+            setdstaddr(s, &ifr, argv[0]);
+            setflags(s, &ifr, IFF_POINTOPOINT, 0);
+        } else if (!strcmp(argv[0], "down")) {
+            setflags(s, &ifr, 0, IFF_UP);
+        } else if (!strcmp(argv[0], "netmask")) {
+            argc--, argv++;
+            if (!argc) { 
+                errno = EINVAL;
+                die("expecting an IP address for parameter \"netmask\"");
+            }
+            setnetmask(s, &ifr, argv[0]);
+        } else if (isdigit(argv[0][0])) {
+            setaddr(s, &ifr, argv[0]);
+            setflags(s, &ifr, IFF_UP, 0);
+        }
+        argc--, argv++;
+    }
+    return 0;
+}
diff --git a/toolbox/iftop.c b/toolbox/iftop.c
new file mode 100644
index 0000000..800c0f0
--- /dev/null
+++ b/toolbox/iftop.c
@@ -0,0 +1,278 @@
+/*
+ * Copyright (c) 2008, The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the 
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <net/if.h>
+
+#define PROC_NET_DEV    "/proc/net/dev"
+
+#define MAX_IF           8   /* max interfaces we can handle */
+
+#ifndef PAGE_SIZE
+# define PAGE_SIZE 4096
+#endif
+
+#define _STR(s) #s
+#define STR(s) _STR(s)
+
+struct if_stats {
+    char name[IFNAMSIZ];
+
+    unsigned int mtu;
+
+    unsigned int rx_bytes;
+    unsigned int rx_packets;
+    unsigned int rx_errors;
+    unsigned int rx_dropped;
+
+    unsigned int tx_bytes;
+    unsigned int tx_packets;
+    unsigned int tx_errors;
+    unsigned int tx_dropped;
+};
+
+static int get_mtu(const char *if_name)
+{
+    struct ifreq ifr;
+    int s, ret;
+
+    s = socket(AF_INET, SOCK_DGRAM, 0);
+    if (s < 0) {
+        perror("socket");
+        exit(EXIT_FAILURE);
+    }
+
+    memset(&ifr, 0, sizeof(struct ifreq));
+    ifr.ifr_addr.sa_family = AF_INET;
+    strcpy(ifr.ifr_name, if_name);
+
+    ret = ioctl(s, SIOCGIFMTU, &ifr);
+    if (ret < 0) {
+        perror("ioctl");
+        exit(EXIT_FAILURE);
+    }
+
+    ret = close(s);
+    if (ret < 0) {
+        perror("close");
+        exit(EXIT_FAILURE);
+    }
+
+    return ifr.ifr_mtu;
+}
+
+static int get_interfaces(struct if_stats *ifs)
+{
+    char buf[PAGE_SIZE];
+    char *p;
+    int ret, nr, fd;
+
+    fd = open(PROC_NET_DEV, O_RDONLY);
+    if (fd < 0) {
+        perror("open");
+        exit(EXIT_FAILURE);
+    }
+
+    ret = read(fd, buf, sizeof(buf) - 1);
+    if (ret < 0) {
+        perror("read");
+        exit(EXIT_FAILURE);
+    } else if (!ret) {
+        fprintf(stderr, "reading " PROC_NET_DEV " returned premature EOF\n");
+        exit(EXIT_FAILURE);
+    }
+    buf[ret] = '\0';
+
+    /* skip down to the third line */
+    p = strchr(buf, '\n');
+    if (!p) {
+        fprintf(stderr, "parsing " PROC_NET_DEV " failed unexpectedly\n");
+        exit(EXIT_FAILURE);
+    }
+    p = strchr(p + 1, '\n');
+    if (!p) {
+        fprintf(stderr, "parsing " PROC_NET_DEV " failed unexpectedly\n");
+        exit(EXIT_FAILURE);
+    }
+    p += 1;
+
+    /*
+     * Key:
+     * if: (Rx) bytes packets errs drop fifo frame compressed multicast \
+     *     (Tx) bytes packets errs drop fifo colls carrier compressed
+     */
+    for (nr = 0; nr < MAX_IF; nr++) {
+        char *c;
+
+        ret = sscanf(p, "%" STR(IFNAMSIZ) "s", ifs->name);
+        if (ret != 1) {
+            fprintf(stderr, "parsing " PROC_NET_DEV " failed unexpectedly\n");
+            exit(EXIT_FAILURE);
+        }
+
+        /*
+         * This works around a bug in the proc file where large interface names
+         * or Rx byte counts eat the delimiter, breaking sscanf.
+         */
+        c = strchr(ifs->name, ':');
+        if (c)
+            *c = '\0';
+
+        p = strchr(p, ':') + 1;
+
+        ret = sscanf(p, "%u %u %u %u %*u %*u %*u %*u %u %u %u %u %*u %*u "
+                     "%*u %*u\n", &ifs->rx_bytes, &ifs->rx_packets,
+                     &ifs->rx_errors, &ifs->rx_dropped, &ifs->tx_bytes,
+                     &ifs->tx_packets, &ifs->tx_errors, &ifs->tx_dropped);
+        if (ret != 8) {
+            fprintf(stderr, "parsing " PROC_NET_DEV " failed unexpectedly\n");
+            exit(EXIT_FAILURE);
+        }
+
+        ifs->mtu = get_mtu(ifs->name);
+
+        p = strchr(p, '\n') + 1;
+        if (*p == '\0') {
+            nr++;
+            break;
+        }
+
+        ifs++;
+    }
+
+    ret = close(fd);
+    if (ret) {
+        perror("close");
+        exit(EXIT_FAILURE);
+    }
+
+    return nr;
+}
+
+static void print_header(void)
+{
+    printf("               Rx                              Tx\n");
+    printf("%-8s %-5s %-10s %-8s %-5s %-5s %-10s %-8s %-5s %-5s\n",
+           "name", "MTU", "bytes", "packets", "errs", "drpd", "bytes",
+           "packets", "errs", "drpd");
+}
+
+static int print_interfaces(struct if_stats *old, struct if_stats *new, int nr)
+{
+    int i = 0;
+
+    while (nr--) {
+        if (old->rx_packets || old->tx_packets) {
+            printf("%-8s %-5u %-10u %-8u %-5u %-5u %-10u %-8u %-5u %-5u\n",
+                   new->name, new->mtu,
+                   new->rx_bytes - old->rx_bytes,
+                   new->rx_packets - old->rx_packets,
+                   new->rx_errors - old->rx_errors,
+                   new->rx_dropped - old->rx_dropped,
+                   new->tx_bytes - old->tx_bytes,
+                   new->tx_packets - old->tx_packets,
+                   new->tx_errors - old->tx_errors,
+                   new->tx_dropped - old->tx_dropped);
+            i++;
+        }
+        old++;
+        new++;
+    }
+
+    return i;
+}
+
+static void usage(const char *cmd)
+{
+    fprintf(stderr, "usage: %s [ -r repeats] [ -d delay ]\n", cmd);
+}
+
+int iftop_main(int argc, char *argv[])
+{
+    struct if_stats ifs[2][MAX_IF];
+    int count = 0, header_interval = 22, delay = 1, i;
+    unsigned int toggle = 0;
+
+    for (i = 1; i < argc; i++) {
+        if (!strcmp(argv[i], "-d")) {
+            if (i >= argc - 1) {
+                fprintf(stderr, "Option -d requires an argument.\n");
+                exit(EXIT_FAILURE);
+            }
+            delay = atoi(argv[i++]);
+            if (!delay)
+                delay = 1;
+            continue;
+        }
+        if (!strcmp(argv[i], "-r")) {
+            if (i >= argc - 1) {
+                fprintf(stderr, "Option -r requires an argument.\n");
+                exit(EXIT_FAILURE);
+            }
+            header_interval = atoi(argv[i++]);
+            if (header_interval < MAX_IF)
+                header_interval = MAX_IF;
+            continue;
+        }
+        if (!strcmp(argv[i], "-h")) {
+            usage(argv[0]);
+            exit(EXIT_SUCCESS);
+        }
+        usage(argv[0]);
+        exit(EXIT_FAILURE);
+    }
+
+    get_interfaces(ifs[!toggle]);
+    if (header_interval)
+        print_header();
+    while (1) {
+        int nr;
+
+        sleep(delay);
+        nr = get_interfaces(ifs[toggle]);
+        if (header_interval && count + nr > header_interval) {
+            print_header();
+            count = 0;
+        }
+        count += print_interfaces(ifs[!toggle], ifs[toggle], nr);
+        toggle = !toggle;
+    }
+
+    return 0;
+}
diff --git a/toolbox/insmod.c b/toolbox/insmod.c
new file mode 100644
index 0000000..756a64b
--- /dev/null
+++ b/toolbox/insmod.c
@@ -0,0 +1,97 @@
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <malloc.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+extern int init_module(void *, unsigned long, const char *);
+
+static void *read_file(const char *filename, ssize_t *_size)
+{
+	int ret, fd;
+	struct stat sb;
+	ssize_t size;
+	void *buffer = NULL;
+
+	/* open the file */
+	fd = open(filename, O_RDONLY);
+	if (fd < 0)
+		return NULL;
+
+	/* find out how big it is */
+	if (fstat(fd, &sb) < 0)
+		goto bail;
+	size = sb.st_size;
+
+	/* allocate memory for it to be read into */
+	buffer = malloc(size);
+	if (!buffer)
+		goto bail;
+
+	/* slurp it into our buffer */
+	ret = read(fd, buffer, size);
+	if (ret != size)
+		goto bail;
+
+	/* let the caller know how big it is */
+	*_size = size;
+
+bail:
+	close(fd);
+	return buffer;
+}
+
+#define min(x,y) ((x) < (y) ? (x) : (y))
+int insmod_main(int argc, char **argv)
+{
+	void *file;
+	ssize_t size = 0;
+	char opts[1024];
+	int ret;
+
+	/* make sure we've got an argument */
+	if (argc < 2) {
+		fprintf(stderr, "usage: insmod <module.o>\n");
+		return -1;
+	}
+
+	/* read the file into memory */
+	file = read_file(argv[1], &size);
+	if (!file) {
+		fprintf(stderr, "insmod: can't open '%s'\n", argv[1]);
+		return -1;
+	}
+
+	opts[0] = '\0';
+	if (argc > 2) {
+		int i, len;
+		char *end = opts + sizeof(opts) - 1;
+		char *ptr = opts;
+
+		for (i = 2; (i < argc) && (ptr < end); i++) {
+			len = min(strlen(argv[i]), end - ptr);
+			memcpy(ptr, argv[i], len);
+			ptr += len;
+			*ptr++ = ' ';
+		}
+		*(ptr - 1) = '\0';
+	}
+
+	/* pass it to the kernel */
+	ret = init_module(file, size, opts);
+	if (ret != 0) {
+		fprintf(stderr,
+                "insmod: init_module '%s' failed (%s)\n",
+                argv[1], strerror(errno));
+	}
+
+	/* free the file buffer */
+	free(file);
+
+	return ret;
+}
+
diff --git a/toolbox/ioctl.c b/toolbox/ioctl.c
new file mode 100644
index 0000000..fb555d2
--- /dev/null
+++ b/toolbox/ioctl.c
@@ -0,0 +1,128 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <string.h>
+#include <linux/kd.h>
+#include <linux/vt.h>
+#include <errno.h>
+#include <pthread.h>
+#include <sys/ioctl.h>
+
+int ioctl_main(int argc, char *argv[])
+{
+    int c;
+    int fd;
+    int res;
+
+    int read_only = 0;
+    int length = -1;
+    int arg_size = 4;
+    int direct_arg = 0;
+    uint32_t ioctl_nr;
+    void *ioctl_args;
+    uint8_t *ioctl_argp;
+    uint8_t *ioctl_argp_save;
+    int rem;
+
+    do {
+        c = getopt(argc, argv, "rdl:a:h");
+        if (c == EOF)
+            break;
+        switch (c) {
+        case 'r':
+            read_only = 1;
+            break;
+        case 'd':
+            direct_arg = 1;
+            break;
+        case 'l':
+            length = strtol(optarg, NULL, 0);
+            break;
+        case 'a':
+            arg_size = strtol(optarg, NULL, 0);
+            break;
+        case 'h':
+            fprintf(stderr, "%s [-l <length>] [-a <argsize>] [-rdh] <device> <ioctlnr>\n"
+                    "  -l <lenght>   Length of io buffer\n"
+                    "  -a <argsize>  Size of each argument (1-8)\n"
+                    "  -r            Open device in read only mode\n"
+                    "  -d            Direct argument (no iobuffer)\n"
+                    "  -h            Print help\n", argv[0]);
+            return -1;
+        case '?':
+            fprintf(stderr, "%s: invalid option -%c\n",
+                argv[0], optopt);
+            exit(1);
+        }
+    } while (1);
+
+    if(optind + 2 > argc) {
+        fprintf(stderr, "%s: too few arguments\n", argv[0]);
+        exit(1);
+    }
+
+    fd = open(argv[optind], O_RDWR | O_SYNC);
+    if (fd < 0) {
+        fprintf(stderr, "cannot open %s\n", argv[optind]);
+        return 1;
+    }
+    optind++;
+    
+    ioctl_nr = strtol(argv[optind], NULL, 0);
+    optind++;
+
+    if(direct_arg) {
+        arg_size = 4;
+        length = 4;
+    }
+
+    if(length < 0) {
+        length = (argc - optind) * arg_size;
+    }
+    if(length) {
+        ioctl_args = calloc(1, length);
+
+        ioctl_argp_save = ioctl_argp = ioctl_args;
+        rem = length;
+        while(optind < argc) {
+            uint64_t tmp = strtoull(argv[optind], NULL, 0);
+            if(rem < arg_size) {
+                fprintf(stderr, "%s: too many arguments\n", argv[0]);
+                exit(1);
+            }
+            memcpy(ioctl_argp, &tmp, arg_size);
+            ioctl_argp += arg_size;
+            rem -= arg_size;
+            optind++;
+        }
+    }
+    printf("sending ioctl 0x%x", ioctl_nr);
+    rem = length;
+    while(rem--) {
+        printf(" 0x%02x", *ioctl_argp_save++);
+    }
+    printf("\n");
+
+    if(direct_arg)
+        res = ioctl(fd, ioctl_nr, *(uint32_t*)ioctl_args);
+    else if(length)
+        res = ioctl(fd, ioctl_nr, ioctl_args);
+    else
+        res = ioctl(fd, ioctl_nr, 0);
+    if (res < 0) {
+        fprintf(stderr, "ioctl 0x%x failed, %d\n", ioctl_nr, res);
+        return 1;
+    }
+    if(length) {
+        printf("return buf:");
+        ioctl_argp = ioctl_args;
+        rem = length;
+        while(rem--) {
+            printf(" %02x", *ioctl_argp++);
+        }
+        printf("\n");
+    }
+    return 0;
+}
diff --git a/toolbox/ionice.c b/toolbox/ionice.c
new file mode 100644
index 0000000..4a182f2
--- /dev/null
+++ b/toolbox/ionice.c
@@ -0,0 +1,57 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <cutils/iosched_policy.h>
+
+static char *classes[] = {"none", "rt", "be", "idle", NULL};
+
+int ionice_main(int argc, char *argv[])
+{
+    IoSchedClass clazz = IoSchedClass_NONE;
+    int ioprio = 0;
+    int pid;
+
+    if(argc != 2 && argc != 4) {
+        fprintf(stderr, "usage: ionice <pid> [none|rt|be|idle] [prio]\n");
+        return 1;
+    }
+
+    if (!(pid = atoi(argv[1]))) {
+        fprintf(stderr, "Invalid pid specified\n");
+        return 1;
+    }
+
+    if (argc == 2) {
+        if (android_get_ioprio(pid, &clazz, &ioprio)) {
+            fprintf(stderr, "Failed to read priority (%s)\n", strerror(errno));
+            return 1;
+        }
+        fprintf(stdout, "Pid %d, class %s (%d), prio %d\n", pid, classes[clazz], clazz, ioprio);
+        return 0;
+    }
+
+    if (!strcmp(argv[2], "none")) {
+        clazz = IoSchedClass_NONE;
+    } else if (!strcmp(argv[2], "rt")) {
+        clazz = IoSchedClass_RT;
+    } else if (!strcmp(argv[2], "be")) {
+        clazz = IoSchedClass_BE;
+    } else if (!strcmp(argv[2], "idle")) {
+        clazz = IoSchedClass_IDLE;
+    } else {
+        fprintf(stderr, "Unsupported class '%s'\n", argv[2]);
+        return 1;
+    }
+
+    ioprio = atoi(argv[3]);
+
+    printf("Setting pid %d i/o class to %d, prio %d\n", pid, clazz, ioprio);
+    if (android_set_ioprio(pid, clazz, ioprio)) {
+        fprintf(stderr, "Failed to set priority (%s)\n", strerror(errno));
+        return 1;
+    }
+
+    return 0;
+}
diff --git a/toolbox/kill.c b/toolbox/kill.c
new file mode 100644
index 0000000..4d0e479
--- /dev/null
+++ b/toolbox/kill.c
@@ -0,0 +1,35 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#include <signal.h>
+
+int kill_main(int argc, char **argv)
+{
+    int sig = SIGTERM;
+    int result = 0;
+    
+    argc--;
+    argv++;
+
+    if(argc >= 2 && argv[0][0] == '-'){
+        sig = atoi(argv[0] + 1);
+        argc--;
+        argv++;
+    }
+
+    while(argc > 0){
+        int pid = atoi(argv[0]);
+        int err = kill(pid, sig);
+        if (err < 0) {
+            result = err;
+            fprintf(stderr, "could not kill pid %d: %s\n", pid, strerror(errno));
+        }
+            
+        argc--;
+        argv++;
+    }
+    
+    return result;
+}
diff --git a/toolbox/ln.c b/toolbox/ln.c
new file mode 100644
index 0000000..dcd5e3a
--- /dev/null
+++ b/toolbox/ln.c
@@ -0,0 +1,34 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+static int usage()
+{
+    fprintf(stderr,"ln [-s] <target> <name>\n");
+    return -1;
+}
+
+int ln_main(int argc, char *argv[])
+{
+    int symbolic = 0;
+    int ret;
+    if(argc < 2) return usage();
+    
+    if(!strcmp(argv[1],"-s")) {
+        symbolic = 1;
+        argc--;
+        argv++;
+    }
+
+    if(argc < 3) return usage();
+
+    if(symbolic) {
+        ret = symlink(argv[1], argv[2]);
+    } else {
+        ret = link(argv[1], argv[2]);
+    }
+    if(ret < 0)
+        fprintf(stderr, "link failed %s\n", strerror(errno));
+    return ret;
+}
diff --git a/toolbox/load_policy.c b/toolbox/load_policy.c
new file mode 100644
index 0000000..eb5aba6
--- /dev/null
+++ b/toolbox/load_policy.c
@@ -0,0 +1,49 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <errno.h>
+#include <selinux/selinux.h>
+
+int load_policy_main(int argc, char **argv)
+{
+    int fd, rc, vers;
+    struct stat sb;
+    void *map;
+    const char *path;
+
+    if (argc != 2) {
+        fprintf(stderr, "usage:  %s policy-file\n", argv[0]);
+        exit(1);
+    }
+
+    path = argv[1];
+    fd = open(path, O_RDONLY);
+    if (fd < 0) {
+        fprintf(stderr, "Could not open %s:  %s\n", path, strerror(errno));
+        exit(2);
+    }
+
+    if (fstat(fd, &sb) < 0) {
+        fprintf(stderr, "Could not stat %s:  %s\n", path, strerror(errno));
+        exit(3);
+    }
+
+    map = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+    if (map == MAP_FAILED) {
+        fprintf(stderr, "Could not mmap %s:  %s\n", path, strerror(errno));
+        exit(4);
+    }
+
+    rc = security_load_policy(map, sb.st_size);
+    if (rc < 0) {
+        fprintf(stderr, "Could not load %s:  %s\n", path, strerror(errno));
+        exit(5);
+    }
+    munmap(map, sb.st_size);
+    close(fd);
+    exit(0);
+}
diff --git a/toolbox/log.c b/toolbox/log.c
new file mode 100644
index 0000000..a3470cc
--- /dev/null
+++ b/toolbox/log.c
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2008, The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the 
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <log/log.h>
+#include <ctype.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <cutils/sockets.h>
+#include <unistd.h>
+
+/*
+ * Note: also accepts 0-9 priorities
+ * returns ANDROID_LOG_UNKNOWN if the character is unrecognized
+ */
+static android_LogPriority filterCharToPri (char c)
+{
+    android_LogPriority pri;
+
+    c = tolower(c);
+
+    if (c >= '0' && c <= '9') {
+        if (c >= ('0'+ANDROID_LOG_SILENT)) {
+            pri = ANDROID_LOG_VERBOSE;
+        } else {
+            pri = (android_LogPriority)(c - '0');
+        }
+    } else if (c == 'v') {
+        pri = ANDROID_LOG_VERBOSE;
+    } else if (c == 'd') {
+        pri = ANDROID_LOG_DEBUG;
+    } else if (c == 'i') {
+        pri = ANDROID_LOG_INFO;
+    } else if (c == 'w') {
+        pri = ANDROID_LOG_WARN;
+    } else if (c == 'e') {
+        pri = ANDROID_LOG_ERROR;
+    } else if (c == 'f') {
+        pri = ANDROID_LOG_FATAL;
+    } else if (c == 's') {
+        pri = ANDROID_LOG_SILENT;
+    } else if (c == '*') {
+        pri = ANDROID_LOG_DEFAULT;
+    } else {
+        pri = ANDROID_LOG_UNKNOWN;
+    }
+
+    return pri;
+}
+
+static int usage(const char *s)
+{
+    fprintf(stderr, "USAGE: %s [-p priorityChar] [-t tag] message\n", s);
+
+    fprintf(stderr, "\tpriorityChar should be one of:\n"
+                        "\t\tv,d,i,w,e\n");
+    exit(-1);
+}
+
+
+int log_main(int argc, char *argv[])
+{
+    android_LogPriority priority; 
+    const char *tag = "log";
+    char buffer[4096];
+    int i;
+
+    priority = ANDROID_LOG_INFO;
+
+    for (;;) {
+        int ret;
+
+        ret = getopt(argc, argv, "t:p:h");
+
+        if (ret < 0) {
+            break;
+        }
+
+        switch(ret) {
+            case 't':
+                tag = optarg;
+            break;
+            
+            case 'p':
+                priority = filterCharToPri(optarg[0]);
+                if (priority == ANDROID_LOG_UNKNOWN) {
+                    usage(argv[0]);                    
+                }
+            break;
+
+            case 'h':
+                usage(argv[0]);
+            break;
+        }
+    }
+
+    if (optind == argc) {
+        usage(argv[0]);
+    }
+
+    buffer[0] = '\0';
+    
+    for (i = optind ; i < argc ; i++) {
+        strncat(buffer, argv[i], sizeof(buffer)-1);
+        strncat(buffer, " ", sizeof(buffer)-1);
+    }
+
+    if(buffer[0] == 0) {
+        usage(argv[0]);
+    }
+
+    __android_log_print(priority, tag, "%s", buffer);
+
+    return 0;
+}
+
diff --git a/toolbox/ls.c b/toolbox/ls.c
new file mode 100644
index 0000000..e93ce92
--- /dev/null
+++ b/toolbox/ls.c
@@ -0,0 +1,490 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <errno.h>
+
+#ifdef HAVE_SELINUX
+#include <selinux/selinux.h>
+#endif
+
+#include <sys/stat.h>
+#include <unistd.h>
+#include <time.h>
+
+#include <pwd.h>
+#include <grp.h>
+
+#include <linux/kdev_t.h>
+#include <limits.h>
+
+#include "dynarray.h"
+
+// bits for flags argument
+#define LIST_LONG           (1 << 0)
+#define LIST_ALL            (1 << 1)
+#define LIST_RECURSIVE      (1 << 2)
+#define LIST_DIRECTORIES    (1 << 3)
+#define LIST_SIZE           (1 << 4)
+#define LIST_LONG_NUMERIC   (1 << 5)
+#define LIST_CLASSIFY       (1 << 6)
+#define LIST_MACLABEL       (1 << 7)
+
+// fwd
+static int listpath(const char *name, int flags);
+
+static char mode2kind(unsigned mode)
+{
+    switch(mode & S_IFMT){
+    case S_IFSOCK: return 's';
+    case S_IFLNK: return 'l';
+    case S_IFREG: return '-';
+    case S_IFDIR: return 'd';
+    case S_IFBLK: return 'b';
+    case S_IFCHR: return 'c';
+    case S_IFIFO: return 'p';
+    default: return '?';
+    }
+}
+
+static void mode2str(unsigned mode, char *out)
+{
+    *out++ = mode2kind(mode);
+
+    *out++ = (mode & 0400) ? 'r' : '-';
+    *out++ = (mode & 0200) ? 'w' : '-';
+    if(mode & 04000) {
+        *out++ = (mode & 0100) ? 's' : 'S';
+    } else {
+        *out++ = (mode & 0100) ? 'x' : '-';
+    }
+    *out++ = (mode & 040) ? 'r' : '-';
+    *out++ = (mode & 020) ? 'w' : '-';
+    if(mode & 02000) {
+        *out++ = (mode & 010) ? 's' : 'S';
+    } else {
+        *out++ = (mode & 010) ? 'x' : '-';
+    }
+    *out++ = (mode & 04) ? 'r' : '-';
+    *out++ = (mode & 02) ? 'w' : '-';
+    if(mode & 01000) {
+        *out++ = (mode & 01) ? 't' : 'T';
+    } else {
+        *out++ = (mode & 01) ? 'x' : '-';
+    }
+    *out = 0;
+}
+
+static void user2str(unsigned uid, char *out)
+{
+    struct passwd *pw = getpwuid(uid);
+    if(pw) {
+        strcpy(out, pw->pw_name);
+    } else {
+        sprintf(out, "%d", uid);
+    }
+}
+
+static void group2str(unsigned gid, char *out)
+{
+    struct group *gr = getgrgid(gid);
+    if(gr) {
+        strcpy(out, gr->gr_name);
+    } else {
+        sprintf(out, "%d", gid);
+    }
+}
+
+static int show_total_size(const char *dirname, DIR *d, int flags)
+{
+    struct dirent *de;
+    char tmp[1024];
+    struct stat s;
+    int sum = 0;
+
+    /* run through the directory and sum up the file block sizes */
+    while ((de = readdir(d)) != 0) {
+        if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
+            continue;
+        if (de->d_name[0] == '.' && (flags & LIST_ALL) == 0)
+            continue;
+
+        if (strcmp(dirname, "/") == 0)
+            snprintf(tmp, sizeof(tmp), "/%s", de->d_name);
+        else
+            snprintf(tmp, sizeof(tmp), "%s/%s", dirname, de->d_name);
+
+        if (lstat(tmp, &s) < 0) {
+            fprintf(stderr, "stat failed on %s: %s\n", tmp, strerror(errno));
+            rewinddir(d);
+            return -1;
+        }
+
+        sum += s.st_blocks / 2;
+    }
+
+    printf("total %d\n", sum);
+    rewinddir(d);
+    return 0;
+}
+
+static int listfile_size(const char *path, const char *filename, int flags)
+{
+    struct stat s;
+
+    if (lstat(path, &s) < 0) {
+        fprintf(stderr, "lstat '%s' failed: %s\n", path, strerror(errno));
+        return -1;
+    }
+
+    /* blocks are 512 bytes, we want output to be KB */
+    if ((flags & LIST_SIZE) != 0) {
+        printf("%lld ", s.st_blocks / 2);
+    }
+
+    if ((flags & LIST_CLASSIFY) != 0) {
+        char filetype = mode2kind(s.st_mode);
+        if (filetype != 'l') {
+            printf("%c ", filetype);
+        } else {
+            struct stat link_dest;
+            if (!stat(path, &link_dest)) {
+                printf("l%c ", mode2kind(link_dest.st_mode));
+            } else {
+                fprintf(stderr, "stat '%s' failed: %s\n", path, strerror(errno));
+                printf("l? ");
+            }
+        }
+    }
+
+    printf("%s\n", filename);
+
+    return 0;
+}
+
+static int listfile_long(const char *path, int flags)
+{
+    struct stat s;
+    char date[32];
+    char mode[16];
+    char user[16];
+    char group[16];
+    const char *name;
+
+    /* name is anything after the final '/', or the whole path if none*/
+    name = strrchr(path, '/');
+    if(name == 0) {
+        name = path;
+    } else {
+        name++;
+    }
+
+    if(lstat(path, &s) < 0) {
+        return -1;
+    }
+
+    mode2str(s.st_mode, mode);
+    if (flags & LIST_LONG_NUMERIC) {
+        sprintf(user, "%ld", s.st_uid);
+        sprintf(group, "%ld", s.st_gid);
+    } else {
+        user2str(s.st_uid, user);
+        group2str(s.st_gid, group);
+    }
+
+    strftime(date, 32, "%Y-%m-%d %H:%M", localtime((const time_t*)&s.st_mtime));
+    date[31] = 0;
+
+// 12345678901234567890123456789012345678901234567890123456789012345678901234567890
+// MMMMMMMM UUUUUUUU GGGGGGGGG XXXXXXXX YYYY-MM-DD HH:MM NAME (->LINK)
+
+    switch(s.st_mode & S_IFMT) {
+    case S_IFBLK:
+    case S_IFCHR:
+        printf("%s %-8s %-8s %3d, %3d %s %s\n",
+               mode, user, group,
+               (int) MAJOR(s.st_rdev), (int) MINOR(s.st_rdev),
+               date, name);
+        break;
+    case S_IFREG:
+        printf("%s %-8s %-8s %8ld %s %s\n",
+               mode, user, group, s.st_size, date, name);
+        break;
+    case S_IFLNK: {
+        char linkto[256];
+        int len;
+
+        len = readlink(path, linkto, 256);
+        if(len < 0) return -1;
+
+        if(len > 255) {
+            linkto[252] = '.';
+            linkto[253] = '.';
+            linkto[254] = '.';
+            linkto[255] = 0;
+        } else {
+            linkto[len] = 0;
+        }
+
+        printf("%s %-8s %-8s          %s %s -> %s\n",
+               mode, user, group, date, name, linkto);
+        break;
+    }
+    default:
+        printf("%s %-8s %-8s          %s %s\n",
+               mode, user, group, date, name);
+
+    }
+    return 0;
+}
+
+static int listfile_maclabel(const char *path, int flags)
+{
+    struct stat s;
+    char mode[16];
+    char user[16];
+    char group[16];
+    char *maclabel = NULL;
+    const char *name;
+
+    /* name is anything after the final '/', or the whole path if none*/
+    name = strrchr(path, '/');
+    if(name == 0) {
+        name = path;
+    } else {
+        name++;
+    }
+
+    if(lstat(path, &s) < 0) {
+        return -1;
+    }
+
+#ifdef HAVE_SELINUX
+    lgetfilecon(path, &maclabel);
+#else
+    maclabel = strdup("-");
+#endif
+    if (!maclabel) {
+        return -1;
+    }
+
+    mode2str(s.st_mode, mode);
+    user2str(s.st_uid, user);
+    group2str(s.st_gid, group);
+
+    switch(s.st_mode & S_IFMT) {
+    case S_IFLNK: {
+        char linkto[256];
+        int len;
+
+        len = readlink(path, linkto, sizeof(linkto));
+        if(len < 0) return -1;
+
+        if(len > sizeof(linkto)-1) {
+            linkto[sizeof(linkto)-4] = '.';
+            linkto[sizeof(linkto)-3] = '.';
+            linkto[sizeof(linkto)-2] = '.';
+            linkto[sizeof(linkto)-1] = 0;
+        } else {
+            linkto[len] = 0;
+        }
+
+        printf("%s %-8s %-8s          %s %s -> %s\n",
+               mode, user, group, maclabel, name, linkto);
+        break;
+    }
+    default:
+        printf("%s %-8s %-8s          %s %s\n",
+               mode, user, group, maclabel, name);
+
+    }
+
+    free(maclabel);
+
+    return 0;
+}
+
+static int listfile(const char *dirname, const char *filename, int flags)
+{
+    if ((flags & LIST_LONG | LIST_SIZE | LIST_CLASSIFY | LIST_MACLABEL) == 0) {
+        printf("%s\n", filename);
+        return 0;
+    }
+
+    char tmp[4096];
+    const char* pathname = filename;
+
+    if (dirname != NULL) {
+        snprintf(tmp, sizeof(tmp), "%s/%s", dirname, filename);
+        pathname = tmp;
+    } else {
+        pathname = filename;
+    }
+
+    if ((flags & LIST_MACLABEL) != 0) {
+        return listfile_maclabel(pathname, flags);
+    } else if ((flags & LIST_LONG) != 0) {
+        return listfile_long(pathname, flags);
+    } else /*((flags & LIST_SIZE) != 0)*/ {
+        return listfile_size(pathname, filename, flags);
+    }
+}
+
+static int listdir(const char *name, int flags)
+{
+    char tmp[4096];
+    DIR *d;
+    struct dirent *de;
+    strlist_t  files = STRLIST_INITIALIZER;
+
+    d = opendir(name);
+    if(d == 0) {
+        fprintf(stderr, "opendir failed, %s\n", strerror(errno));
+        return -1;
+    }
+
+    if ((flags & LIST_SIZE) != 0) {
+        show_total_size(name, d, flags);
+    }
+
+    while((de = readdir(d)) != 0){
+        if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) continue;
+        if(de->d_name[0] == '.' && (flags & LIST_ALL) == 0) continue;
+
+        strlist_append_dup(&files, de->d_name);
+    }
+
+    strlist_sort(&files);
+    STRLIST_FOREACH(&files, filename, listfile(name, filename, flags));
+    strlist_done(&files);
+
+    if (flags & LIST_RECURSIVE) {
+        strlist_t subdirs = STRLIST_INITIALIZER;
+
+        rewinddir(d);
+
+        while ((de = readdir(d)) != 0) {
+            struct stat s;
+            int err;
+
+            if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
+                continue;
+            if (de->d_name[0] == '.' && (flags & LIST_ALL) == 0)
+                continue;
+
+            if (!strcmp(name, "/"))
+                snprintf(tmp, sizeof(tmp), "/%s", de->d_name);
+            else
+                snprintf(tmp, sizeof(tmp), "%s/%s", name, de->d_name);
+
+            /*
+             * If the name ends in a '/', use stat() so we treat it like a
+             * directory even if it's a symlink.
+             */
+            if (tmp[strlen(tmp)-1] == '/')
+                err = stat(tmp, &s);
+            else
+                err = lstat(tmp, &s);
+
+            if (err < 0) {
+                perror(tmp);
+                closedir(d);
+                return -1;
+            }
+
+            if (S_ISDIR(s.st_mode)) {
+                strlist_append_dup(&subdirs, tmp);
+            }
+        }
+        strlist_sort(&subdirs);
+        STRLIST_FOREACH(&subdirs, path, {
+            printf("\n%s:\n", path);
+            listdir(path, flags);
+        });
+        strlist_done(&subdirs);
+    }
+
+    closedir(d);
+    return 0;
+}
+
+static int listpath(const char *name, int flags)
+{
+    struct stat s;
+    int err;
+
+    /*
+     * If the name ends in a '/', use stat() so we treat it like a
+     * directory even if it's a symlink.
+     */
+    if (name[strlen(name)-1] == '/')
+        err = stat(name, &s);
+    else
+        err = lstat(name, &s);
+
+    if (err < 0) {
+        perror(name);
+        return -1;
+    }
+
+    if ((flags & LIST_DIRECTORIES) == 0 && S_ISDIR(s.st_mode)) {
+        if (flags & LIST_RECURSIVE)
+            printf("\n%s:\n", name);
+        return listdir(name, flags);
+    } else {
+        /* yeah this calls stat() again*/
+        return listfile(NULL, name, flags);
+    }
+}
+
+int ls_main(int argc, char **argv)
+{
+    int flags = 0;
+    int listed = 0;
+
+    if(argc > 1) {
+        int i;
+        int err = 0;
+        strlist_t  files = STRLIST_INITIALIZER;
+
+        for (i = 1; i < argc; i++) {
+            if (argv[i][0] == '-') {
+                /* an option ? */
+                const char *arg = argv[i]+1;
+                while (arg[0]) {
+                    switch (arg[0]) {
+                    case 'l': flags |= LIST_LONG; break;
+                    case 'n': flags |= LIST_LONG | LIST_LONG_NUMERIC; break;
+                    case 's': flags |= LIST_SIZE; break;
+                    case 'R': flags |= LIST_RECURSIVE; break;
+                    case 'd': flags |= LIST_DIRECTORIES; break;
+                    case 'Z': flags |= LIST_MACLABEL; break;
+                    case 'a': flags |= LIST_ALL; break;
+                    case 'F': flags |= LIST_CLASSIFY; break;
+                    default:
+                        fprintf(stderr, "%s: Unknown option '-%c'. Aborting.\n", "ls", arg[0]);
+                        exit(1);
+                    }
+                    arg++;
+                }
+            } else {
+                /* not an option ? */
+                strlist_append_dup(&files, argv[i]);
+            }
+        }
+
+        if (files.count > 0) {
+            STRLIST_FOREACH(&files, path, {
+                if (listpath(path, flags) != 0) {
+                    err = EXIT_FAILURE;
+                }
+            });
+            strlist_done(&files);
+            return err;
+        }
+    }
+
+    // list working directory if no files or directories were specified
+    return listpath(".", flags);
+}
diff --git a/toolbox/lsmod.c b/toolbox/lsmod.c
new file mode 100644
index 0000000..8b55ee6
--- /dev/null
+++ b/toolbox/lsmod.c
@@ -0,0 +1,10 @@
+#include <stdio.h>
+
+extern int cat_main(int argc, char **argv);
+
+int lsmod_main(int argc, char **argv)
+{
+	char *cat_argv[] = { "cat", "/proc/modules", NULL };
+	return cat_main(2, cat_argv);
+}
+
diff --git a/toolbox/lsof.c b/toolbox/lsof.c
new file mode 100644
index 0000000..4e2f77a
--- /dev/null
+++ b/toolbox/lsof.c
@@ -0,0 +1,254 @@
+/*
+ * Copyright (c) 2010, The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <pwd.h>
+#include <sys/stat.h>
+
+#define BUF_MAX 1024
+#define CMD_DISPLAY_MAX (9 + 1)
+#define USER_DISPLAY_MAX (10 + 1)
+
+struct pid_info_t {
+    pid_t pid;
+    char user[USER_DISPLAY_MAX];
+
+    char cmdline[CMD_DISPLAY_MAX];
+
+    char path[PATH_MAX];
+    ssize_t parent_length;
+};
+
+void print_header()
+{
+    printf("%-9s %5s %10s %4s %9s %18s %9s %10s %s\n",
+            "COMMAND",
+            "PID",
+            "USER",
+            "FD",
+            "TYPE",
+            "DEVICE",
+            "SIZE/OFF",
+            "NODE",
+            "NAME");
+}
+
+void print_type(char *type, struct pid_info_t* info)
+{
+    static ssize_t link_dest_size;
+    static char link_dest[PATH_MAX];
+
+    strncat(info->path, type, sizeof(info->path));
+    if ((link_dest_size = readlink(info->path, link_dest, sizeof(link_dest)-1)) < 0) {
+        if (errno == ENOENT)
+            goto out;
+
+        snprintf(link_dest, sizeof(link_dest), "%s (readlink: %s)", info->path, strerror(errno));
+    } else {
+        link_dest[link_dest_size] = '\0';
+    }
+
+    // Things that are just the root filesystem are uninteresting (we already know)
+    if (!strcmp(link_dest, "/"))
+        goto out;
+
+    printf("%-9s %5d %10s %4s %9s %18s %9s %10s %s\n",
+            info->cmdline, info->pid, info->user, type,
+            "???", "???", "???", "???", link_dest);
+
+out:
+    info->path[info->parent_length] = '\0';
+}
+
+// Prints out all file that have been memory mapped
+void print_maps(struct pid_info_t* info)
+{
+    FILE *maps;
+    char buffer[PATH_MAX + 100];
+
+    size_t offset;
+    int major, minor;
+    char device[10];
+    long int inode;
+    char file[PATH_MAX];
+
+    strncat(info->path, "maps", sizeof(info->path));
+
+    maps = fopen(info->path, "r");
+    if (!maps)
+        goto out;
+
+    while (fscanf(maps, "%*x-%*x %*s %zx %5s %ld %s\n", &offset, device, &inode,
+            file) == 4) {
+        // We don't care about non-file maps
+        if (inode == 0 || !strcmp(device, "00:00"))
+            continue;
+
+        printf("%-9s %5d %10s %4s %9s %18s %9zd %10ld %s\n",
+                info->cmdline, info->pid, info->user, "mem",
+                "???", device, offset, inode, file);
+    }
+
+    fclose(maps);
+
+out:
+    info->path[info->parent_length] = '\0';
+}
+
+// Prints out all open file descriptors
+void print_fds(struct pid_info_t* info)
+{
+    static char* fd_path = "fd/";
+    strncat(info->path, fd_path, sizeof(info->path));
+
+    int previous_length = info->parent_length;
+    info->parent_length += strlen(fd_path);
+
+    DIR *dir = opendir(info->path);
+    if (dir == NULL) {
+        char msg[BUF_MAX];
+        snprintf(msg, sizeof(msg), "%s (opendir: %s)", info->path, strerror(errno));
+        printf("%-9s %5d %10s %4s %9s %18s %9s %10s %s\n",
+                info->cmdline, info->pid, info->user, "FDS",
+                "", "", "", "", msg);
+        goto out;
+    }
+
+    struct dirent* de;
+    while ((de = readdir(dir))) {
+        if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
+            continue;
+
+        print_type(de->d_name, info);
+    }
+    closedir(dir);
+
+out:
+    info->parent_length = previous_length;
+    info->path[info->parent_length] = '\0';
+}
+
+void lsof_dumpinfo(pid_t pid)
+{
+    int fd;
+    struct pid_info_t info;
+    struct stat pidstat;
+    struct passwd *pw;
+
+    info.pid = pid;
+    snprintf(info.path, sizeof(info.path), "/proc/%d/", pid);
+    info.parent_length = strlen(info.path);
+
+    // Get the UID by calling stat on the proc/pid directory.
+    if (!stat(info.path, &pidstat)) {
+        pw = getpwuid(pidstat.st_uid);
+        if (pw) {
+            strncpy(info.user, pw->pw_name, USER_DISPLAY_MAX - 1);
+            info.user[USER_DISPLAY_MAX - 1] = '\0';
+        } else {
+            snprintf(info.user, USER_DISPLAY_MAX, "%d", (int)pidstat.st_uid);
+        }
+    } else {
+        strcpy(info.user, "???");
+    }
+
+    // Read the command line information; each argument is terminated with NULL.
+    strncat(info.path, "cmdline", sizeof(info.path));
+    fd = open(info.path, O_RDONLY);
+    if (fd < 0) {
+        fprintf(stderr, "Couldn't read %s\n", info.path);
+        return;
+    }
+    char cmdline[PATH_MAX];
+    if (read(fd, cmdline, sizeof(cmdline)) < 0) {
+        fprintf(stderr, "Error reading cmdline: %s: %s\n", info.path, strerror(errno));
+        close(fd);
+        return;
+    }
+    close(fd);
+    info.path[info.parent_length] = '\0';
+
+    // We only want the basename of the cmdline
+    strncpy(info.cmdline, basename(cmdline), sizeof(info.cmdline));
+    info.cmdline[sizeof(info.cmdline)-1] = '\0';
+
+    // Read each of these symlinks
+    print_type("cwd", &info);
+    print_type("exe", &info);
+    print_type("root", &info);
+
+    print_fds(&info);
+    print_maps(&info);
+}
+
+int lsof_main(int argc, char *argv[])
+{
+    long int pid = 0;
+    char* endptr;
+    if (argc == 2) {
+        pid = strtol(argv[1], &endptr, 10);
+    }
+
+    print_header();
+
+    if (pid) {
+        lsof_dumpinfo(pid);
+    } else {
+        DIR *dir = opendir("/proc");
+        if (dir == NULL) {
+            fprintf(stderr, "Couldn't open /proc\n");
+            return -1;
+        }
+
+        struct dirent* de;
+        while ((de = readdir(dir))) {
+            if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
+                continue;
+
+            // Only inspect directories that are PID numbers
+            pid = strtol(de->d_name, &endptr, 10);
+            if (*endptr != '\0')
+                continue;
+
+            lsof_dumpinfo(pid);
+        }
+        closedir(dir);
+    }
+
+    return 0;
+}
diff --git a/toolbox/lsusb.c b/toolbox/lsusb.c
new file mode 100644
index 0000000..236e74b
--- /dev/null
+++ b/toolbox/lsusb.c
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <endian.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <usbhost/usbhost.h>
+
+static int verbose = 0;
+static char str_buff[4096];
+
+static const char *get_str(struct usb_device *dev, int id)
+{
+    char *str = usb_device_get_string(dev, id);
+
+    if (id && str) {
+        strlcpy(str_buff, str, sizeof(str_buff));
+        free(str);
+    } else {
+        snprintf(str_buff, sizeof(str_buff), "%02x", id);
+    }
+
+    return str_buff;
+}
+
+
+static void lsusb_parse_device_descriptor(struct usb_device *dev,
+                                          struct usb_device_descriptor *desc)
+{
+    printf("  Device Descriptor\n");
+    printf("\tbcdUSB: %04x\n", letoh16(desc->bcdUSB));
+    printf("\tbDeviceClass: %02x\n", desc->bDeviceClass);
+    printf("\tbDeviceSubClass: %02x\n", desc->bDeviceSubClass);
+    printf("\tbDeviceProtocol: %02x\n", desc->bDeviceProtocol);
+    printf("\tbMaxPacketSize0: %02x\n", desc->bMaxPacketSize0);
+    printf("\tidVendor: %04x\n", letoh16(desc->idVendor));
+    printf("\tidProduct: %04x\n", letoh16(desc->idProduct));
+    printf("\tbcdDevice: %04x\n", letoh16(desc->bcdDevice));
+    printf("\tiManufacturer: %s\n", get_str(dev, desc->iManufacturer));
+    printf("\tiProduct: %s\n", get_str(dev, desc->iProduct));
+    printf("\tiSerialNumber: %s\n", get_str(dev,desc->iSerialNumber));
+    printf("\tbNumConfiguration: %02x\n", desc->bNumConfigurations);
+    printf("\n");
+}
+
+static void lsusb_parse_config_descriptor(struct usb_device *dev,
+                                          struct usb_config_descriptor *desc)
+{
+    printf("  Config Descriptor\n");
+    printf("\twTotalLength: %04x\n", letoh16(desc->wTotalLength));
+    printf("\tbNumInterfaces: %02x\n", desc->bNumInterfaces);
+    printf("\tbConfigurationValue: %02x\n", desc->bConfigurationValue);
+    printf("\tiConfiguration: %s\n", get_str(dev, desc->iConfiguration));
+    printf("\tbmAttributes: %02x\n", desc->bmAttributes);
+    printf("\tbMaxPower: %d mA\n", desc->bMaxPower * 2);
+    printf("\n");
+}
+
+static void lsusb_parse_interface_descriptor(struct usb_device *dev,
+                                             struct usb_interface_descriptor *desc)
+{
+    printf("  Interface Descriptor\n");
+    printf("\tbInterfaceNumber: %02x\n", desc->bInterfaceNumber);
+    printf("\tbAlternateSetting: %02x\n", desc->bAlternateSetting);
+    printf("\tbNumEndpoints: %02x\n", desc->bNumEndpoints);
+    printf("\tbInterfaceClass: %02x\n", desc->bInterfaceClass);
+    printf("\tbInterfaceSubClass: %02x\n", desc->bInterfaceSubClass);
+    printf("\tbInterfaceProtocol: %02x\n", desc->bInterfaceProtocol);
+    printf("\tiInterface: %s\n", get_str(dev, desc->iInterface));
+    printf("\n");
+}
+
+static void lsusb_parse_endpoint_descriptor(struct usb_device *dev,
+                                            struct usb_endpoint_descriptor *desc)
+{
+    printf("  Endpoint Descriptor\n");
+    printf("\tbEndpointAddress: %02x\n", desc->bEndpointAddress);
+    printf("\tbmAttributes: %02x\n", desc->bmAttributes);
+    printf("\twMaxPacketSize: %02x\n", letoh16(desc->wMaxPacketSize));
+    printf("\tbInterval: %02x\n", desc->bInterval);
+    printf("\tbRefresh: %02x\n", desc->bRefresh);
+    printf("\tbSynchAddress: %02x\n", desc->bSynchAddress);
+    printf("\n");
+}
+
+static void lsusb_dump_descriptor(struct usb_device *dev,
+                                  struct usb_descriptor_header *desc)
+{
+    int i;
+    printf("  Descriptor type %02x\n", desc->bDescriptorType);
+
+    for (i = 0; i < desc->bLength; i++ ) {
+        if ((i % 16) == 0)
+            printf("\t%02x:", i);
+        printf(" %02x", ((uint8_t *)desc)[i]);
+        if ((i % 16) == 15)
+            printf("\n");
+    }
+
+    if ((i % 16) != 0)
+        printf("\n");
+    printf("\n");
+}
+
+static void lsusb_parse_descriptor(struct usb_device *dev,
+                                   struct usb_descriptor_header *desc)
+{
+    switch (desc->bDescriptorType) {
+    case USB_DT_DEVICE:
+        lsusb_parse_device_descriptor(dev, (struct usb_device_descriptor *) desc);
+        break;
+
+    case USB_DT_CONFIG:
+        lsusb_parse_config_descriptor(dev, (struct usb_config_descriptor *) desc);
+        break;
+
+    case USB_DT_INTERFACE:
+        lsusb_parse_interface_descriptor(dev, (struct usb_interface_descriptor *) desc);
+        break;
+
+    case USB_DT_ENDPOINT:
+        lsusb_parse_endpoint_descriptor(dev, (struct usb_endpoint_descriptor *) desc);
+        break;
+
+    default:
+        lsusb_dump_descriptor(dev, desc);
+
+        break;
+    }
+}
+
+static int lsusb_device_added(const char *dev_name, void *client_data)
+{
+    struct usb_device *dev = usb_device_open(dev_name);
+
+    if (!dev) {
+        fprintf(stderr, "can't open device %s: %s\n", dev_name, strerror(errno));
+        return 0;
+    }
+
+    if (verbose) {
+        struct usb_descriptor_iter iter;
+        struct usb_descriptor_header *desc;
+
+        printf("%s:\n", dev_name);
+
+        usb_descriptor_iter_init(dev, &iter);
+
+        while ((desc = usb_descriptor_iter_next(&iter)) != NULL)
+            lsusb_parse_descriptor(dev, desc);
+
+    } else {
+        uint16_t vid, pid;
+        char *mfg_name, *product_name, *serial;
+
+        vid = usb_device_get_vendor_id(dev);
+        pid = usb_device_get_product_id(dev);
+        mfg_name = usb_device_get_manufacturer_name(dev);
+        product_name = usb_device_get_product_name(dev);
+        serial = usb_device_get_serial(dev);
+
+        printf("%s: %04x:%04x %s %s %s\n", dev_name, vid, pid,
+               mfg_name, product_name, serial);
+
+        free(mfg_name);
+        free(product_name);
+        free(serial);
+    }
+
+    usb_device_close(dev);
+
+    return 0;
+}
+
+static int lsusb_device_removed(const char *dev_name, void *client_data)
+{
+    return 0;
+}
+
+
+static int lsusb_discovery_done(void *client_data)
+{
+    return 1;
+}
+
+
+
+int lsusb_main(int argc, char **argv)
+{
+    struct usb_host_context *ctx;
+
+    if (argc == 2 && !strcmp(argv[1], "-v"))
+        verbose = 1;
+
+    ctx = usb_host_init();
+    if (!ctx) {
+        perror("usb_host_init:");
+        return 1;
+    }
+
+    usb_host_run(ctx,
+                 lsusb_device_added,
+                 lsusb_device_removed,
+                 lsusb_discovery_done,
+                 NULL);
+
+    usb_host_cleanup(ctx);
+
+    return 0;
+}
+
diff --git a/toolbox/mkdir.c b/toolbox/mkdir.c
new file mode 100644
index 0000000..c81d1db
--- /dev/null
+++ b/toolbox/mkdir.c
@@ -0,0 +1,80 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <cutils/memory.h>
+
+static int usage()
+{
+    fprintf(stderr,"mkdir [OPTION] <target>\n");
+    fprintf(stderr,"    --help           display usage and exit\n");
+    fprintf(stderr,"    -p, --parents    create parent directories as needed\n");
+    return -1;
+}
+
+int mkdir_main(int argc, char *argv[])
+{
+    int symbolic = 0;
+    int ret;
+    if(argc < 2 || strcmp(argv[1], "--help") == 0) {
+        return usage();
+    }
+
+    int recursive = (strcmp(argv[1], "-p") == 0 ||
+                     strcmp(argv[1], "--parents") == 0) ? 1 : 0;
+
+    if(recursive && argc < 3) {
+        // -p specified without a path
+        return usage();
+    }
+
+    if(recursive) {
+        argc--;
+        argv++;
+    }
+
+    char currpath[PATH_MAX], *pathpiece;
+    struct stat st;
+
+    while(argc > 1) {
+        argc--;
+        argv++;
+        if(recursive) {
+            // reset path
+            strcpy(currpath, "");
+            // create the pieces of the path along the way
+            pathpiece = strtok(argv[0], "/");
+            if(argv[0][0] == '/') {
+                // prepend / if needed
+                strcat(currpath, "/");
+            }
+            while(pathpiece != NULL) {
+                if(strlen(currpath) + strlen(pathpiece) + 2/*NUL and slash*/ > PATH_MAX) {
+                    fprintf(stderr, "Invalid path specified: too long\n");
+                    return 1;
+                }
+                strcat(currpath, pathpiece);
+                strcat(currpath, "/");
+                if(stat(currpath, &st) != 0) {
+                    ret = mkdir(currpath, 0777);
+                    if(ret < 0) {
+                        fprintf(stderr, "mkdir failed for %s, %s\n", currpath, strerror(errno));
+                        return ret;
+                    }
+                }
+                pathpiece = strtok(NULL, "/");
+            }
+        } else {
+            ret = mkdir(argv[0], 0777);
+            if(ret < 0) {
+                fprintf(stderr, "mkdir failed for %s, %s\n", argv[0], strerror(errno));
+                return ret;
+            }
+        }
+    }
+    
+    return 0;
+}
diff --git a/toolbox/mknod.c b/toolbox/mknod.c
new file mode 100644
index 0000000..0d2131d
--- /dev/null
+++ b/toolbox/mknod.c
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 20012, The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+// if glibc version >= 2.25
+#include <sys/sysmacros.h>
+
+static void usage(char *cmd);
+
+const struct {
+    char name;
+    mode_t mode;
+} modes_table [] = {
+    {'b', S_IFBLK},
+    {'c', S_IFCHR},
+    {'u', S_IFCHR},
+    {'p', S_IFIFO}
+};
+
+int mknod_main(int argc, char *argv[]) {
+    mode_t mode = 0666;
+    char *name;
+    dev_t dev = 0;
+    int i, total;
+
+    if ((argc != 5) && (argc != 3)) {
+        usage(argv[0]);
+        return -1;
+    }
+
+    name  = argv[1];
+    total = sizeof(modes_table) / sizeof(modes_table[0]);
+    for (i = 0; i < total; i ++) {
+        if (*argv[2] == modes_table[i].name) {
+            mode |= modes_table[i].mode;
+            break;
+        }
+    }
+
+    if (i == total) {
+        usage(argv[0]);
+        return -1;
+    }
+    if (argc == 5) {
+        dev = makedev(atoi(argv[3]), atoi(argv[4]));
+    }
+    if (mknod(name, mode, dev)) {
+        fprintf(stderr, "mknod error: %s\n", strerror(errno));
+    }
+    return 0;
+}
+
+static void usage(char *cmd) {
+    fprintf(stderr, "Usage: %s NAME TYPE [MAJOR MINOR]\n"
+        "b      create a block (buffered) special file\n"
+        "c, u   create a character (unbuffered) special file"
+        "p      create a FIFO"
+        , cmd);
+}
+
+#ifdef HOST
+int main(int argc, char *argv[])
+{
+    return mknod_main(argc, argv);
+}
+#endif
diff --git a/toolbox/mount.c b/toolbox/mount.c
new file mode 100644
index 0000000..b570bdc
--- /dev/null
+++ b/toolbox/mount.c
@@ -0,0 +1,347 @@
+/*
+ * mount.c, by rmk
+ */
+
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <linux/loop.h>
+
+#define ARRAY_SIZE(x)	(sizeof(x) / sizeof(x[0]))
+
+#define DEFAULT_LOOP_DEVICE "/dev/block/loop0"
+#define LOOPDEV_MAXLEN 64
+
+struct mount_opts {
+	const char str[8];
+	unsigned long rwmask;
+	unsigned long rwset;
+	unsigned long rwnoset;
+};
+
+struct extra_opts {
+	char *str;
+	char *end;
+	int used_size;
+	int alloc_size;
+};
+
+/* Newer mount options which may not be defined in a non-bionic libc;
+ * copied  from bionic's sys/mount.h */
+#ifndef MS_DIRSYNC
+#define MS_DIRSYNC      128
+#endif
+#ifndef MS_MOVE
+#define MS_MOVE         8192
+#endif
+#ifndef MS_REC
+#define MS_REC          16384
+#endif
+#ifndef MS_VERBOSE
+#define MS_VERBOSE      32768
+#endif
+
+/*
+ * These options define the function of "mount(2)".
+ */
+#define MS_TYPE	(MS_REMOUNT|MS_BIND|MS_MOVE)
+
+
+static const struct mount_opts options[] = {
+	/* name		mask		set		noset		*/
+	{ "async",	MS_SYNCHRONOUS,	0,		MS_SYNCHRONOUS	},
+	{ "atime",	MS_NOATIME,	0,		MS_NOATIME	},
+	{ "bind",	MS_TYPE,	MS_BIND,	0,		},
+	{ "dev",	MS_NODEV,	0,		MS_NODEV	},
+	{ "diratime",	MS_NODIRATIME,	0,		MS_NODIRATIME	},
+	{ "dirsync",	MS_DIRSYNC,	MS_DIRSYNC,	0		},
+	{ "exec",	MS_NOEXEC,	0,		MS_NOEXEC	},
+	{ "move",	MS_TYPE,	MS_MOVE,	0		},
+	{ "recurse",	MS_REC,		MS_REC,		0		},
+	{ "remount",	MS_TYPE,	MS_REMOUNT,	0		},
+	{ "ro",		MS_RDONLY,	MS_RDONLY,	0		},
+	{ "rw",		MS_RDONLY,	0,		MS_RDONLY	},
+	{ "suid",	MS_NOSUID,	0,		MS_NOSUID	},
+	{ "sync",	MS_SYNCHRONOUS,	MS_SYNCHRONOUS,	0		},
+	{ "verbose",	MS_VERBOSE,	MS_VERBOSE,	0		},
+};
+
+static void add_extra_option(struct extra_opts *extra, char *s)
+{
+	int len = strlen(s);
+	int newlen = extra->used_size + len;
+
+	if (extra->str)
+	       len++;			/* +1 for ',' */
+
+	if (newlen >= extra->alloc_size) {
+		char *new;
+
+		new = realloc(extra->str, newlen + 1);	/* +1 for NUL */
+		if (!new)
+			return;
+
+		extra->str = new;
+		extra->end = extra->str + extra->used_size;
+		extra->alloc_size = newlen;
+	}
+
+	if (extra->used_size) {
+		*extra->end = ',';
+		extra->end++;
+	}
+	strcpy(extra->end, s);
+	extra->used_size += len;
+
+}
+
+static unsigned long
+parse_mount_options(char *arg, unsigned long rwflag, struct extra_opts *extra, int* loop, char *loopdev)
+{
+	char *s;
+    
+    *loop = 0;
+	while ((s = strsep(&arg, ",")) != NULL) {
+		char *opt = s;
+		unsigned int i;
+		int res, no = s[0] == 'n' && s[1] == 'o';
+
+		if (no)
+			s += 2;
+
+        if (strncmp(s, "loop=", 5) == 0) {
+            *loop = 1;
+            strlcpy(loopdev, s + 5, LOOPDEV_MAXLEN);
+            continue;
+        }
+
+        if (strcmp(s, "loop") == 0) {
+            *loop = 1;
+            strlcpy(loopdev, DEFAULT_LOOP_DEVICE, LOOPDEV_MAXLEN);
+            continue;
+        }
+		for (i = 0, res = 1; i < ARRAY_SIZE(options); i++) {
+			res = strcmp(s, options[i].str);
+
+			if (res == 0) {
+				rwflag &= ~options[i].rwmask;
+				if (no)
+					rwflag |= options[i].rwnoset;
+				else
+					rwflag |= options[i].rwset;
+			}
+			if (res <= 0)
+				break;
+		}
+
+		if (res != 0 && s[0])
+			add_extra_option(extra, opt);
+	}
+
+	return rwflag;
+}
+
+static char *progname;
+
+static struct extra_opts extra;
+static unsigned long rwflag;
+
+static int
+do_mount(char *dev, char *dir, char *type, unsigned long rwflag, void *data, int loop,
+         char *loopdev)
+{
+	char *s;
+	int error = 0;
+
+    if (loop) {
+        int file_fd, device_fd;
+        int flags;
+
+        flags = (rwflag & MS_RDONLY) ? O_RDONLY : O_RDWR;
+        
+        file_fd = open(dev, flags);
+        if (file_fd < 0) {
+            perror("open backing file failed");
+            return 1;
+        }
+        device_fd = open(loopdev, flags);
+        if (device_fd < 0) {
+            perror("open loop device failed");
+            close(file_fd);
+            return 1;
+        }
+        if (ioctl(device_fd, LOOP_SET_FD, file_fd) < 0) {
+            perror("ioctl LOOP_SET_FD failed");
+            close(file_fd);
+            close(device_fd);
+            return 1;
+        }
+
+        close(file_fd);
+        close(device_fd);
+        dev = loopdev;
+    }
+
+	while ((s = strsep(&type, ",")) != NULL) {
+retry:
+		if (mount(dev, dir, s, rwflag, data) == -1) {
+			error = errno;
+			/*
+			 * If the filesystem is not found, or the
+			 * superblock is invalid, try the next.
+			 */
+			if (error == ENODEV || error == EINVAL)
+				continue;
+
+			/*
+			 * If we get EACCESS, and we're trying to
+			 * mount readwrite and this isn't a remount,
+			 * try read only.
+			 */
+			if (error == EACCES &&
+			    (rwflag & (MS_REMOUNT|MS_RDONLY)) == 0) {
+				rwflag |= MS_RDONLY;
+				goto retry;
+			}
+			break;
+		}
+	}
+
+	if (error) {
+		errno = error;
+		perror("mount");
+		return 255;
+	}
+
+	return 0;
+}
+
+static int print_mounts()
+{
+    FILE* f;
+    int length;
+    char buffer[100];
+    
+    f = fopen("/proc/mounts", "r");
+    if (!f) {
+        fprintf(stdout, "could not open /proc/mounts\n");
+        return -1;
+    }
+
+    do {
+        length = fread(buffer, 1, 100, f);
+        if (length > 0)
+            fwrite(buffer, 1, length, stdout);
+    } while (length > 0);
+
+    fclose(f);
+    return 0;
+}
+
+static int get_mounts_dev_dir(const char *arg, char **dev, char **dir)
+{
+	FILE *f;
+	char mount_dev[256];
+	char mount_dir[256];
+	char mount_type[256];
+	char mount_opts[256];
+	int mount_freq;
+	int mount_passno;
+	int match;
+
+	f = fopen("/proc/mounts", "r");
+	if (!f) {
+		fprintf(stdout, "could not open /proc/mounts\n");
+		return -1;
+	}
+
+	do {
+		match = fscanf(f, "%255s %255s %255s %255s %d %d\n",
+					   mount_dev, mount_dir, mount_type,
+					   mount_opts, &mount_freq, &mount_passno);
+		mount_dev[255] = 0;
+		mount_dir[255] = 0;
+		mount_type[255] = 0;
+		mount_opts[255] = 0;
+		if (match == 6 &&
+			(strcmp(arg, mount_dev) == 0 ||
+			 strcmp(arg, mount_dir) == 0)) {
+			*dev = strdup(mount_dev);
+			*dir = strdup(mount_dir);
+			fclose(f);
+			return 0;
+		}
+	} while (match != EOF);
+
+	fclose(f);
+	return -1;
+}
+
+int mount_main(int argc, char *argv[])
+{
+	char *type = NULL;
+	char *dev = NULL;
+	char *dir = NULL;
+	int c;
+	int loop = 0;
+	char loopdev[LOOPDEV_MAXLEN];
+
+	progname = argv[0];
+	rwflag = MS_VERBOSE;
+	
+	// mount with no arguments is equivalent to "cat /proc/mounts"
+	if (argc == 1) return print_mounts();
+
+	do {
+		c = getopt(argc, argv, "o:rt:w");
+		if (c == EOF)
+			break;
+		switch (c) {
+		case 'o':
+			rwflag = parse_mount_options(optarg, rwflag, &extra, &loop, loopdev);
+			break;
+		case 'r':
+			rwflag |= MS_RDONLY;
+			break;
+		case 't':
+			type = optarg;
+			break;
+		case 'w':
+			rwflag &= ~MS_RDONLY;
+			break;
+		case '?':
+			fprintf(stderr, "%s: invalid option -%c\n",
+				progname, optopt);
+			exit(1);
+		}
+	} while (1);
+
+	/*
+	 * If remount, bind or move was specified, then we don't
+	 * have a "type" as such.  Use the dummy "none" type.
+	 */
+	if (rwflag & MS_TYPE)
+		type = "none";
+
+	if (optind + 2 == argc) {
+		dev = argv[optind];
+		dir = argv[optind + 1];
+	} else if (optind + 1 == argc && rwflag & MS_REMOUNT) {
+		get_mounts_dev_dir(argv[optind], &dev, &dir);
+	}
+
+	if (dev == NULL || dir == NULL || type == NULL) {
+		fprintf(stderr, "Usage: %s [-r] [-w] [-o options] [-t type] "
+			"device directory\n", progname);
+		exit(1);
+	}
+
+	return do_mount(dev, dir, type, rwflag, extra.str, loop, loopdev);
+	/* We leak dev and dir in some cases, but we're about to exit */
+}
diff --git a/toolbox/mv.c b/toolbox/mv.c
new file mode 100644
index 0000000..a5bc225
--- /dev/null
+++ b/toolbox/mv.c
@@ -0,0 +1,59 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+
+int mv_main(int argc, char *argv[])
+{
+    const char* dest;
+    struct stat st;
+    int i;
+
+    if (argc < 3) {
+        fprintf(stderr,"USAGE: %s <source...> <destination>\n", argv[0]);
+        return -1;
+    }
+
+    /* check if destination exists */
+    dest = argv[argc - 1];
+    if (stat(dest, &st)) {
+        /* an error, unless the destination was missing */
+        if (errno != ENOENT) {
+            fprintf(stderr, "failed on %s - %s\n", dest, strerror(errno));
+            return -1;
+        }
+        st.st_mode = 0;
+    }
+
+    for (i = 1; i < argc - 1; i++) {
+        const char *source = argv[i];
+        char fullDest[PATH_MAX + 1 + PATH_MAX + 1];
+        /* assume we build "dest/source", and let rename() fail on pathsize */
+        if (strlen(dest) + 1 + strlen(source) + 1 > sizeof(fullDest)) {
+            fprintf(stderr, "path too long\n");
+            return -1;
+        }
+        strcpy(fullDest, dest);
+
+        /* if destination is a directory, concat the source file name */
+        if (S_ISDIR(st.st_mode)) {
+            const char *fileName = strrchr(source, '/');
+            if (fullDest[strlen(fullDest)-1] != '/') {
+                strcat(fullDest, "/");
+            }
+            strcat(fullDest, fileName ? fileName + 1 : source);
+        }
+
+        /* attempt to move it */
+        if (rename(source, fullDest)) {
+            fprintf(stderr, "failed on '%s' - %s\n", source, strerror(errno));
+            return -1;
+        }
+    }
+
+    return 0;
+}
+
diff --git a/toolbox/nandread.c b/toolbox/nandread.c
new file mode 100644
index 0000000..cd37630
--- /dev/null
+++ b/toolbox/nandread.c
@@ -0,0 +1,292 @@
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <linux/version.h>
+#include <mtd/mtd-user.h>
+#include <sys/ioctl.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)
+# define MTD_MODE_RAW MTD_FILE_MODE_RAW
+#endif
+
+static int test_empty(const char *buf, size_t size)
+{
+    while(size--) {
+        if (*buf++ != 0xff)
+            return 0;
+    }
+    return 1;
+}
+
+int nandread_main(int argc, char **argv)
+{
+    char *devname = NULL;
+    char *filename = NULL;
+    char *statusfilename = NULL;
+    char *statusext = ".stat";
+    int fd;
+    int outfd = -1;
+    FILE *statusfile = NULL;
+    int ret;
+    int verbose = 0;
+    void *buffer;
+    loff_t pos, opos, end, bpos;
+    loff_t start = 0, len = 0;
+    int c;
+    int i;
+    int empty_pages = 0;
+    int page_count = 0;
+    int bad_block;
+    int rawmode = 0;
+    uint32_t *oob_data;
+    uint8_t *oob_fixed;
+    size_t spare_size = 64;
+    struct mtd_info_user mtdinfo;
+    struct mtd_ecc_stats initial_ecc, last_ecc, ecc;
+    struct mtd_oob_buf oobbuf;
+    nand_ecclayout_t ecclayout;
+
+    do {
+        c = getopt(argc, argv, "d:f:s:S:L:Rhv");
+        if (c == EOF)
+            break;
+        switch (c) {
+        case 'd':
+            devname = optarg;
+            break;
+        case 'f':
+            filename = optarg;
+            break;
+        case 's':
+            spare_size = atoi(optarg);
+            break;
+        case 'S':
+            start = strtoll(optarg, NULL, 0);
+            break;
+        case 'L':
+            len = strtoll(optarg, NULL, 0);
+            break;
+        case 'R':
+            rawmode = 1;
+            break;
+        case 'v':
+            verbose++;
+            break;
+        case 'h':
+            fprintf(stderr, "%s [-d <dev>] [-f file] [-s sparesize] [-vh]\n"
+                    "  -d <dev>   Read from <dev>\n"
+                    "  -f <file>  Write to <file>\n"
+                    "  -s <size>  Number of spare bytes in file (default 64)\n"
+                    "  -R         Raw mode\n"
+                    "  -S <start> Start offset (default 0)\n"
+                    "  -L <len>   Length (default 0)\n"
+                    "  -v         Print info\n"
+                    "  -h         Print help\n", argv[0]);
+            return -1;
+        case '?':
+            fprintf(stderr, "%s: invalid option -%c\n",
+                argv[0], optopt);
+            exit(1);
+        }
+    } while (1);
+
+    if (optind < argc) {
+        fprintf(stderr, "%s: extra arguments\n", argv[0]);
+        return 1;
+    }
+    if (!devname) {
+        fprintf(stderr, "%s: specify device name\n", argv[0]);
+        return 1;
+    }
+
+    fd = open(devname, O_RDONLY);
+    if (fd < 0) {
+        fprintf(stderr, "cannot open %s, %s\n", devname, strerror(errno));
+        return 1;
+    }
+
+    if (filename) {
+        outfd = creat(filename, 0666);
+        if (outfd < 0) {
+            fprintf(stderr, "cannot open %s, %s\n", filename, strerror(errno));
+            return 1;
+        }
+        statusfilename = malloc(strlen(filename) + strlen(statusext) + 1);
+        strcpy(statusfilename, filename);
+        strcat(statusfilename, statusext);
+        statusfile = fopen(statusfilename, "w+");
+        if (!statusfile) {
+            fprintf(stderr, "cannot open %s, %s\n", statusfilename, strerror(errno));
+            return 1;
+        }
+    }
+
+    ret = ioctl(fd, MEMGETINFO, &mtdinfo);
+    if (ret) {
+        fprintf(stderr, "failed get mtd info for %s, %s\n",
+                devname, strerror(errno));
+        return 1;
+    }
+
+    if (verbose) {
+        printf("size: %u\n", mtdinfo.size);
+        printf("erase size: %u\n", mtdinfo.erasesize);
+        printf("write size: %u\n", mtdinfo.writesize);
+        printf("oob size: %u\n", mtdinfo.oobsize);
+    }
+
+    buffer = malloc(mtdinfo.writesize + mtdinfo.oobsize + spare_size);
+    if (!buffer) {
+        fprintf(stderr, "failed allocate readbuffer size %u\n",
+                mtdinfo.writesize + mtdinfo.oobsize);
+        return 1;
+    }
+
+    oobbuf.length = mtdinfo.oobsize;
+    oob_data = (uint32_t *)((uint8_t *)buffer + mtdinfo.writesize);
+    memset(oob_data, 0xff, mtdinfo.oobsize + spare_size);
+    oobbuf.ptr = (uint8_t *)oob_data + spare_size;
+
+    ret = ioctl(fd, ECCGETLAYOUT, &ecclayout);
+    if (ret) {
+        fprintf(stderr, "failed get ecc layout for %s, %s\n",
+                devname, strerror(errno));
+        return 1;
+    }
+    if (verbose) {
+        printf("ecc bytes: %u\n", ecclayout.eccbytes);
+        printf("oobavail: %u\n", ecclayout.oobavail);
+    }
+    if (ecclayout.oobavail > spare_size)
+        printf("oobavail, %d > image spare size, %d\n", ecclayout.oobavail, spare_size);
+
+    ret = ioctl(fd, ECCGETSTATS, &initial_ecc);
+    if (ret) {
+        fprintf(stderr, "failed get ecc stats for %s, %s\n",
+                devname, strerror(errno));
+        return 1;
+    }
+    last_ecc = initial_ecc;
+
+    if (verbose) {
+        printf("initial ecc corrected: %u\n", initial_ecc.corrected);
+        printf("initial ecc failed: %u\n", initial_ecc.failed);
+        printf("initial ecc badblocks: %u\n", initial_ecc.badblocks);
+        printf("initial ecc bbtblocks: %u\n", initial_ecc.bbtblocks);
+    }
+
+    if (rawmode) {
+        rawmode = mtdinfo.oobsize;
+        ret = ioctl(fd, MTDFILEMODE, MTD_MODE_RAW);
+        if (ret) {
+            fprintf(stderr, "failed set raw mode for %s, %s\n",
+                    devname, strerror(errno));
+            return 1;
+        }
+    }
+
+    end = len ? (start + len) : mtdinfo.size;
+    for (pos = start, opos = 0; pos < end; pos += mtdinfo.writesize) {
+        bad_block = 0;
+        if (verbose > 3)
+            printf("reading at %llx\n", pos);
+        lseek64(fd, pos, SEEK_SET);
+        ret = read(fd, buffer, mtdinfo.writesize + rawmode);
+        if (ret < (int)mtdinfo.writesize) {
+            fprintf(stderr, "short read at %llx, %d\n", pos, ret);
+            bad_block = 2;
+        }
+        if (!rawmode) {
+            oobbuf.start = pos;
+            ret = ioctl(fd, MEMREADOOB, &oobbuf);
+            if (ret) {
+                fprintf(stderr, "failed to read oob data at %llx, %d\n", pos, ret);
+                bad_block = 2;
+            }
+        }
+        ret = ioctl(fd, ECCGETSTATS, &ecc);
+        if (ret) {
+            fprintf(stderr, "failed get ecc stats for %s, %s\n",
+                    devname, strerror(errno));
+            return 1;
+        }
+        bpos = pos / mtdinfo.erasesize * mtdinfo.erasesize;
+        ret = ioctl(fd, MEMGETBADBLOCK, &bpos);
+        if (ret && errno != EOPNOTSUPP) {
+            printf("badblock at %llx\n", pos);
+            bad_block = 1;
+        }
+        if (ecc.corrected != last_ecc.corrected)
+            printf("ecc corrected, %u, at %llx\n", ecc.corrected - last_ecc.corrected, pos);
+        if (ecc.failed != last_ecc.failed)
+            printf("ecc failed, %u, at %llx\n", ecc.failed - last_ecc.failed, pos);
+        if (ecc.badblocks != last_ecc.badblocks)
+            printf("ecc badblocks, %u, at %llx\n", ecc.badblocks - last_ecc.badblocks, pos);
+        if (ecc.bbtblocks != last_ecc.bbtblocks)
+            printf("ecc bbtblocks, %u, at %llx\n", ecc.bbtblocks - last_ecc.bbtblocks, pos);
+
+        if (!rawmode) {
+            oob_fixed = (uint8_t *)oob_data;
+            for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES; i++) {
+                int len = ecclayout.oobfree[i].length;
+                if (oob_fixed + len > oobbuf.ptr)
+                    len = oobbuf.ptr - oob_fixed;
+                if (len) {
+                    memcpy(oob_fixed, oobbuf.ptr + ecclayout.oobfree[i].offset, len);
+                    oob_fixed += len;
+                }
+            }
+        }
+
+        if (outfd >= 0) {
+            ret = write(outfd, buffer, mtdinfo.writesize + spare_size);
+            if (ret < (int)(mtdinfo.writesize + spare_size)) {
+                fprintf(stderr, "short write at %llx, %d\n", pos, ret);
+                close(outfd);
+                outfd = -1;
+            }
+            if (ecc.corrected != last_ecc.corrected)
+                fprintf(statusfile, "%08llx: ecc corrected\n", opos);
+            if (ecc.failed != last_ecc.failed)
+                fprintf(statusfile, "%08llx: ecc failed\n", opos);
+            if (bad_block == 1)
+                fprintf(statusfile, "%08llx: badblock\n", opos);
+            if (bad_block == 2)
+                fprintf(statusfile, "%08llx: read error\n", opos);
+            opos += mtdinfo.writesize + spare_size;
+        }
+
+        last_ecc = ecc;
+        page_count++;
+        if (test_empty(buffer, mtdinfo.writesize + mtdinfo.oobsize + spare_size))
+            empty_pages++;
+        else if (verbose > 2 || (verbose > 1 && !(pos & (mtdinfo.erasesize - 1))))
+            printf("page at %llx (%d oobbytes): %08x %08x %08x %08x "
+                   "%08x %08x %08x %08x\n", pos, oobbuf.start,
+                   oob_data[0], oob_data[1], oob_data[2], oob_data[3],
+                   oob_data[4], oob_data[5], oob_data[6], oob_data[7]);
+    }
+
+    if (outfd >= 0) {
+        fprintf(statusfile, "read %d pages, %d empty\n", page_count, empty_pages);
+        fprintf(statusfile, "total ecc corrected, %u\n", ecc.corrected - initial_ecc.corrected);
+        fprintf(statusfile, "total ecc failed, %u\n", ecc.failed - initial_ecc.failed);
+        fprintf(statusfile, "total ecc badblocks, %u\n", ecc.badblocks - initial_ecc.badblocks);
+        fprintf(statusfile, "total ecc bbtblocks, %u\n", ecc.bbtblocks - initial_ecc.bbtblocks);
+    }
+    if (verbose) {
+        printf("total ecc corrected, %u\n", ecc.corrected - initial_ecc.corrected);
+        printf("total ecc failed, %u\n", ecc.failed - initial_ecc.failed);
+        printf("total ecc badblocks, %u\n", ecc.badblocks - initial_ecc.badblocks);
+        printf("total ecc bbtblocks, %u\n", ecc.bbtblocks - initial_ecc.bbtblocks);
+    }
+    printf("read %d pages, %d empty\n", page_count, empty_pages);
+
+    return 0;
+}
diff --git a/toolbox/netstat.c b/toolbox/netstat.c
new file mode 100644
index 0000000..5768599
--- /dev/null
+++ b/toolbox/netstat.c
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2008, The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <arpa/inet.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+typedef union iaddr iaddr;
+typedef union iaddr6 iaddr6;
+
+union iaddr {
+    unsigned u;
+    unsigned char b[4];
+};
+
+union iaddr6 {
+    struct {
+        unsigned a;
+        unsigned b;
+        unsigned c;
+        unsigned d;
+    } u;
+    unsigned char b[16];
+};
+
+static const char *state2str(unsigned state)
+{
+    switch(state){
+    case 0x1: return "ESTABLISHED";
+    case 0x2: return "SYN_SENT";
+    case 0x3: return "SYN_RECV";
+    case 0x4: return "FIN_WAIT1";
+    case 0x5: return "FIN_WAIT2";
+    case 0x6: return "TIME_WAIT";
+    case 0x7: return "CLOSE";
+    case 0x8: return "CLOSE_WAIT";
+    case 0x9: return "LAST_ACK";
+    case 0xA: return "LISTEN";
+    case 0xB: return "CLOSING";
+    default: return "UNKNOWN";
+    }
+}
+
+/* addr + : + port + \0 */
+#define ADDR_LEN INET6_ADDRSTRLEN + 1 + 5 + 1
+
+static void addr2str(int af, const void *addr, unsigned port, char *buf)
+{
+    if (inet_ntop(af, addr, buf, ADDR_LEN) == NULL) {
+        *buf = '\0';
+        return;
+    }
+    size_t len = strlen(buf);
+    if (port) {
+        snprintf(buf+len, ADDR_LEN-len, ":%d", port);
+    } else {
+        strncat(buf+len, ":*", ADDR_LEN-len-1);
+    }
+}
+
+static void ipv4(const char *filename, const char *label) {
+    FILE *fp = fopen(filename, "r");
+    if (fp == NULL) {
+        return;
+    }
+    char buf[BUFSIZ];
+    fgets(buf, BUFSIZ, fp);
+    while (fgets(buf, BUFSIZ, fp)){
+        char lip[ADDR_LEN];
+        char rip[ADDR_LEN];
+        iaddr laddr, raddr;
+        unsigned lport, rport, state, txq, rxq, num;
+        int n = sscanf(buf, " %d: %x:%x %x:%x %x %x:%x",
+                       &num, &laddr.u, &lport, &raddr.u, &rport,
+                       &state, &txq, &rxq);
+        if (n == 8) {
+            addr2str(AF_INET, &laddr, lport, lip);
+            addr2str(AF_INET, &raddr, rport, rip);
+
+            printf("%4s  %6d %6d %-22s %-22s %s\n",
+                   label, txq, rxq, lip, rip,
+                   state2str(state));
+        }
+    }
+    fclose(fp);
+}
+
+static void ipv6(const char *filename, const char *label) {
+    FILE *fp = fopen(filename, "r");
+    if (fp == NULL) {
+        return;
+    }
+    char buf[BUFSIZ];
+    fgets(buf, BUFSIZ, fp);
+    while (fgets(buf, BUFSIZ, fp)){
+        char lip[ADDR_LEN];
+        char rip[ADDR_LEN];
+        iaddr6 laddr6, raddr6;
+        unsigned lport, rport, state, txq, rxq, num;
+        int n = sscanf(buf, " %d: %8x%8x%8x%8x:%x %8x%8x%8x%8x:%x %x %x:%x",
+                       &num, &laddr6.u.a, &laddr6.u.b, &laddr6.u.c, &laddr6.u.d, &lport,
+                       &raddr6.u.a, &raddr6.u.b, &raddr6.u.c, &raddr6.u.d, &rport,
+                       &state, &txq, &rxq);
+        if (n == 14) {
+            addr2str(AF_INET6, &laddr6, lport, lip);
+            addr2str(AF_INET6, &raddr6, rport, rip);
+
+            printf("%4s  %6d %6d %-22s %-22s %s\n",
+                   label, txq, rxq, lip, rip,
+                   state2str(state));
+        }
+    }
+    fclose(fp);
+}
+
+int netstat_main(int argc, char *argv[])
+{
+    printf("Proto Recv-Q Send-Q Local Address          Foreign Address        State\n");
+    ipv4("/proc/net/tcp",  "tcp");
+    ipv4("/proc/net/udp",  "udp");
+    ipv6("/proc/net/tcp6", "tcp6");
+    ipv6("/proc/net/udp6", "udp6");
+    return 0;
+}
diff --git a/toolbox/newfs_msdos.c b/toolbox/newfs_msdos.c
new file mode 100644
index 0000000..5f94083
--- /dev/null
+++ b/toolbox/newfs_msdos.c
@@ -0,0 +1,1090 @@
+/*
+ * Copyright (c) 1998 Robert Nordier
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+  "$FreeBSD: src/sbin/newfs_msdos/newfs_msdos.c,v 1.33 2009/04/11 14:56:29 ed Exp $";
+#endif /* not lint */
+
+#include <sys/param.h>
+
+#ifndef ANDROID
+  #include <sys/fdcio.h>
+  #include <sys/disk.h>
+  #include <sys/disklabel.h>
+  #include <sys/mount.h>
+#else
+  #include <stdarg.h>
+  #include <linux/fs.h>
+  #include <linux/hdreg.h>
+#endif
+
+#if HAVE_GLIBC
+  #define __unused __attribute__((__unused__))
+#endif
+
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#define MAXU16	  0xffff	/* maximum unsigned 16-bit quantity */
+#define BPN	  4		/* bits per nibble */
+#define NPB	  2		/* nibbles per byte */
+
+#define DOSMAGIC  0xaa55	/* DOS magic number */
+#define MINBPS	  512		/* minimum bytes per sector */
+#define MAXSPC	  128		/* maximum sectors per cluster */
+#define MAXNFT	  16		/* maximum number of FATs */
+#define DEFBLK	  4096		/* default block size */
+#define DEFBLK16  2048		/* default block size FAT16 */
+#define DEFRDE	  512		/* default root directory entries */
+#define RESFTE	  2		/* reserved FAT entries */
+#define MINCLS12  1		/* minimum FAT12 clusters */
+#define MINCLS16  0x1000	/* minimum FAT16 clusters */
+#define MINCLS32  2		/* minimum FAT32 clusters */
+#define MAXCLS12  0xfed 	/* maximum FAT12 clusters */
+#define MAXCLS16  0xfff5	/* maximum FAT16 clusters */
+#define MAXCLS32  0xffffff5	/* maximum FAT32 clusters */
+
+#define mincls(fat)  ((fat) == 12 ? MINCLS12 :	\
+		      (fat) == 16 ? MINCLS16 :	\
+				    MINCLS32)
+
+#define maxcls(fat)  ((fat) == 12 ? MAXCLS12 :	\
+		      (fat) == 16 ? MAXCLS16 :	\
+				    MAXCLS32)
+
+#define mk1(p, x)				\
+    (p) = (u_int8_t)(x)
+
+#define mk2(p, x)				\
+    (p)[0] = (u_int8_t)(x),			\
+    (p)[1] = (u_int8_t)((x) >> 010)
+
+#define mk4(p, x)				\
+    (p)[0] = (u_int8_t)(x),			\
+    (p)[1] = (u_int8_t)((x) >> 010),		\
+    (p)[2] = (u_int8_t)((x) >> 020),		\
+    (p)[3] = (u_int8_t)((x) >> 030)
+
+#define argto1(arg, lo, msg)  argtou(arg, lo, 0xff, msg)
+#define argto2(arg, lo, msg)  argtou(arg, lo, 0xffff, msg)
+#define argto4(arg, lo, msg)  argtou(arg, lo, 0xffffffff, msg)
+#define argtox(arg, lo, msg)  argtou(arg, lo, UINT_MAX, msg)
+
+struct bs {
+    u_int8_t jmp[3];		/* bootstrap entry point */
+    u_int8_t oem[8];		/* OEM name and version */
+};
+
+struct bsbpb {
+    u_int8_t bps[2];		/* bytes per sector */
+    u_int8_t spc;		/* sectors per cluster */
+    u_int8_t res[2];		/* reserved sectors */
+    u_int8_t nft;		/* number of FATs */
+    u_int8_t rde[2];		/* root directory entries */
+    u_int8_t sec[2];		/* total sectors */
+    u_int8_t mid;		/* media descriptor */
+    u_int8_t spf[2];		/* sectors per FAT */
+    u_int8_t spt[2];		/* sectors per track */
+    u_int8_t hds[2];		/* drive heads */
+    u_int8_t hid[4];		/* hidden sectors */
+    u_int8_t bsec[4];		/* big total sectors */
+};
+
+struct bsxbpb {
+    u_int8_t bspf[4];		/* big sectors per FAT */
+    u_int8_t xflg[2];		/* FAT control flags */
+    u_int8_t vers[2];		/* file system version */
+    u_int8_t rdcl[4];		/* root directory start cluster */
+    u_int8_t infs[2];		/* file system info sector */
+    u_int8_t bkbs[2];		/* backup boot sector */
+    u_int8_t rsvd[12];		/* reserved */
+};
+
+struct bsx {
+    u_int8_t drv;		/* drive number */
+    u_int8_t rsvd;		/* reserved */
+    u_int8_t sig;		/* extended boot signature */
+    u_int8_t volid[4];		/* volume ID number */
+    u_int8_t label[11]; 	/* volume label */
+    u_int8_t type[8];		/* file system type */
+};
+
+struct de {
+    u_int8_t namext[11];	/* name and extension */
+    u_int8_t attr;		/* attributes */
+    u_int8_t rsvd[10];		/* reserved */
+    u_int8_t time[2];		/* creation time */
+    u_int8_t date[2];		/* creation date */
+    u_int8_t clus[2];		/* starting cluster */
+    u_int8_t size[4];		/* size */
+};
+
+struct bpb {
+    u_int bps;			/* bytes per sector */
+    u_int spc;			/* sectors per cluster */
+    u_int res;			/* reserved sectors */
+    u_int nft;			/* number of FATs */
+    u_int rde;			/* root directory entries */
+    u_int sec;			/* total sectors */
+    u_int mid;			/* media descriptor */
+    u_int spf;			/* sectors per FAT */
+    u_int spt;			/* sectors per track */
+    u_int hds;			/* drive heads */
+    u_int hid;			/* hidden sectors */
+    u_int bsec; 		/* big total sectors */
+    u_int bspf; 		/* big sectors per FAT */
+    u_int rdcl; 		/* root directory start cluster */
+    u_int infs; 		/* file system info sector */
+    u_int bkbs; 		/* backup boot sector */
+};
+
+#define BPBGAP 0, 0, 0, 0, 0, 0
+
+static struct {
+    const char *name;
+    struct bpb bpb;
+} const stdfmt[] = {
+    {"160",  {512, 1, 1, 2,  64,  320, 0xfe, 1,  8, 1, BPBGAP}},
+    {"180",  {512, 1, 1, 2,  64,  360, 0xfc, 2,  9, 1, BPBGAP}},
+    {"320",  {512, 2, 1, 2, 112,  640, 0xff, 1,  8, 2, BPBGAP}},
+    {"360",  {512, 2, 1, 2, 112,  720, 0xfd, 2,  9, 2, BPBGAP}},
+    {"640",  {512, 2, 1, 2, 112, 1280, 0xfb, 2,  8, 2, BPBGAP}},    
+    {"720",  {512, 2, 1, 2, 112, 1440, 0xf9, 3,  9, 2, BPBGAP}},
+    {"1200", {512, 1, 1, 2, 224, 2400, 0xf9, 7, 15, 2, BPBGAP}},
+    {"1232", {1024,1, 1, 2, 192, 1232, 0xfe, 2,  8, 2, BPBGAP}},    
+    {"1440", {512, 1, 1, 2, 224, 2880, 0xf0, 9, 18, 2, BPBGAP}},
+    {"2880", {512, 2, 1, 2, 240, 5760, 0xf0, 9, 36, 2, BPBGAP}}
+};
+
+static const u_int8_t bootcode[] = {
+    0xfa,			/* cli		    */
+    0x31, 0xc0, 		/* xor	   ax,ax    */
+    0x8e, 0xd0, 		/* mov	   ss,ax    */
+    0xbc, 0x00, 0x7c,		/* mov	   sp,7c00h */
+    0xfb,			/* sti		    */
+    0x8e, 0xd8, 		/* mov	   ds,ax    */
+    0xe8, 0x00, 0x00,		/* call    $ + 3    */
+    0x5e,			/* pop	   si	    */
+    0x83, 0xc6, 0x19,		/* add	   si,+19h  */
+    0xbb, 0x07, 0x00,		/* mov	   bx,0007h */
+    0xfc,			/* cld		    */
+    0xac,			/* lodsb	    */
+    0x84, 0xc0, 		/* test    al,al    */
+    0x74, 0x06, 		/* jz	   $ + 8    */
+    0xb4, 0x0e, 		/* mov	   ah,0eh   */
+    0xcd, 0x10, 		/* int	   10h	    */
+    0xeb, 0xf5, 		/* jmp	   $ - 9    */
+    0x30, 0xe4, 		/* xor	   ah,ah    */
+    0xcd, 0x16, 		/* int	   16h	    */
+    0xcd, 0x19, 		/* int	   19h	    */
+    0x0d, 0x0a,
+    'N', 'o', 'n', '-', 's', 'y', 's', 't',
+    'e', 'm', ' ', 'd', 'i', 's', 'k',
+    0x0d, 0x0a,
+    'P', 'r', 'e', 's', 's', ' ', 'a', 'n',
+    'y', ' ', 'k', 'e', 'y', ' ', 't', 'o',
+    ' ', 'r', 'e', 'b', 'o', 'o', 't',
+    0x0d, 0x0a,
+    0
+};
+
+static void check_mounted(const char *, mode_t);
+static void getstdfmt(const char *, struct bpb *);
+static void getdiskinfo(int, const char *, const char *, int,
+			struct bpb *);
+static void print_bpb(struct bpb *);
+static u_int ckgeom(const char *, u_int, const char *);
+static u_int argtou(const char *, u_int, u_int, const char *);
+static off_t argtooff(const char *, const char *);
+static int oklabel(const char *);
+static void mklabel(u_int8_t *, const char *);
+static void setstr(u_int8_t *, const char *, size_t);
+static void usage(void);
+
+#ifdef ANDROID
+#define powerof2(x)     ((((x) - 1) & (x)) == 0)
+#define howmany(x, y)   (((x) + ((y) - 1)) / (y))
+#define MAX(x,y) ((x) > (y) ? (x) : (y))
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+
+#endif
+/*
+ * Construct a FAT12, FAT16, or FAT32 file system.
+ */
+int
+newfs_msdos_main(int argc, char *argv[])
+{
+    static const char opts[] = "@:NB:C:F:I:L:O:S:a:b:c:e:f:h:i:k:m:n:o:r:s:u:";
+    const char *opt_B = NULL, *opt_L = NULL, *opt_O = NULL, *opt_f = NULL;
+    u_int opt_F = 0, opt_I = 0, opt_S = 0, opt_a = 0, opt_b = 0, opt_c = 0;
+    u_int opt_e = 0, opt_h = 0, opt_i = 0, opt_k = 0, opt_m = 0, opt_n = 0;
+    u_int opt_o = 0, opt_r = 0, opt_s = 0, opt_u = 0;
+    int opt_N = 0;
+    int Iflag = 0, mflag = 0, oflag = 0;
+    char buf[MAXPATHLEN];
+    struct stat sb;
+    struct timeval tv;
+    struct bpb bpb;
+    struct tm *tm;
+    struct bs *bs;
+    struct bsbpb *bsbpb;
+    struct bsxbpb *bsxbpb;
+    struct bsx *bsx;
+    struct de *de;
+    u_int8_t *img;
+    const char *fname, *dtype, *bname;
+    ssize_t n;
+    time_t now;
+    u_int fat, bss, rds, cls, dir, lsn, x, x1, x2;
+    int ch, fd, fd1;
+    off_t opt_create = 0, opt_ofs = 0;
+
+    while ((ch = getopt(argc, argv, opts)) != -1)
+	switch (ch) {
+	case '@':
+	    opt_ofs = argtooff(optarg, "offset");
+	    break;
+	case 'N':
+	    opt_N = 1;
+	    break;
+	case 'B':
+	    opt_B = optarg;
+	    break;
+	case 'C':
+	    opt_create = argtooff(optarg, "create size");
+	    break;
+	case 'F':
+	    if (strcmp(optarg, "12") &&
+		strcmp(optarg, "16") &&
+		strcmp(optarg, "32"))
+		errx(1, "%s: bad FAT type", optarg);
+	    opt_F = atoi(optarg);
+	    break;
+	case 'I':
+	    opt_I = argto4(optarg, 0, "volume ID");
+	    Iflag = 1;
+	    break;
+	case 'L':
+	    if (!oklabel(optarg))
+		errx(1, "%s: bad volume label", optarg);
+	    opt_L = optarg;
+	    break;
+	case 'O':
+	    if (strlen(optarg) > 8)
+		errx(1, "%s: bad OEM string", optarg);
+	    opt_O = optarg;
+	    break;
+	case 'S':
+	    opt_S = argto2(optarg, 1, "bytes/sector");
+	    break;
+	case 'a':
+	    opt_a = argto4(optarg, 1, "sectors/FAT");
+	    break;
+	case 'b':
+	    opt_b = argtox(optarg, 1, "block size");
+	    opt_c = 0;
+	    break;
+	case 'c':
+	    opt_c = argto1(optarg, 1, "sectors/cluster");
+	    opt_b = 0;
+	    break;
+	case 'e':
+	    opt_e = argto2(optarg, 1, "directory entries");
+	    break;
+	case 'f':
+	    opt_f = optarg;
+	    break;
+	case 'h':
+	    opt_h = argto2(optarg, 1, "drive heads");
+	    break;
+	case 'i':
+	    opt_i = argto2(optarg, 1, "info sector");
+	    break;
+	case 'k':
+	    opt_k = argto2(optarg, 1, "backup sector");
+	    break;
+	case 'm':
+	    opt_m = argto1(optarg, 0, "media descriptor");
+	    mflag = 1;
+	    break;
+	case 'n':
+	    opt_n = argto1(optarg, 1, "number of FATs");
+	    break;
+	case 'o':
+	    opt_o = argto4(optarg, 0, "hidden sectors");
+	    oflag = 1;
+	    break;
+	case 'r':
+	    opt_r = argto2(optarg, 1, "reserved sectors");
+	    break;
+	case 's':
+	    opt_s = argto4(optarg, 1, "file system size");
+	    break;
+	case 'u':
+	    opt_u = argto2(optarg, 1, "sectors/track");
+	    break;
+	default:
+	    usage();
+	}
+    argc -= optind;
+    argv += optind;
+    if (argc < 1 || argc > 2)
+	usage();
+    fname = *argv++;
+    if (!opt_create && !strchr(fname, '/')) {
+	snprintf(buf, sizeof(buf), "%s%s", _PATH_DEV, fname);
+	if (!(fname = strdup(buf)))
+	    err(1, NULL);
+    }
+    dtype = *argv;
+    if (opt_create) {
+	if (opt_N)
+	    errx(1, "create (-C) is incompatible with -N");
+	fd = open(fname, O_RDWR | O_CREAT | O_TRUNC, 0644);
+	if (fd == -1)
+	    errx(1, "failed to create %s", fname);
+	if (ftruncate(fd, opt_create))
+	    errx(1, "failed to initialize %jd bytes", (intmax_t)opt_create);
+    } else if ((fd = open(fname, opt_N ? O_RDONLY : O_RDWR)) == -1)
+	err(1, "%s", fname);
+    if (fstat(fd, &sb))
+	err(1, "%s", fname);
+    if (opt_create) {
+	if (!S_ISREG(sb.st_mode))
+	    warnx("warning, %s is not a regular file", fname);
+    } else {
+	if (!S_ISCHR(sb.st_mode))
+	    warnx("warning, %s is not a character device", fname);
+    }
+    if (!opt_N)
+	check_mounted(fname, sb.st_mode);
+    if (opt_ofs && opt_ofs != lseek(fd, opt_ofs, SEEK_SET))
+	errx(1, "cannot seek to %jd", (intmax_t)opt_ofs);
+    memset(&bpb, 0, sizeof(bpb));
+    if (opt_f) {
+	getstdfmt(opt_f, &bpb);
+	bpb.bsec = bpb.sec;
+	bpb.sec = 0;
+	bpb.bspf = bpb.spf;
+	bpb.spf = 0;
+    }
+    if (opt_h)
+	bpb.hds = opt_h;
+    if (opt_u)
+	bpb.spt = opt_u;
+    if (opt_S)
+	bpb.bps = opt_S;
+    if (opt_s)
+	bpb.bsec = opt_s;
+    if (oflag)
+	bpb.hid = opt_o;
+    if (!(opt_f || (opt_h && opt_u && opt_S && opt_s && oflag))) {
+	off_t delta;
+	getdiskinfo(fd, fname, dtype, oflag, &bpb);
+        if (opt_s) {
+            bpb.bsec = opt_s;
+        }
+	bpb.bsec -= (opt_ofs / bpb.bps);
+	delta = bpb.bsec % bpb.spt;
+	if (delta != 0) {
+	    warnx("trim %d sectors from %d to adjust to a multiple of %d",
+		(int)delta, bpb.bsec, bpb.spt);
+	    bpb.bsec -= delta;
+	}
+	if (bpb.spc == 0) {	/* set defaults */
+	    if (bpb.bsec <= 6000)	/* about 3MB -> 512 bytes */
+		bpb.spc = 1;
+	    else if (bpb.bsec <= (1<<17)) /* 64M -> 4k */
+		bpb.spc = 8;
+	    else if (bpb.bsec <= (1<<19)) /* 256M -> 8k */
+		bpb.spc = 16;
+	    else if (bpb.bsec <= (1<<21)) /* 1G -> 16k */
+		bpb.spc = 32;
+	    else
+		bpb.spc = 64;		/* otherwise 32k */
+	}
+    }
+    if (!powerof2(bpb.bps))
+	errx(1, "bytes/sector (%u) is not a power of 2", bpb.bps);
+    if (bpb.bps < MINBPS)
+	errx(1, "bytes/sector (%u) is too small; minimum is %u",
+	     bpb.bps, MINBPS);
+    if (!(fat = opt_F)) {
+	if (opt_f)
+	    fat = 12;
+	else if (!opt_e && (opt_i || opt_k))
+	    fat = 32;
+    }
+    if ((fat == 32 && opt_e) || (fat != 32 && (opt_i || opt_k)))
+	errx(1, "-%c is not a legal FAT%s option",
+	     fat == 32 ? 'e' : opt_i ? 'i' : 'k',
+	     fat == 32 ? "32" : "12/16");
+    if (opt_f && fat == 32)
+	bpb.rde = 0;
+    if (opt_b) {
+	if (!powerof2(opt_b))
+	    errx(1, "block size (%u) is not a power of 2", opt_b);
+	if (opt_b < bpb.bps)
+	    errx(1, "block size (%u) is too small; minimum is %u",
+		 opt_b, bpb.bps);
+	if (opt_b > bpb.bps * MAXSPC)
+	    errx(1, "block size (%u) is too large; maximum is %u",
+		 opt_b, bpb.bps * MAXSPC);
+	bpb.spc = opt_b / bpb.bps;
+    }
+    if (opt_c) {
+	if (!powerof2(opt_c))
+	    errx(1, "sectors/cluster (%u) is not a power of 2", opt_c);
+	bpb.spc = opt_c;
+    }
+    if (opt_r)
+	bpb.res = opt_r;
+    if (opt_n) {
+	if (opt_n > MAXNFT)
+	    errx(1, "number of FATs (%u) is too large; maximum is %u",
+		 opt_n, MAXNFT);
+	bpb.nft = opt_n;
+    }
+    if (opt_e)
+	bpb.rde = opt_e;
+    if (mflag) {
+	if (opt_m < 0xf0)
+	    errx(1, "illegal media descriptor (%#x)", opt_m);
+	bpb.mid = opt_m;
+    }
+    if (opt_a)
+	bpb.bspf = opt_a;
+    if (opt_i)
+	bpb.infs = opt_i;
+    if (opt_k)
+	bpb.bkbs = opt_k;
+    bss = 1;
+    bname = NULL;
+    fd1 = -1;
+    if (opt_B) {
+	bname = opt_B;
+	if (!strchr(bname, '/')) {
+	    snprintf(buf, sizeof(buf), "/boot/%s", bname);
+	    if (!(bname = strdup(buf)))
+		err(1, NULL);
+	}
+	if ((fd1 = open(bname, O_RDONLY)) == -1 || fstat(fd1, &sb))
+	    err(1, "%s", bname);
+	if (!S_ISREG(sb.st_mode) || sb.st_size % bpb.bps ||
+	    sb.st_size < bpb.bps || sb.st_size > bpb.bps * MAXU16)
+	    errx(1, "%s: inappropriate file type or format", bname);
+	bss = sb.st_size / bpb.bps;
+    }
+    if (!bpb.nft)
+	bpb.nft = 2;
+    if (!fat) {
+	if (bpb.bsec < (bpb.res ? bpb.res : bss) +
+	    howmany((RESFTE + (bpb.spc ? MINCLS16 : MAXCLS12 + 1)) *
+		    ((bpb.spc ? 16 : 12) / BPN), bpb.bps * NPB) *
+	    bpb.nft +
+	    howmany(bpb.rde ? bpb.rde : DEFRDE,
+		    bpb.bps / sizeof(struct de)) +
+	    (bpb.spc ? MINCLS16 : MAXCLS12 + 1) *
+	    (bpb.spc ? bpb.spc : howmany(DEFBLK, bpb.bps)))
+	    fat = 12;
+	else if (bpb.rde || bpb.bsec <
+		 (bpb.res ? bpb.res : bss) +
+		 howmany((RESFTE + MAXCLS16) * 2, bpb.bps) * bpb.nft +
+		 howmany(DEFRDE, bpb.bps / sizeof(struct de)) +
+		 (MAXCLS16 + 1) *
+		 (bpb.spc ? bpb.spc : howmany(8192, bpb.bps)))
+	    fat = 16;
+	else
+	    fat = 32;
+    }
+    x = bss;
+    if (fat == 32) {
+	if (!bpb.infs) {
+	    if (x == MAXU16 || x == bpb.bkbs)
+		errx(1, "no room for info sector");
+	    bpb.infs = x;
+	}
+	if (bpb.infs != MAXU16 && x <= bpb.infs)
+	    x = bpb.infs + 1;
+	if (!bpb.bkbs) {
+	    if (x == MAXU16)
+		errx(1, "no room for backup sector");
+	    bpb.bkbs = x;
+	} else if (bpb.bkbs != MAXU16 && bpb.bkbs == bpb.infs)
+	    errx(1, "backup sector would overwrite info sector");
+	if (bpb.bkbs != MAXU16 && x <= bpb.bkbs)
+	    x = bpb.bkbs + 1;
+    }
+    if (!bpb.res)
+	bpb.res = fat == 32 ? MAX(x, MAX(16384 / bpb.bps, 4)) : x;
+    else if (bpb.res < x)
+	errx(1, "too few reserved sectors");
+    if (fat != 32 && !bpb.rde)
+	bpb.rde = DEFRDE;
+    rds = howmany(bpb.rde, bpb.bps / sizeof(struct de));
+    if (!bpb.spc)
+	for (bpb.spc = howmany(fat == 16 ? DEFBLK16 : DEFBLK, bpb.bps);
+	     bpb.spc < MAXSPC &&
+	     bpb.res +
+	     howmany((RESFTE + maxcls(fat)) * (fat / BPN),
+		     bpb.bps * NPB) * bpb.nft +
+	     rds +
+	     (u_int64_t)(maxcls(fat) + 1) * bpb.spc <= bpb.bsec;
+	     bpb.spc <<= 1);
+    if (fat != 32 && bpb.bspf > MAXU16)
+	errx(1, "too many sectors/FAT for FAT12/16");
+    x1 = bpb.res + rds;
+    x = bpb.bspf ? bpb.bspf : 1;
+    if (x1 + (u_int64_t)x * bpb.nft > bpb.bsec)
+	errx(1, "meta data exceeds file system size");
+    x1 += x * bpb.nft;
+    x = (u_int64_t)(bpb.bsec - x1) * bpb.bps * NPB /
+	(bpb.spc * bpb.bps * NPB + fat / BPN * bpb.nft);
+    x2 = howmany((RESFTE + MIN(x, maxcls(fat))) * (fat / BPN),
+		 bpb.bps * NPB);
+    if (!bpb.bspf) {
+	bpb.bspf = x2;
+	x1 += (bpb.bspf - 1) * bpb.nft;
+    }
+    cls = (bpb.bsec - x1) / bpb.spc;
+    x = (u_int64_t)bpb.bspf * bpb.bps * NPB / (fat / BPN) - RESFTE;
+    if (cls > x)
+	cls = x;
+    if (bpb.bspf < x2)
+	warnx("warning: sectors/FAT limits file system to %u clusters",
+	      cls);
+    if (cls < mincls(fat))
+	errx(1, "%u clusters too few clusters for FAT%u, need %u", cls, fat,
+	    mincls(fat));
+    if (cls > maxcls(fat)) {
+	cls = maxcls(fat);
+	bpb.bsec = x1 + (cls + 1) * bpb.spc - 1;
+	warnx("warning: FAT type limits file system to %u sectors",
+	      bpb.bsec);
+    }
+    printf("%s: %u sector%s in %u FAT%u cluster%s "
+	   "(%u bytes/cluster)\n", fname, cls * bpb.spc,
+	   cls * bpb.spc == 1 ? "" : "s", cls, fat,
+	   cls == 1 ? "" : "s", bpb.bps * bpb.spc);
+    if (!bpb.mid)
+	bpb.mid = !bpb.hid ? 0xf0 : 0xf8;
+    if (fat == 32)
+	bpb.rdcl = RESFTE;
+    if (bpb.hid + bpb.bsec <= MAXU16) {
+	bpb.sec = bpb.bsec;
+	bpb.bsec = 0;
+    }
+    if (fat != 32) {
+	bpb.spf = bpb.bspf;
+	bpb.bspf = 0;
+    }
+    print_bpb(&bpb);
+    if (!opt_N) {
+	gettimeofday(&tv, NULL);
+	now = tv.tv_sec;
+	tm = localtime(&now);
+	if (!(img = malloc(bpb.bps)))
+	    err(1, NULL);
+	dir = bpb.res + (bpb.spf ? bpb.spf : bpb.bspf) * bpb.nft;
+	for (lsn = 0; lsn < dir + (fat == 32 ? bpb.spc : rds); lsn++) {
+	    x = lsn;
+	    if (opt_B &&
+		fat == 32 && bpb.bkbs != MAXU16 &&
+		bss <= bpb.bkbs && x >= bpb.bkbs) {
+		x -= bpb.bkbs;
+		if (!x && lseek(fd1, opt_ofs, SEEK_SET))
+		    err(1, "%s", bname);
+	    }
+	    if (opt_B && x < bss) {
+		if ((n = read(fd1, img, bpb.bps)) == -1)
+		    err(1, "%s", bname);
+		if ((unsigned)n != bpb.bps)
+		    errx(1, "%s: can't read sector %u", bname, x);
+	    } else
+		memset(img, 0, bpb.bps);
+	    if (!lsn ||
+	      (fat == 32 && bpb.bkbs != MAXU16 && lsn == bpb.bkbs)) {
+		x1 = sizeof(struct bs);
+		bsbpb = (struct bsbpb *)(img + x1);
+		mk2(bsbpb->bps, bpb.bps);
+		mk1(bsbpb->spc, bpb.spc);
+		mk2(bsbpb->res, bpb.res);
+		mk1(bsbpb->nft, bpb.nft);
+		mk2(bsbpb->rde, bpb.rde);
+		mk2(bsbpb->sec, bpb.sec);
+		mk1(bsbpb->mid, bpb.mid);
+		mk2(bsbpb->spf, bpb.spf);
+		mk2(bsbpb->spt, bpb.spt);
+		mk2(bsbpb->hds, bpb.hds);
+		mk4(bsbpb->hid, bpb.hid);
+		mk4(bsbpb->bsec, bpb.bsec);
+		x1 += sizeof(struct bsbpb);
+		if (fat == 32) {
+		    bsxbpb = (struct bsxbpb *)(img + x1);
+		    mk4(bsxbpb->bspf, bpb.bspf);
+		    mk2(bsxbpb->xflg, 0);
+		    mk2(bsxbpb->vers, 0);
+		    mk4(bsxbpb->rdcl, bpb.rdcl);
+		    mk2(bsxbpb->infs, bpb.infs);
+		    mk2(bsxbpb->bkbs, bpb.bkbs);
+		    x1 += sizeof(struct bsxbpb);
+		}
+		bsx = (struct bsx *)(img + x1);
+		mk1(bsx->sig, 0x29);
+		if (Iflag)
+		    x = opt_I;
+		else
+		    x = (((u_int)(1 + tm->tm_mon) << 8 |
+			  (u_int)tm->tm_mday) +
+			 ((u_int)tm->tm_sec << 8 |
+			  (u_int)(tv.tv_usec / 10))) << 16 |
+			((u_int)(1900 + tm->tm_year) +
+			 ((u_int)tm->tm_hour << 8 |
+			  (u_int)tm->tm_min));
+		mk4(bsx->volid, x);
+		mklabel(bsx->label, opt_L ? opt_L : "NO NAME");
+		sprintf(buf, "FAT%u", fat);
+		setstr(bsx->type, buf, sizeof(bsx->type));
+		if (!opt_B) {
+		    x1 += sizeof(struct bsx);
+		    bs = (struct bs *)img;
+		    mk1(bs->jmp[0], 0xeb);
+		    mk1(bs->jmp[1], x1 - 2);
+		    mk1(bs->jmp[2], 0x90);
+		    setstr(bs->oem, opt_O ? opt_O : "BSD  4.4",
+			   sizeof(bs->oem));
+		    memcpy(img + x1, bootcode, sizeof(bootcode));
+		    mk2(img + MINBPS - 2, DOSMAGIC);
+		}
+	    } else if (fat == 32 && bpb.infs != MAXU16 &&
+		       (lsn == bpb.infs ||
+			(bpb.bkbs != MAXU16 &&
+			 lsn == bpb.bkbs + bpb.infs))) {
+		mk4(img, 0x41615252);
+		mk4(img + MINBPS - 28, 0x61417272);
+		mk4(img + MINBPS - 24, 0xffffffff);
+		mk4(img + MINBPS - 20, bpb.rdcl);
+		mk2(img + MINBPS - 2, DOSMAGIC);
+	    } else if (lsn >= bpb.res && lsn < dir &&
+		       !((lsn - bpb.res) %
+			 (bpb.spf ? bpb.spf : bpb.bspf))) {
+		mk1(img[0], bpb.mid);
+		for (x = 1; x < fat * (fat == 32 ? 3 : 2) / 8; x++)
+		    mk1(img[x], fat == 32 && x % 4 == 3 ? 0x0f : 0xff);
+	    } else if (lsn == dir && opt_L) {
+		de = (struct de *)img;
+		mklabel(de->namext, opt_L);
+		mk1(de->attr, 050);
+		x = (u_int)tm->tm_hour << 11 |
+		    (u_int)tm->tm_min << 5 |
+		    (u_int)tm->tm_sec >> 1;
+		mk2(de->time, x);
+		x = (u_int)(tm->tm_year - 80) << 9 |
+		    (u_int)(tm->tm_mon + 1) << 5 |
+		    (u_int)tm->tm_mday;
+		mk2(de->date, x);
+	    }
+	    if ((n = write(fd, img, bpb.bps)) == -1)
+		err(1, "%s", fname);
+	    if ((unsigned)n != bpb.bps) {
+		errx(1, "%s: can't write sector %u", fname, lsn);
+                exit(1);
+            }
+	}
+    }
+    return 0;
+}
+
+/*
+ * Exit with error if file system is mounted.
+ */
+static void
+check_mounted(const char *fname, mode_t mode)
+{
+    struct statfs *mp;
+    const char *s1, *s2;
+    size_t len;
+    int n, r;
+
+#ifdef ANDROID
+    warnx("Skipping mount checks");
+#else
+    if (!(n = getmntinfo(&mp, MNT_NOWAIT)))
+	err(1, "getmntinfo");
+    len = strlen(_PATH_DEV);
+    s1 = fname;
+    if (!strncmp(s1, _PATH_DEV, len))
+	s1 += len;
+    r = S_ISCHR(mode) && s1 != fname && *s1 == 'r';
+    for (; n--; mp++) {
+	s2 = mp->f_mntfromname;
+	if (!strncmp(s2, _PATH_DEV, len))
+	    s2 += len;
+	if ((r && s2 != mp->f_mntfromname && !strcmp(s1 + 1, s2)) ||
+	    !strcmp(s1, s2))
+	    errx(1, "%s is mounted on %s", fname, mp->f_mntonname);
+    }
+#endif
+}
+
+/*
+ * Get a standard format.
+ */
+static void
+getstdfmt(const char *fmt, struct bpb *bpb)
+{
+    u_int x, i;
+
+    x = sizeof(stdfmt) / sizeof(stdfmt[0]);
+    for (i = 0; i < x && strcmp(fmt, stdfmt[i].name); i++);
+    if (i == x)
+	errx(1, "%s: unknown standard format", fmt);
+    *bpb = stdfmt[i].bpb;
+}
+
+/*
+ * Get disk slice, partition, and geometry information.
+ */
+
+#ifdef ANDROID
+static void
+getdiskinfo(int fd, const char *fname, const char *dtype, __unused int oflag,
+	    struct bpb *bpb)
+{
+    struct hd_geometry geom;
+
+    if (ioctl(fd, BLKSSZGET, &bpb->bps)) {
+        fprintf(stderr, "Error getting bytes / sector (%s)\n", strerror(errno));
+        exit(1);
+    }
+
+    ckgeom(fname, bpb->bps, "bytes/sector");
+
+    if (ioctl(fd, BLKGETSIZE, &bpb->bsec)) {
+        fprintf(stderr, "Error getting blocksize (%s)\n", strerror(errno));
+        exit(1);
+    }
+
+    if (ioctl(fd, HDIO_GETGEO, &geom)) {
+        fprintf(stderr, "Error getting gemoetry (%s) - trying sane values\n", strerror(errno));
+        geom.heads = 64;
+        geom.sectors = 63;
+    }
+
+    if (!geom.heads) {
+        printf("Bogus heads from kernel - setting sane value\n");
+        geom.heads = 64;
+    }
+
+    if (!geom.sectors) {
+        printf("Bogus sectors from kernel - setting sane value\n");
+        geom.sectors = 63;
+    }
+
+    bpb->spt = geom.sectors;
+    ckgeom(fname, bpb->spt, "sectors/track");
+
+    bpb->hds = geom.heads;
+    ckgeom(fname, bpb->hds, "drive heads");
+}
+
+#else
+
+static void
+getdiskinfo(int fd, const char *fname, const char *dtype, __unused int oflag,
+	    struct bpb *bpb)
+{
+    struct disklabel *lp, dlp;
+    struct fd_type type;
+    off_t ms, hs = 0;
+
+    lp = NULL;
+
+    /* If the user specified a disk type, try to use that */
+    if (dtype != NULL) {
+	lp = getdiskbyname(dtype);
+    }
+
+    /* Maybe it's a floppy drive */
+    if (lp == NULL) {
+	if (ioctl(fd, DIOCGMEDIASIZE, &ms) == -1) {
+	    struct stat st;
+
+	    if (fstat(fd, &st))
+		err(1, "Cannot get disk size");
+	    /* create a fake geometry for a file image */
+	    ms = st.st_size;
+	    dlp.d_secsize = 512;
+	    dlp.d_nsectors = 63;
+	    dlp.d_ntracks = 255;
+	    dlp.d_secperunit = ms / dlp.d_secsize;
+	    lp = &dlp;
+	} else if (ioctl(fd, FD_GTYPE, &type) != -1) {
+	    dlp.d_secsize = 128 << type.secsize;
+	    dlp.d_nsectors = type.sectrac;
+	    dlp.d_ntracks = type.heads;
+	    dlp.d_secperunit = ms / dlp.d_secsize;
+	    lp = &dlp;
+	}
+    }
+
+    /* Maybe it's a fixed drive */
+    if (lp == NULL) {
+	if (ioctl(fd, DIOCGDINFO, &dlp) == -1) {
+	    if (bpb->bps == 0 && ioctl(fd, DIOCGSECTORSIZE, &dlp.d_secsize) == -1)
+		errx(1, "Cannot get sector size, %s", strerror(errno));
+
+	    /* XXX Should we use bpb->bps if it's set? */
+	    dlp.d_secperunit = ms / dlp.d_secsize;
+
+	    if (bpb->spt == 0 && ioctl(fd, DIOCGFWSECTORS, &dlp.d_nsectors) == -1) {
+		warnx("Cannot get number of sectors per track, %s", strerror(errno));
+		dlp.d_nsectors = 63;
+	    }
+	    if (bpb->hds == 0 && ioctl(fd, DIOCGFWHEADS, &dlp.d_ntracks) == -1) {
+		warnx("Cannot get number of heads, %s", strerror(errno));
+		if (dlp.d_secperunit <= 63*1*1024)
+		    dlp.d_ntracks = 1;
+		else if (dlp.d_secperunit <= 63*16*1024)
+		    dlp.d_ntracks = 16;
+		else
+		    dlp.d_ntracks = 255;
+	    }
+	}
+
+	hs = (ms / dlp.d_secsize) - dlp.d_secperunit;
+	lp = &dlp;
+    }
+
+    if (bpb->bps == 0)
+	bpb->bps = ckgeom(fname, lp->d_secsize, "bytes/sector");
+    if (bpb->spt == 0)
+	bpb->spt = ckgeom(fname, lp->d_nsectors, "sectors/track");
+    if (bpb->hds == 0)
+	bpb->hds = ckgeom(fname, lp->d_ntracks, "drive heads");
+    if (bpb->bsec == 0)
+	bpb->bsec = lp->d_secperunit;
+    if (bpb->hid == 0)
+	bpb->hid = hs;
+}
+#endif
+
+/*
+ * Print out BPB values.
+ */
+static void
+print_bpb(struct bpb *bpb)
+{
+    printf("bps=%u spc=%u res=%u nft=%u", bpb->bps, bpb->spc, bpb->res,
+	   bpb->nft);
+    if (bpb->rde)
+	printf(" rde=%u", bpb->rde);
+    if (bpb->sec)
+	printf(" sec=%u", bpb->sec);
+    printf(" mid=%#x", bpb->mid);
+    if (bpb->spf)
+	printf(" spf=%u", bpb->spf);
+    printf(" spt=%u hds=%u hid=%u", bpb->spt, bpb->hds, bpb->hid);
+    if (bpb->bsec)
+	printf(" bsec=%u", bpb->bsec);
+    if (!bpb->spf) {
+	printf(" bspf=%u rdcl=%u", bpb->bspf, bpb->rdcl);
+	printf(" infs=");
+	printf(bpb->infs == MAXU16 ? "%#x" : "%u", bpb->infs);
+	printf(" bkbs=");
+	printf(bpb->bkbs == MAXU16 ? "%#x" : "%u", bpb->bkbs);
+    }
+    printf("\n");
+}
+
+/*
+ * Check a disk geometry value.
+ */
+static u_int
+ckgeom(const char *fname, u_int val, const char *msg)
+{
+    if (!val)
+	errx(1, "%s: no default %s", fname, msg);
+    if (val > MAXU16)
+	errx(1, "%s: illegal %s %d", fname, msg, val);
+    return val;
+}
+
+/*
+ * Convert and check a numeric option argument.
+ */
+static u_int
+argtou(const char *arg, u_int lo, u_int hi, const char *msg)
+{
+    char *s;
+    u_long x;
+
+    errno = 0;
+    x = strtoul(arg, &s, 0);
+    if (errno || !*arg || *s || x < lo || x > hi)
+	errx(1, "%s: bad %s", arg, msg);
+    return x;
+}
+
+/*
+ * Same for off_t, with optional skmgpP suffix
+ */
+static off_t
+argtooff(const char *arg, const char *msg)
+{
+    char *s;
+    off_t x;
+
+    x = strtoll(arg, &s, 0);
+    /* allow at most one extra char */
+    if (errno || x < 0 || (s[0] && s[1]) )
+	errx(1, "%s: bad %s", arg, msg);
+    if (*s) {	/* the extra char is the multiplier */
+	switch (*s) {
+	default:
+	    errx(1, "%s: bad %s", arg, msg);
+	    /* notreached */
+	
+	case 's':	/* sector */
+	case 'S':
+	    x <<= 9;	/* times 512 */
+	    break;
+
+	case 'k':	/* kilobyte */
+	case 'K':
+	    x <<= 10;	/* times 1024 */
+	    break;
+
+	case 'm':	/* megabyte */
+	case 'M':
+	    x <<= 20;	/* times 1024*1024 */
+	    break;
+
+	case 'g':	/* gigabyte */
+	case 'G':
+	    x <<= 30;	/* times 1024*1024*1024 */
+	    break;
+
+	case 'p':	/* partition start */
+	case 'P':	/* partition start */
+	case 'l':	/* partition length */
+	case 'L':	/* partition length */
+	    errx(1, "%s: not supported yet %s", arg, msg);
+	    /* notreached */
+	}
+    }
+    return x;
+}
+
+/*
+ * Check a volume label.
+ */
+static int
+oklabel(const char *src)
+{
+    int c, i;
+
+    for (i = 0; i <= 11; i++) {
+	c = (u_char)*src++;
+	if (c < ' ' + !i || strchr("\"*+,./:;<=>?[\\]|", c))
+	    break;
+    }
+    return i && !c;
+}
+
+/*
+ * Make a volume label.
+ */
+static void
+mklabel(u_int8_t *dest, const char *src)
+{
+    int c, i;
+
+    for (i = 0; i < 11; i++) {
+	c = *src ? toupper(*src++) : ' ';
+	*dest++ = !i && c == '\xe5' ? 5 : c;
+    }
+}
+
+/*
+ * Copy string, padding with spaces.
+ */
+static void
+setstr(u_int8_t *dest, const char *src, size_t len)
+{
+    while (len--)
+	*dest++ = *src ? *src++ : ' ';
+}
+
+/*
+ * Print usage message.
+ */
+static void
+usage(void)
+{
+	fprintf(stderr,
+	    "usage: newfs_msdos [ -options ] special [disktype]\n"
+	    "where the options are:\n"
+	    "\t-@ create file system at specified offset\n"                         
+	    "\t-B get bootstrap from file\n"
+	    "\t-C create image file with specified size\n"
+	    "\t-F FAT type (12, 16, or 32)\n"
+	    "\t-I volume ID\n"
+	    "\t-L volume label\n"
+	    "\t-N don't create file system: just print out parameters\n"
+	    "\t-O OEM string\n"
+	    "\t-S bytes/sector\n"
+	    "\t-a sectors/FAT\n"
+	    "\t-b block size\n"
+	    "\t-c sectors/cluster\n"
+	    "\t-e root directory entries\n"
+	    "\t-f standard format\n"
+	    "\t-h drive heads\n"
+	    "\t-i file system info sector\n"
+	    "\t-k backup boot sector\n"
+	    "\t-m media descriptor\n"
+	    "\t-n number of FATs\n"
+	    "\t-o hidden sectors\n"
+	    "\t-r reserved sectors\n"
+	    "\t-s file system size (sectors)\n"
+	    "\t-u sectors/track\n");
+	exit(1);
+}
diff --git a/toolbox/notify.c b/toolbox/notify.c
new file mode 100644
index 0000000..c983ed5
--- /dev/null
+++ b/toolbox/notify.c
@@ -0,0 +1,145 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/inotify.h>
+#include <errno.h>
+
+int notify_main(int argc, char *argv[])
+{
+    int c;
+    int nfd, ffd;
+    int res;
+	char event_buf[512];
+    struct inotify_event *event;
+	int event_mask = IN_ALL_EVENTS;
+    int event_count = 1;
+	int print_files = 0;
+	int verbose = 2;
+	int width = 80;
+	char **file_names;
+	int file_count;
+	int id_offset = 0;
+	int i;
+	char *buf;
+
+    do {
+        c = getopt(argc, argv, "m:c:pv:w:");
+        if (c == EOF)
+            break;
+        switch (c) {
+        case 'm':
+            event_mask = strtol(optarg, NULL, 0);
+            break;
+        case 'c':
+            event_count = atoi(optarg);
+            break;
+		case 'p':
+			print_files = 1;
+			break;
+        case 'v':
+            verbose = atoi(optarg);
+            break;
+        case 'w':
+            width = atoi(optarg);
+            break;
+        case '?':
+            fprintf(stderr, "%s: invalid option -%c\n",
+                argv[0], optopt);
+            exit(1);
+        }
+    } while (1);
+
+    if (argc <= optind) {
+        fprintf(stderr, "Usage: %s [-m eventmask] [-c count] [-p] [-v verbosity] path [path ...]\n", argv[0]);
+		return 1;
+    }
+
+    nfd = inotify_init();
+    if(nfd < 0) {
+        fprintf(stderr, "inotify_init failed, %s\n", strerror(errno));
+        return 1;
+    }
+	file_names = argv + optind;
+	file_count = argc - optind;
+	for(i = 0; i < file_count; i++) {
+		res = inotify_add_watch(nfd, file_names[i], event_mask);
+		if(res < 0) {
+	        fprintf(stderr, "inotify_add_watch failed for %s, %s\n", file_names[i], strerror(errno));
+			return 1;
+		}
+		if(i == 0)
+			id_offset = -res;
+		if(res + id_offset != i) {
+			fprintf(stderr, "%s got unexpected id %d instead of %d\n", file_names[i], res, i);
+			return 1;
+		}
+	}
+
+	buf = malloc(width + 2);
+    
+    while(1) {
+		int event_pos = 0;
+        res = read(nfd, event_buf, sizeof(event_buf));
+        if(res < (int)sizeof(*event)) {
+			if(errno == EINTR)
+				continue;
+            fprintf(stderr, "could not get event, %s\n", strerror(errno));
+            return 1;
+        }
+		//printf("got %d bytes of event information\n", res);
+		while(res >= (int)sizeof(*event)) {
+			int event_size;
+			event = (struct inotify_event *)(event_buf + event_pos);
+			if(verbose >= 2)
+		        printf("%s: %08x %08x \"%s\"\n", file_names[event->wd + id_offset], event->mask, event->cookie, event->len ? event->name : "");
+			else if(verbose >= 2)
+		        printf("%s: %08x \"%s\"\n", file_names[event->wd + id_offset], event->mask, event->len ? event->name : "");
+			else if(verbose >= 1)
+		        printf("%d: %08x \"%s\"\n", event->wd, event->mask, event->len ? event->name : "");
+			if(print_files && (event->mask & IN_MODIFY)) {
+				char filename[512];
+				ssize_t read_len;
+				char *display_name;
+				int buflen;
+				strcpy(filename, file_names[event->wd + id_offset]);
+				if(event->len) {
+					strcat(filename, "/");
+					strcat(filename, event->name);
+				}
+				ffd = open(filename, O_RDONLY);
+				display_name = (verbose >= 2 || event->len == 0) ? filename : event->name;
+				buflen = width - strlen(display_name);
+				read_len = read(ffd, buf, buflen);
+				if(read_len > 0) {
+					if(read_len < buflen && buf[read_len-1] != '\n') {
+						buf[read_len] = '\n';
+						read_len++;
+					}
+					if(read_len == buflen) {
+						buf[--read_len] = '\0';
+						buf[--read_len] = '\n';
+						buf[--read_len] = '.';
+						buf[--read_len] = '.';
+						buf[--read_len] = '.';
+					}
+					else {
+						buf[read_len] = '\0';
+					}
+					printf("%s: %s", display_name, buf);
+				}
+				close(ffd);
+			}
+	        if(event_count && --event_count == 0)
+	            return 0;
+			event_size = sizeof(*event) + event->len;
+			res -= event_size;
+			event_pos += event_size;
+		}
+    }
+
+    return 0;
+}
diff --git a/toolbox/powerd.c b/toolbox/powerd.c
new file mode 100644
index 0000000..1f29a8b
--- /dev/null
+++ b/toolbox/powerd.c
@@ -0,0 +1,441 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/select.h>
+#include <sys/inotify.h>
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
+//#include <linux/input.h> // this does not compile
+
+// from <linux/input.h>
+
+struct input_event {
+	struct timeval time;
+	__u16 type;
+	__u16 code;
+	__s32 value;
+};
+
+#define EVIOCGVERSION		_IOR('E', 0x01, int)			/* get driver version */
+#define EVIOCGID		_IOR('E', 0x02, struct input_id)	/* get device ID */
+#define EVIOCGKEYCODE		_IOR('E', 0x04, int[2])			/* get keycode */
+#define EVIOCSKEYCODE		_IOW('E', 0x04, int[2])			/* set keycode */
+
+#define EVIOCGNAME(len)		_IOC(_IOC_READ, 'E', 0x06, len)		/* get device name */
+#define EVIOCGPHYS(len)		_IOC(_IOC_READ, 'E', 0x07, len)		/* get physical location */
+#define EVIOCGUNIQ(len)		_IOC(_IOC_READ, 'E', 0x08, len)		/* get unique identifier */
+
+#define EVIOCGKEY(len)		_IOC(_IOC_READ, 'E', 0x18, len)		/* get global keystate */
+#define EVIOCGLED(len)		_IOC(_IOC_READ, 'E', 0x19, len)		/* get all LEDs */
+#define EVIOCGSND(len)		_IOC(_IOC_READ, 'E', 0x1a, len)		/* get all sounds status */
+#define EVIOCGSW(len)		_IOC(_IOC_READ, 'E', 0x1b, len)		/* get all switch states */
+
+#define EVIOCGBIT(ev,len)	_IOC(_IOC_READ, 'E', 0x20 + ev, len)	/* get event bits */
+#define EVIOCGABS(abs)		_IOR('E', 0x40 + abs, struct input_absinfo)		/* get abs value/limits */
+#define EVIOCSABS(abs)		_IOW('E', 0xc0 + abs, struct input_absinfo)		/* set abs value/limits */
+
+#define EVIOCSFF		_IOC(_IOC_WRITE, 'E', 0x80, sizeof(struct ff_effect))	/* send a force effect to a force feedback device */
+#define EVIOCRMFF		_IOW('E', 0x81, int)			/* Erase a force effect */
+#define EVIOCGEFFECTS		_IOR('E', 0x84, int)			/* Report number of effects playable at the same time */
+
+#define EVIOCGRAB		_IOW('E', 0x90, int)			/* Grab/Release device */
+
+/*
+ * Event types
+ */
+
+#define EV_SYN			0x00
+#define EV_KEY			0x01
+#define EV_REL			0x02
+#define EV_ABS			0x03
+#define EV_MSC			0x04
+#define EV_SW			0x05
+#define EV_LED			0x11
+#define EV_SND			0x12
+#define EV_REP			0x14
+#define EV_FF			0x15
+#define EV_PWR			0x16
+#define EV_FF_STATUS		0x17
+#define EV_MAX			0x1f
+
+#define KEY_POWER		116
+#define KEY_SLEEP		142
+#define SW_0		0x00
+
+// end <linux/input.h>
+
+struct notify_entry {
+    int id;
+    int (*handler)(struct notify_entry *entry, struct inotify_event *event);
+    const char *filename;
+};
+
+int charging_state_notify_handler(struct notify_entry *entry, struct inotify_event *event)
+{
+    static int state = -1;
+    int last_state;
+    char buf[40];
+    int read_len;
+    int fd;
+
+    last_state = state;
+    fd = open(entry->filename, O_RDONLY);
+    read_len = read(fd, buf, sizeof(buf));
+    if(read_len > 0) {
+        //printf("charging_state_notify_handler: \"%s\"\n", buf);
+        state = !(strncmp(buf, "Unknown", 7) == 0 
+                  || strncmp(buf, "Discharging", 11) == 0);
+    }
+    close(fd);
+    //printf("charging_state_notify_handler: %d -> %d\n", last_state, state);
+    return state > last_state;
+}
+
+struct notify_entry watched_files[] = {
+    {
+        .filename = "/sys/android_power/charging_state",
+        .handler = charging_state_notify_handler
+    }
+};
+
+int call_notify_handler(struct inotify_event *event)
+{
+    unsigned int start, i;
+    start = event->wd - watched_files[0].id;
+    if(start >= ARRAY_SIZE(watched_files))
+        start = 0;
+    //printf("%d: %08x \"%s\"\n", event->wd, event->mask, event->len ? event->name : "");
+    for(i = start; i < ARRAY_SIZE(watched_files); i++) {
+        if(event->wd == watched_files[i].id) {
+            if(watched_files[i].handler) {
+                return watched_files[i].handler(&watched_files[i], event);
+            }
+            return 1;
+        }
+    }
+    for(i = 0; i < start; i++) {
+        if(event->wd == watched_files[i].id) {
+            if(watched_files[i].handler) {
+                return watched_files[i].handler(&watched_files[i], event);
+            }
+            return 1;
+        }
+    }
+    return 0;
+}
+
+int handle_inotify_event(int nfd)
+{
+    int res;
+    int wake_up = 0;
+    struct inotify_event *event;
+    char event_buf[512];
+    int event_pos = 0;
+
+    res = read(nfd, event_buf, sizeof(event_buf));
+    if(res < (int)sizeof(*event)) {
+        if(errno == EINTR)
+            return 0;
+        fprintf(stderr, "could not get event, %s\n", strerror(errno));
+        return 0;
+    }
+    printf("got %d bytes of event information\n", res);
+    while(res >= (int)sizeof(*event)) {
+        int event_size;
+        event = (struct inotify_event *)(event_buf + event_pos);
+        wake_up |= call_notify_handler(event);
+        event_size = sizeof(*event) + event->len;
+        res -= event_size;
+        event_pos += event_size;
+    }
+    return wake_up;
+}
+
+int powerd_main(int argc, char *argv[])
+{
+    int c;
+    unsigned int i;
+    int res;
+    struct timeval tv;
+    int eventfd;
+    int notifyfd;
+    int powerfd;
+    int powerfd_is_sleep;
+    int user_activity_fd;
+    int acquire_partial_wake_lock_fd;
+    int acquire_full_wake_lock_fd;
+    int release_wake_lock_fd;
+    char *eventdev = "/dev/input/event0";
+    const char *android_sleepdev = "/sys/android_power/request_sleep";
+    const char *android_autooff_dev = "/sys/android_power/auto_off_timeout";
+    const char *android_user_activity_dev = "/sys/android_power/last_user_activity";
+    const char *android_acquire_partial_wake_lock_dev = "/sys/android_power/acquire_partial_wake_lock";
+    const char *android_acquire_full_wake_lock_dev = "/sys/android_power/acquire_full_wake_lock";
+    const char *android_release_wake_lock_dev = "/sys/android_power/release_wake_lock";
+    const char *powerdev = "/sys/power/state";
+    const char suspendstring[] = "standby";
+    const char wakelockstring[] = "powerd";
+    fd_set rfds;
+    struct input_event event;
+    struct input_event light_event;
+    struct input_event light_event2;
+    int gotkey = 1;
+    time_t idle_time = 5;
+    const char *idle_time_string = "5";
+    time_t lcd_light_time = 0;
+    time_t key_light_time = 0;
+    int verbose = 1;
+    int event_sleep = 0;
+    int got_power_key_down = 0;
+    struct timeval power_key_down_time = { 0, 0 };
+
+    light_event.type = EV_LED;
+    light_event.code = 4; // bright lcd backlight
+    light_event.value = 0; // light off -- sleep after timeout
+
+    light_event2.type = EV_LED;
+    light_event2.code = 8; // keyboard backlight
+    light_event2.value = 0; // light off -- sleep after timeout
+
+    do {
+        c = getopt(argc, argv, "e:ni:vql:k:");
+        if (c == EOF)
+            break;
+        switch (c) {
+        case 'e':
+            eventdev = optarg;
+            break;
+        case 'n':
+            gotkey = 0;
+            break;
+        case 'i':
+            idle_time = atoi(optarg);
+            idle_time_string = optarg;
+            break;
+        case 'v':
+            verbose = 2;
+            break;
+        case 'q':
+            verbose = 0;
+            break;
+        case 'l':
+            lcd_light_time = atoi(optarg);
+            break;
+        case 'k':
+            key_light_time = atoi(optarg);
+            break;
+        case '?':
+            fprintf(stderr, "%s: invalid option -%c\n",
+                argv[0], optopt);
+            exit(1);
+        }
+    } while (1);
+    if(optind  != argc) {
+        fprintf(stderr,"%s [-e eventdev]\n", argv[0]);
+        return 1;
+    }
+
+    eventfd = open(eventdev, O_RDWR | O_NONBLOCK);
+    if(eventfd < 0) {
+        fprintf(stderr, "could not open %s, %s\n", eventdev, strerror(errno));
+        return 1;
+    }
+    if(key_light_time >= lcd_light_time) {
+        lcd_light_time = key_light_time + 1;
+        fprintf(stderr,"lcd bright backlight time must be longer than keyboard backlight time.\n"
+            "Setting lcd bright backlight time to %ld seconds\n", lcd_light_time);
+    }
+
+    user_activity_fd = open(android_user_activity_dev, O_RDWR);
+    if(user_activity_fd >= 0) {
+        int auto_off_fd = open(android_autooff_dev, O_RDWR);
+        write(auto_off_fd, idle_time_string, strlen(idle_time_string));
+        close(auto_off_fd);
+    }
+
+    powerfd = open(android_sleepdev, O_RDWR);
+    if(powerfd >= 0) {
+        powerfd_is_sleep = 1;
+        if(verbose > 0)
+            printf("Using android sleep dev: %s\n", android_sleepdev);
+    }
+    else {
+        powerfd_is_sleep = 0;
+        powerfd = open(powerdev, O_RDWR);
+        if(powerfd >= 0) {
+            if(verbose > 0)
+                printf("Using linux power dev: %s\n", powerdev);
+        }
+    }
+    if(powerfd < 0) {
+        fprintf(stderr, "could not open %s, %s\n", powerdev, strerror(errno));
+        return 1;
+    }
+
+    notifyfd = inotify_init();
+    if(notifyfd < 0) {
+        fprintf(stderr, "inotify_init failed, %s\n", strerror(errno));
+        return 1;
+    }
+    fcntl(notifyfd, F_SETFL, O_NONBLOCK | fcntl(notifyfd, F_GETFL));
+    for(i = 0; i < ARRAY_SIZE(watched_files); i++) {
+        watched_files[i].id = inotify_add_watch(notifyfd, watched_files[i].filename, IN_MODIFY);
+        printf("Watching %s, id %d\n", watched_files[i].filename, watched_files[i].id);
+    }
+
+    acquire_partial_wake_lock_fd = open(android_acquire_partial_wake_lock_dev, O_RDWR);
+    acquire_full_wake_lock_fd = open(android_acquire_full_wake_lock_dev, O_RDWR);
+    release_wake_lock_fd = open(android_release_wake_lock_dev, O_RDWR);
+
+    if(user_activity_fd >= 0) {
+        idle_time = 60*60*24; // driver handles real timeout
+    }
+    if(gotkey) {
+        tv.tv_sec = idle_time;
+        tv.tv_usec = 0;
+    }
+    else {
+        tv.tv_sec = 0;
+        tv.tv_usec = 500000;
+    }
+    
+    while(1) {
+        FD_ZERO(&rfds);
+        //FD_SET(0, &rfds);
+        FD_SET(eventfd, &rfds);
+        FD_SET(notifyfd, &rfds);
+        res = select(((notifyfd > eventfd) ? notifyfd : eventfd) + 1, &rfds, NULL, NULL, &tv);
+        if(res < 0) {
+            fprintf(stderr, "select failed, %s\n", strerror(errno));
+            return 1;
+        }
+        if(res == 0) {
+            if(light_event2.value == 1)
+                goto light2_off;
+            if(light_event.value == 1)
+                goto light_off;
+            if(user_activity_fd < 0) {
+                if(gotkey && verbose > 0)
+                    printf("Idle - sleep\n");
+                if(!gotkey && verbose > 1)
+                    printf("Reenter sleep\n");
+                goto sleep;
+            }
+            else {
+                tv.tv_sec = 60*60*24;
+                tv.tv_usec = 0;
+            }
+        }
+        if(res > 0) {
+            //if(FD_ISSET(0, &rfds)) {
+            //  printf("goto data on stdin quit\n");
+            //  return 0;
+            //}
+            if(FD_ISSET(notifyfd, &rfds)) {
+                write(acquire_partial_wake_lock_fd, wakelockstring, sizeof(wakelockstring) - 1);
+                if(handle_inotify_event(notifyfd) > 0) {
+                    write(acquire_full_wake_lock_fd, wakelockstring, sizeof(wakelockstring) - 1);
+                }
+                write(release_wake_lock_fd, wakelockstring, sizeof(wakelockstring) - 1);
+            }
+            if(FD_ISSET(eventfd, &rfds)) {
+                write(acquire_partial_wake_lock_fd, wakelockstring, sizeof(wakelockstring) - 1);
+                res = read(eventfd, &event, sizeof(event));
+                if(res < (int)sizeof(event)) {
+                    fprintf(stderr, "could not get event\n");
+                    write(release_wake_lock_fd, wakelockstring, sizeof(wakelockstring) - 1);
+                    return 1;
+                }
+                if(event.type == EV_PWR && event.code == KEY_SLEEP) {
+                    event_sleep = event.value;
+                }
+                if(event.type == EV_KEY || (event.type == EV_SW && event.code == SW_0 && event.value == 1)) {
+                    gotkey = 1;
+                    if(user_activity_fd >= 0) {
+                        char buf[32];
+                        int len;
+                        len = sprintf(buf, "%ld%06lu000", event.time.tv_sec, event.time.tv_usec);
+                        write(user_activity_fd, buf, len);
+                    }
+                    if(lcd_light_time | key_light_time) {
+                        tv.tv_sec = key_light_time;
+                        light_event.value = 1;
+                        write(eventfd, &light_event, sizeof(light_event));
+                        light_event2.value = 1;
+                        write(eventfd, &light_event2, sizeof(light_event2));
+                    }
+                    else {
+                        tv.tv_sec = idle_time;
+                    }
+                    tv.tv_usec = 0;
+                    if(verbose > 1)
+                        printf("got %s %s %d%s\n", event.type == EV_KEY ? "key" : "switch", event.value ? "down" : "up", event.code, event_sleep ? " from sleep" : "");
+                    if(event.code == KEY_POWER) {
+                        if(event.value == 0) {
+                            int tmp_got_power_key_down = got_power_key_down;
+                            got_power_key_down = 0;
+                            if(tmp_got_power_key_down) {
+                                // power key released
+                                if(verbose > 0)
+                                    printf("Power key released - sleep\n");
+                                write(release_wake_lock_fd, wakelockstring, sizeof(wakelockstring) - 1);
+                                goto sleep;
+                            }
+                        }
+                        else if(event_sleep == 0) {
+                            got_power_key_down = 1;
+                            power_key_down_time = event.time;
+                        }
+                    }
+                }
+                if(event.type == EV_SW && event.code == SW_0 && event.value == 0) {
+                    if(verbose > 0)
+                        printf("Flip closed - sleep\n");
+                    power_key_down_time = event.time;
+                    write(release_wake_lock_fd, wakelockstring, sizeof(wakelockstring) - 1);
+                    goto sleep;
+                }
+                write(release_wake_lock_fd, wakelockstring, sizeof(wakelockstring) - 1);
+            }
+        }
+        if(0) {
+light_off:
+            light_event.value = 0;
+            write(eventfd, &light_event, sizeof(light_event));
+            tv.tv_sec = idle_time - lcd_light_time;
+        }
+        if(0) {
+light2_off:
+            light_event2.value = 0;
+            write(eventfd, &light_event2, sizeof(light_event2));
+            tv.tv_sec = lcd_light_time - key_light_time;
+        }
+        if(0) {
+sleep:
+            if(light_event.value == 1) {
+                light_event.value = 0;
+                write(eventfd, &light_event, sizeof(light_event));
+                light_event2.value = 0;
+                write(eventfd, &light_event2, sizeof(light_event2));
+                tv.tv_sec = idle_time - lcd_light_time;
+            }
+            if(powerfd_is_sleep) {
+                char buf[32];
+                int len;
+                len = sprintf(buf, "%ld%06lu000", power_key_down_time.tv_sec, power_key_down_time.tv_usec);
+                write(powerfd, buf, len);
+            }
+            else
+                write(powerfd, suspendstring, sizeof(suspendstring) - 1);
+            gotkey = 0;
+            tv.tv_sec = 0;
+            tv.tv_usec = 500000;
+        }
+    }
+
+    return 0;
+}
diff --git a/toolbox/printenv.c b/toolbox/printenv.c
new file mode 100644
index 0000000..d5ea531
--- /dev/null
+++ b/toolbox/printenv.c
@@ -0,0 +1,29 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+extern char** environ;
+
+int printenv_main (int argc, char **argv)
+{
+    char** e;
+    char* v;
+    int i;
+   
+    if (argc == 1) {
+        e = environ;
+        while (*e) {
+            printf("%s\n", *e);
+            e++;
+        }
+    } else {
+        for (i=1; i<argc; i++) {
+            v = getenv(argv[i]);
+            if (v) {
+                printf("%s\n", v);
+            }
+        }
+    }
+
+    return 0;
+}
+
diff --git a/toolbox/ps.c b/toolbox/ps.c
new file mode 100644
index 0000000..7c3de4a
--- /dev/null
+++ b/toolbox/ps.c
@@ -0,0 +1,262 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <fcntl.h>
+
+#include <string.h>
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <dirent.h>
+
+#include <pwd.h>
+
+#include <cutils/sched_policy.h>
+
+static char *nexttoksep(char **strp, char *sep)
+{
+    char *p = strsep(strp,sep);
+    return (p == 0) ? "" : p;
+}
+static char *nexttok(char **strp)
+{
+    return nexttoksep(strp, " ");
+}
+
+#define SHOW_PRIO 1
+#define SHOW_TIME 2
+#define SHOW_POLICY 4
+#define SHOW_CPU  8
+#define SHOW_MACLABEL 16
+
+static int display_flags = 0;
+
+static int ps_line(int pid, int tid, char *namefilter)
+{
+    char statline[1024];
+    char cmdline[1024];
+    char macline[1024];
+    char user[32];
+    struct stat stats;
+    int fd, r;
+    char *ptr, *name, *state;
+    int ppid, tty;
+    unsigned wchan, rss, vss, eip;
+    unsigned utime, stime;
+    int prio, nice, rtprio, sched, psr;
+    struct passwd *pw;
+    
+    sprintf(statline, "/proc/%d", pid);
+    stat(statline, &stats);
+
+    if(tid) {
+        sprintf(statline, "/proc/%d/task/%d/stat", pid, tid);
+        cmdline[0] = 0;
+        snprintf(macline, sizeof(macline), "/proc/%d/task/%d/attr/current", pid, tid);
+    } else {
+        sprintf(statline, "/proc/%d/stat", pid);
+        sprintf(cmdline, "/proc/%d/cmdline", pid);
+        snprintf(macline, sizeof(macline), "/proc/%d/attr/current", pid);
+        fd = open(cmdline, O_RDONLY);
+        if(fd == 0) {
+            r = 0;
+        } else {
+            r = read(fd, cmdline, 1023);
+            close(fd);
+            if(r < 0) r = 0;
+        }
+        cmdline[r] = 0;
+    }
+    
+    fd = open(statline, O_RDONLY);
+    if(fd == 0) return -1;
+    r = read(fd, statline, 1023);
+    close(fd);
+    if(r < 0) return -1;
+    statline[r] = 0;
+
+    ptr = statline;
+    nexttok(&ptr); // skip pid
+    ptr++;          // skip "("
+
+    name = ptr;
+    ptr = strrchr(ptr, ')'); // Skip to *last* occurence of ')',
+    *ptr++ = '\0';           // and null-terminate name.
+
+    ptr++;          // skip " "
+    state = nexttok(&ptr);
+    ppid = atoi(nexttok(&ptr));
+    nexttok(&ptr); // pgrp
+    nexttok(&ptr); // sid
+    tty = atoi(nexttok(&ptr));
+    
+    nexttok(&ptr); // tpgid
+    nexttok(&ptr); // flags
+    nexttok(&ptr); // minflt
+    nexttok(&ptr); // cminflt
+    nexttok(&ptr); // majflt
+    nexttok(&ptr); // cmajflt
+#if 1
+    utime = atoi(nexttok(&ptr));
+    stime = atoi(nexttok(&ptr));
+#else
+    nexttok(&ptr); // utime
+    nexttok(&ptr); // stime
+#endif
+    nexttok(&ptr); // cutime
+    nexttok(&ptr); // cstime
+    prio = atoi(nexttok(&ptr));
+    nice = atoi(nexttok(&ptr));
+    nexttok(&ptr); // threads
+    nexttok(&ptr); // itrealvalue
+    nexttok(&ptr); // starttime
+    vss = strtoul(nexttok(&ptr), 0, 10); // vsize
+    rss = strtoul(nexttok(&ptr), 0, 10); // rss
+    nexttok(&ptr); // rlim
+    nexttok(&ptr); // startcode
+    nexttok(&ptr); // endcode
+    nexttok(&ptr); // startstack
+    nexttok(&ptr); // kstkesp
+    eip = strtoul(nexttok(&ptr), 0, 10); // kstkeip
+    nexttok(&ptr); // signal
+    nexttok(&ptr); // blocked
+    nexttok(&ptr); // sigignore
+    nexttok(&ptr); // sigcatch
+    wchan = strtoul(nexttok(&ptr), 0, 10); // wchan
+    nexttok(&ptr); // nswap
+    nexttok(&ptr); // cnswap
+    nexttok(&ptr); // exit signal
+    psr = atoi(nexttok(&ptr)); // processor
+    rtprio = atoi(nexttok(&ptr)); // rt_priority
+    sched = atoi(nexttok(&ptr)); // scheduling policy
+    
+    tty = atoi(nexttok(&ptr));
+    
+    if(tid != 0) {
+        ppid = pid;
+        pid = tid;
+    }
+
+    pw = getpwuid(stats.st_uid);
+    if(pw == 0) {
+        sprintf(user,"%d",(int)stats.st_uid);
+    } else {
+        strcpy(user,pw->pw_name);
+    }
+    
+    if(!namefilter || !strncmp(name, namefilter, strlen(namefilter))) {
+        if (display_flags & SHOW_MACLABEL) {
+            fd = open(macline, O_RDONLY);
+            strcpy(macline, "-");
+            if (fd >= 0) {
+                r = read(fd, macline, sizeof(macline)-1);
+                close(fd);
+                if (r > 0)
+                    macline[r] = 0;
+            }
+            printf("%-30s %-9s %-5d %-5d %s\n", macline, user, pid, ppid, cmdline[0] ? cmdline : name);
+            return 0;
+        }
+
+        printf("%-9s %-5d %-5d %-6d %-5d", user, pid, ppid, vss / 1024, rss * 4);
+        if (display_flags & SHOW_CPU)
+            printf(" %-2d", psr);
+        if (display_flags & SHOW_PRIO)
+            printf(" %-5d %-5d %-5d %-5d", prio, nice, rtprio, sched);
+        if (display_flags & SHOW_POLICY) {
+            SchedPolicy p;
+            if (get_sched_policy(pid, &p) < 0)
+                printf(" un ");
+            else {
+                if (p == SP_BACKGROUND)
+                    printf(" bg ");
+                else if (p == SP_FOREGROUND)
+                    printf(" fg ");
+                else
+                    printf(" er ");
+            }
+        }
+        printf(" %08x %08x %s %s", wchan, eip, state, cmdline[0] ? cmdline : name);
+        if(display_flags&SHOW_TIME)
+            printf(" (u:%d, s:%d)", utime, stime);
+
+        printf("\n");
+    }
+    return 0;
+}
+
+
+void ps_threads(int pid, char *namefilter)
+{
+    char tmp[128];
+    DIR *d;
+    struct dirent *de;
+
+    sprintf(tmp,"/proc/%d/task",pid);
+    d = opendir(tmp);
+    if(d == 0) return;
+    
+    while((de = readdir(d)) != 0){
+        if(isdigit(de->d_name[0])){
+            int tid = atoi(de->d_name);
+            if(tid == pid) continue;
+            ps_line(pid, tid, namefilter);
+        }
+    }
+    closedir(d);    
+}
+
+int ps_main(int argc, char **argv)
+{
+    DIR *d;
+    struct dirent *de;
+    char *namefilter = 0;
+    int pidfilter = 0;
+    int threads = 0;
+    
+    d = opendir("/proc");
+    if(d == 0) return -1;
+
+    while(argc > 1){
+        if(!strcmp(argv[1],"-t")) {
+            threads = 1;
+        } else if(!strcmp(argv[1],"-x")) {
+            display_flags |= SHOW_TIME;
+        } else if(!strcmp(argv[1], "-Z")) {
+            display_flags |= SHOW_MACLABEL;
+        } else if(!strcmp(argv[1],"-P")) {
+            display_flags |= SHOW_POLICY;
+        } else if(!strcmp(argv[1],"-p")) {
+            display_flags |= SHOW_PRIO;
+        } else if(!strcmp(argv[1],"-c")) {
+            display_flags |= SHOW_CPU;
+        }  else if(isdigit(argv[1][0])){
+            pidfilter = atoi(argv[1]);
+        } else {
+            namefilter = argv[1];
+        }
+        argc--;
+        argv++;
+    }
+
+    if (display_flags & SHOW_MACLABEL) {
+        printf("LABEL                          USER     PID   PPID  NAME\n");
+    } else {
+        printf("USER     PID   PPID  VSIZE  RSS   %s%s %s WCHAN    PC         NAME\n",
+               (display_flags&SHOW_CPU)?"CPU ":"",
+               (display_flags&SHOW_PRIO)?"PRIO  NICE  RTPRI SCHED ":"",
+               (display_flags&SHOW_POLICY)?"PCY " : "");
+    }
+    while((de = readdir(d)) != 0){
+        if(isdigit(de->d_name[0])){
+            int pid = atoi(de->d_name);
+            if(!pidfilter || (pidfilter == pid)) {
+                ps_line(pid, 0, namefilter);
+                if(threads) ps_threads(pid, namefilter);
+            }
+        }
+    }
+    closedir(d);
+    return 0;
+}
+
diff --git a/toolbox/r.c b/toolbox/r.c
new file mode 100644
index 0000000..7de0396
--- /dev/null
+++ b/toolbox/r.c
@@ -0,0 +1,97 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <string.h>
+
+#define PAGE_SIZE 4096
+
+static int usage()
+{
+    fprintf(stderr,"r [-b|-s] <address> [<value>]\n");
+    return -1;
+}
+
+int r_main(int argc, char *argv[])
+{
+    int width = 4, set = 0, fd;
+    unsigned addr, value, endaddr = 0;
+    unsigned long mmap_start, mmap_size;
+    void *page;
+    char *end;
+    
+    if(argc < 2) return usage();
+
+    if(!strcmp(argv[1], "-b")) {
+        width = 1;
+        argc--;
+        argv++;
+    } else if(!strcmp(argv[1], "-s")) {
+        width = 2;
+        argc--;
+        argv++;
+    }
+
+    if(argc < 2) return usage();
+    addr = strtoul(argv[1], 0, 16);
+
+    end = strchr(argv[1], '-');
+    if (end)
+        endaddr = strtoul(end + 1, 0, 16);
+
+    if (!endaddr)
+        endaddr = addr + width - 1;
+
+    if (endaddr <= addr) {
+        fprintf(stderr, "invalid end address\n");
+        return -1;
+    }
+
+    if(argc > 2) {
+        set = 1;
+        value = strtoul(argv[2], 0, 16);
+    }
+
+    fd = open("/dev/mem", O_RDWR | O_SYNC);
+    if(fd < 0) {
+        fprintf(stderr,"cannot open /dev/mem\n");
+        return -1;
+    }
+    
+    mmap_start = addr & ~(PAGE_SIZE - 1);
+    mmap_size = endaddr - mmap_start + 1;
+    mmap_size = (mmap_size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
+
+    page = mmap(0, mmap_size, PROT_READ | PROT_WRITE,
+                MAP_SHARED, fd, mmap_start);
+
+    if(page == MAP_FAILED){
+        fprintf(stderr,"cannot mmap region\n");
+        return -1;
+    }
+
+    while (addr <= endaddr) {
+        switch(width){
+        case 4: {
+            unsigned *x = (unsigned*) (((unsigned) page) + (addr & 4095));
+            if(set) *x = value;
+            fprintf(stderr,"%08x: %08x\n", addr, *x);
+            break;
+        }
+        case 2: {
+            unsigned short *x = (unsigned short*) (((unsigned) page) + (addr & 4095));
+            if(set) *x = value;
+            fprintf(stderr,"%08x: %04x\n", addr, *x);
+            break;
+        }
+        case 1: {
+            unsigned char *x = (unsigned char*) (((unsigned) page) + (addr & 4095));
+            if(set) *x = value;
+            fprintf(stderr,"%08x: %02x\n", addr, *x);
+            break;
+        }
+        }
+        addr += width;
+    }
+    return 0;
+}
diff --git a/toolbox/readtty.c b/toolbox/readtty.c
new file mode 100644
index 0000000..2b27548
--- /dev/null
+++ b/toolbox/readtty.c
@@ -0,0 +1,183 @@
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+struct {
+    char key;
+    char *chars;
+} map[] = {
+    { '1', "_ -1?!,.:;\"'<=>()_" },
+    { '2', "Cabc2ABC" },
+    { '3', "Fdef3DEF" },
+    { '4', "Ighi4GHI" },
+    { '5', "Ljkl5JKL" },
+    { '6', "Omno6MNO" },
+    { '7', "Spqrs7PQRS" },
+    { '8', "Vtuv8TUV" },
+    { '9', "Zwxyz9WXYZ" },
+    { '0', "*+&0@/#*" },
+};
+
+char next_char(char key, char current)
+{
+    int i;
+    char *next;
+    for(i = 0; i < sizeof(map) / sizeof(map[0]); i++) {
+        if(key == map[i].key) {
+            next = strchr(map[i].chars, current);
+            if(next && next[1])
+                return next[1];
+            return map[i].chars[1];
+        }
+    }
+    return key;
+}
+
+char prev_char(char key, char current)
+{
+    int i;
+    char *next;
+    for(i = 0; i < sizeof(map) / sizeof(map[0]); i++) {
+        if(key == map[i].key) {
+            next = strchr(map[i].chars+1, current);
+            if(next && next[-1])
+                return next[-1];
+            return map[i].chars[1];
+        }
+    }
+    return key;
+}
+
+int readtty_main(int argc, char *argv[])
+{
+    int c;
+    //int flags;
+    char buf[1];
+    int res;
+    struct termios ttyarg;
+    struct termios savedttyarg;
+    int nonblock = 0;
+    int timeout = 0;
+    int flush = 0;
+    int phone = 0;
+    char *accept = NULL;
+    char *rejectstring = NULL;
+    char last_char_in = 0;
+    char current_char = 0;
+    char *exit_string = NULL;
+    int exit_match = 0;
+
+    do {
+        c = getopt(argc, argv, "nt:fa:r:pe:");
+        if (c == EOF)
+            break;
+        switch (c) {
+        case 't':
+            timeout = atoi(optarg);
+            break;
+        case 'n':
+            nonblock = 1;
+            break;
+        case 'f':
+            flush = 1;
+            break;
+        case 'a':
+            accept = optarg;
+            break;
+        case 'r':
+            rejectstring = optarg;
+            break;
+        case 'p':
+            phone = 1;
+            break;
+        case 'e':
+            exit_string = optarg;
+            break;
+        case '?':
+            fprintf(stderr, "%s: invalid option -%c\n",
+                argv[0], optopt);
+            exit(1);
+        }
+    } while (1);
+
+    if(flush)
+        tcflush(STDIN_FILENO, TCIFLUSH);
+    ioctl(STDIN_FILENO, TCGETS , &savedttyarg) ;       /* set changed tty arguments */
+    ttyarg = savedttyarg;
+    ttyarg.c_cc[VMIN] = (timeout > 0 || nonblock) ? 0 : 1;                /* minimum of 0 chars */
+    ttyarg.c_cc[VTIME] = timeout;              /* wait max 15/10 sec */
+    ttyarg.c_iflag = BRKINT | ICRNL; 
+    ttyarg.c_lflag &= ~(ECHO | ICANON);
+    ioctl(STDIN_FILENO, TCSETS , &ttyarg);
+
+    while (1) {
+        res = read(STDIN_FILENO, buf, 1);
+        if(res <= 0) {
+            if(phone) {
+                if(current_char) {
+                    write(STDERR_FILENO, &current_char, 1);
+                    write(STDOUT_FILENO, &current_char, 1);
+                    if(exit_string && current_char == exit_string[exit_match]) {
+                        exit_match++;
+                        if(exit_string[exit_match] == '\0')
+                            break;
+                    }
+                    else
+                        exit_match = 0;
+                    current_char = 0;
+                }
+                continue;
+            }
+            break;
+        }
+        if(accept && strchr(accept, buf[0]) == NULL) {
+            if(rejectstring) {
+                write(STDOUT_FILENO, rejectstring, strlen(rejectstring));
+                break;
+            }
+            if(flush)
+                tcflush(STDIN_FILENO, TCIFLUSH);
+            continue;
+        }
+        if(phone) {
+            //if(!isprint(buf[0])) {
+            //  fprintf(stderr, "got unprintable character 0x%x\n", buf[0]);
+            //}
+            if(buf[0] == '\0') {
+                if(current_char) {
+                    current_char = prev_char(last_char_in, current_char);
+                    write(STDERR_FILENO, &current_char, 1);
+                    write(STDERR_FILENO, "\b", 1);
+                }
+                continue;
+            }
+            if(current_char && buf[0] != last_char_in) {
+                write(STDERR_FILENO, &current_char, 1);
+                write(STDOUT_FILENO, &current_char, 1);
+                if(exit_string && current_char == exit_string[exit_match]) {
+                    exit_match++;
+                    if(exit_string[exit_match] == '\0')
+                        break;
+                }
+                else
+                    exit_match = 0;
+                current_char = 0;
+            }
+            last_char_in = buf[0];
+            current_char = next_char(last_char_in, current_char);
+            write(STDERR_FILENO, &current_char, 1);
+            write(STDERR_FILENO, "\b", 1);
+            continue;
+        }
+        write(STDOUT_FILENO, buf, 1);
+        break;
+    }
+    ioctl(STDIN_FILENO, TCSETS , &savedttyarg) ;       /* set changed tty arguments */
+
+    return 0;
+}
diff --git a/toolbox/reboot.c b/toolbox/reboot.c
new file mode 100644
index 0000000..84fc723
--- /dev/null
+++ b/toolbox/reboot.c
@@ -0,0 +1,64 @@
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <cutils/android_reboot.h>
+#include <unistd.h>
+
+#if HAVE_GLIBC
+#include <linux/reboot.h>
+#include <sys/syscall.h>
+#define __reboot(...) syscall(__NR_reboot,__VA_ARGS__)
+#endif
+
+int reboot_main(int argc, char *argv[])
+{
+    int ret;
+    int nosync = 0;
+    int poweroff = 0;
+    int flags = 0;
+
+    opterr = 0;
+    do {
+        int c;
+
+        c = getopt(argc, argv, "np");
+        
+        if (c == EOF) {
+            break;
+        }
+        
+        switch (c) {
+        case 'n':
+            nosync = 1;
+            break;
+        case 'p':
+            poweroff = 1;
+            break;
+        case '?':
+            fprintf(stderr, "usage: %s [-n] [-p] [rebootcommand]\n", argv[0]);
+            exit(EXIT_FAILURE);
+        }
+    } while (1);
+
+    if(argc > optind + 1) {
+        fprintf(stderr, "%s: too many arguments\n", argv[0]);
+        exit(EXIT_FAILURE);
+    }
+
+    if(nosync)
+        /* also set NO_REMOUNT_RO as remount ro includes an implicit sync */
+        flags = ANDROID_RB_FLAG_NO_SYNC | ANDROID_RB_FLAG_NO_REMOUNT_RO;
+
+    if(poweroff)
+        ret = android_reboot(ANDROID_RB_POWEROFF, flags, 0);
+    else if(argc > optind)
+        ret = android_reboot(ANDROID_RB_RESTART2, flags, argv[optind]);
+    else
+        ret = android_reboot(ANDROID_RB_RESTART, flags, 0);
+    if(ret < 0) {
+        perror("reboot");
+        exit(EXIT_FAILURE);
+    }
+    fprintf(stderr, "reboot returned\n");
+    return 0;
+}
diff --git a/toolbox/renice.c b/toolbox/renice.c
new file mode 100644
index 0000000..978b329
--- /dev/null
+++ b/toolbox/renice.c
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2008, The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the 
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sched.h>
+
+static void
+usage(const char *s)
+{
+    fprintf(stderr, "USAGE: %s [[-r] priority pids ...] [-g pid]\n", s);
+    exit(EXIT_FAILURE);
+}
+
+void print_prio(pid_t pid)
+{
+    int sched;
+    struct sched_param sp;
+
+    printf("pid %d's priority: %d\n", pid, getpriority(PRIO_PROCESS, pid));
+
+    printf("scheduling class: ");
+    sched = sched_getscheduler(pid);
+    switch (sched) {
+    case SCHED_FIFO:
+        printf("FIFO\n");
+        break;
+    case SCHED_RR:
+        printf("RR\n");
+        break;
+    case SCHED_OTHER:
+        printf("Normal\n");
+        break;
+    case -1:
+        perror("sched_getscheduler");
+        break;
+    default:
+        printf("Unknown\n");
+    }
+
+    sched_getparam(pid, &sp);
+    printf("RT prio: %d (of %d to %d)\n", sp.sched_priority,
+           sched_get_priority_min(sched), sched_get_priority_max(sched));
+}
+
+int renice_main(int argc, char *argv[])
+{
+    int prio;
+    int realtime = 0;
+    char *cmd = argv[0];
+
+    // consume command name
+    argc--;
+    argv++;
+
+    if (argc < 1)
+        usage(cmd);
+
+    if(strcmp("-r", argv[0]) == 0) {
+        // do realtime priority adjustment
+        realtime = 1;
+        argc--;
+        argv++;
+    }
+
+	if(strcmp("-g", argv[0]) == 0) {
+        if (argc < 2)
+            usage(cmd);
+        print_prio(atoi(argv[1]));
+        return 0;
+    }
+
+    if (argc < 1)
+        usage(cmd);
+
+    prio = atoi(argv[0]);
+    argc--;
+    argv++;
+
+    if (argc < 1)
+        usage(cmd);
+
+    while(argc) {
+        pid_t pid;
+
+        pid = atoi(argv[0]);
+        argc--;
+        argv++;
+
+        if (realtime) {
+            struct sched_param sp = { .sched_priority = prio };
+            int ret;
+
+            ret = sched_setscheduler(pid, SCHED_RR, &sp);
+            if (ret) {
+                perror("sched_set_scheduler");
+                exit(EXIT_FAILURE);
+            }
+        } else {
+            int ret;
+
+            ret = setpriority(PRIO_PROCESS, pid, prio);
+            if (ret) {
+                perror("setpriority");
+                exit(EXIT_FAILURE);
+            }
+        }
+    }
+   
+    return 0;
+}
+
+
diff --git a/toolbox/restorecon.c b/toolbox/restorecon.c
new file mode 100644
index 0000000..5ef0ef1
--- /dev/null
+++ b/toolbox/restorecon.c
@@ -0,0 +1,141 @@
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fts.h>
+#include <selinux/selinux.h>
+#include <selinux/label.h>
+
+#define FCPATH "/file_contexts"
+
+static struct selabel_handle *sehandle;
+static const char *progname;
+static int nochange;
+static int verbose;
+
+static void usage(void)
+{
+    fprintf(stderr, "usage:  %s [-f file_contexts] [-nrRv] pathname...\n", progname);
+    exit(1);
+}
+
+static int restore(const char *pathname, const struct stat *sb)
+{
+    char *oldcontext, *newcontext;
+
+    if (lgetfilecon(pathname, &oldcontext) < 0) {
+        fprintf(stderr, "Could not get context of %s:  %s\n",
+                pathname, strerror(errno));
+        return -1;
+    }
+    if (selabel_lookup(sehandle, &newcontext, pathname, sb->st_mode) < 0) {
+        fprintf(stderr, "Could not lookup context for %s:  %s\n", pathname,
+                strerror(errno));
+        return -1;
+    }
+    if (strcmp(newcontext, "<<none>>") &&
+        strcmp(oldcontext, newcontext)) {
+        if (verbose)
+            printf("Relabeling %s from %s to %s.\n", pathname, oldcontext, newcontext);
+        if (!nochange) {
+            if (lsetfilecon(pathname, newcontext) < 0) {
+                fprintf(stderr, "Could not label %s with %s:  %s\n",
+                        pathname, newcontext, strerror(errno));
+                return -1;
+            }
+        }
+    }
+    freecon(oldcontext);
+    freecon(newcontext);
+    return 0;
+}
+
+int restorecon_main(int argc, char **argv)
+{
+    struct selinux_opt seopts[] = {
+        { SELABEL_OPT_PATH, FCPATH }
+    };
+    int ch, recurse = 0, ftsflags = FTS_PHYSICAL;
+
+    progname = argv[0];
+
+    do {
+        ch = getopt(argc, argv, "f:nrRv");
+        if (ch == EOF)
+            break;
+        switch (ch) {
+        case 'f':
+            seopts[0].value = optarg;
+            break;
+        case 'n':
+            nochange = 1;
+            break;
+        case 'r':
+        case 'R':
+            recurse = 1;
+            break;
+        case 'v':
+            verbose = 1;
+            break;
+        default:
+            usage();
+        }
+    } while (1);
+
+    argc -= optind;
+    argv += optind;
+    if (!argc)
+        usage();
+
+    sehandle = selabel_open(SELABEL_CTX_FILE, seopts, 1);
+    if (!sehandle) {
+        fprintf(stderr, "Could not load file contexts from %s:  %s\n", seopts[0].value,
+                strerror(errno));
+        return -1;
+    }
+
+    if (recurse) {
+        FTS *fts;
+        FTSENT *ftsent;
+        fts = fts_open(argv, ftsflags, NULL);
+        if (!fts) {
+            fprintf(stderr, "Could not traverse filesystems (first was %s):  %s\n",
+                    argv[0], strerror(errno));
+            return -1;
+        }
+        while ((ftsent = fts_read(fts))) {
+            switch (ftsent->fts_info) {
+            case FTS_DP:
+                break;
+            case FTS_DNR:
+            case FTS_ERR:
+            case FTS_NS:
+                fprintf(stderr, "Could not access %s:  %s\n", ftsent->fts_path,
+                        strerror(errno));
+                fts_set(fts, ftsent, FTS_SKIP);
+                break;
+            default:
+                if (restore(ftsent->fts_path, ftsent->fts_statp) < 0)
+                    fts_set(fts, ftsent, FTS_SKIP);
+                break;
+            }
+        }
+    } else {
+        int i, rc;
+        struct stat sb;
+
+        for (i = 0; i < argc; i++) {
+            rc = lstat(argv[i], &sb);
+            if (rc < 0) {
+                fprintf(stderr, "Could not stat %s:  %s\n", argv[i],
+                        strerror(errno));
+                continue;
+            }
+            restore(argv[i], &sb);
+        }
+    }
+
+    return 0;
+}
diff --git a/toolbox/rm.c b/toolbox/rm.c
new file mode 100644
index 0000000..c29384e
--- /dev/null
+++ b/toolbox/rm.c
@@ -0,0 +1,93 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <dirent.h>
+#include <limits.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+static int usage()
+{
+    fprintf(stderr,"rm [-rR] <target>\n");
+    return -1;
+}
+
+/* return -1 on failure, with errno set to the first error */
+static int unlink_recursive(const char* name)
+{
+    struct stat st;
+    DIR *dir;
+    struct dirent *de;
+    int fail = 0;
+
+    /* is it a file or directory? */
+    if (lstat(name, &st) < 0)
+        return -1;
+
+    /* a file, so unlink it */
+    if (!S_ISDIR(st.st_mode))
+        return unlink(name);
+
+    /* a directory, so open handle */
+    dir = opendir(name);
+    if (dir == NULL)
+        return -1;
+
+    /* recurse over components */
+    errno = 0;
+    while ((de = readdir(dir)) != NULL) {
+        char dn[PATH_MAX];
+        if (!strcmp(de->d_name, "..") || !strcmp(de->d_name, "."))
+            continue;
+        sprintf(dn, "%s/%s", name, de->d_name);
+        if (unlink_recursive(dn) < 0) {
+            fail = 1;
+            break;
+        }
+        errno = 0;
+    }
+    /* in case readdir or unlink_recursive failed */
+    if (fail || errno < 0) {
+        int save = errno;
+        closedir(dir);
+        errno = save;
+        return -1;
+    }
+
+    /* close directory handle */
+    if (closedir(dir) < 0)
+        return -1;
+
+    /* delete target directory */
+    return rmdir(name);
+}
+
+int rm_main(int argc, char *argv[])
+{
+    int ret, err;
+    int i = 1;
+    int recursive = 0;
+
+    if (argc < 2)
+        return usage();
+
+    /* check if recursive */
+    if (argc >=2 && (!strcmp(argv[1], "-r") || !strcmp(argv[1], "-R"))) {
+        recursive = 1;
+        i = 2;
+    }
+
+    /* loop over the file/directory args */
+    err = 0;
+    for (; i < argc; i++) {
+        int ret = recursive ? unlink_recursive(argv[i]) : unlink(argv[i]);
+        if (ret < 0) {
+            fprintf(stderr, "rm failed for %s, %s\n", argv[i], strerror(errno));
+            err = -1;
+        }
+    }
+
+    return err;
+}
+
diff --git a/toolbox/rmdir.c b/toolbox/rmdir.c
new file mode 100644
index 0000000..06f3df2
--- /dev/null
+++ b/toolbox/rmdir.c
@@ -0,0 +1,29 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+static int usage()
+{
+    fprintf(stderr,"rmdir <directory>\n");
+    return -1;
+}
+
+int rmdir_main(int argc, char *argv[])
+{
+    int symbolic = 0;
+    int ret;
+    if(argc < 2) return usage();
+
+    while(argc > 1) {
+        argc--;
+        argv++;
+        ret = rmdir(argv[0]);
+        if(ret < 0) {
+            fprintf(stderr, "rmdir failed for %s, %s\n", argv[0], strerror(errno));
+            return ret;
+        }
+    }
+    
+    return 0;
+}
diff --git a/toolbox/rmmod.c b/toolbox/rmmod.c
new file mode 100644
index 0000000..25257cc
--- /dev/null
+++ b/toolbox/rmmod.c
@@ -0,0 +1,44 @@
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <malloc.h>
+#include <errno.h>
+#include <asm/unistd.h>
+
+extern int delete_module(const char *, unsigned int);
+
+int rmmod_main(int argc, char **argv)
+{
+	int ret;
+	char *modname, *dot;
+
+	/* make sure we've got an argument */
+	if (argc < 2) {
+		fprintf(stderr, "usage: rmmod <module>\n");
+		return -1;
+	}
+
+	/* if given /foo/bar/blah.ko, make a weak attempt
+	 * to convert to "blah", just for convenience
+	 */
+	modname = strrchr(argv[1], '/');
+	if (!modname)
+		modname = argv[1];
+	else modname++;
+
+	dot = strchr(argv[1], '.');
+	if (dot)
+		*dot = '\0';
+
+	/* pass it to the kernel */
+	ret = delete_module(modname, O_NONBLOCK | O_EXCL);
+	if (ret != 0) {
+		fprintf(stderr, "rmmod: delete_module '%s' failed (errno %d)\n",
+						modname, errno);
+		return -1;
+	}
+
+	return 0;
+}
+
diff --git a/toolbox/rotatefb.c b/toolbox/rotatefb.c
new file mode 100644
index 0000000..2ff4127
--- /dev/null
+++ b/toolbox/rotatefb.c
@@ -0,0 +1,71 @@
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+#include <errno.h>
+#include <linux/fb.h>
+
+
+int rotatefb_main(int argc, char *argv[])
+{
+    int c;
+    char *fbdev = "/dev/graphics/fb0";
+    int rotation = 0;
+    int fd;
+    int res;
+    struct fb_var_screeninfo fbinfo;
+
+    do {
+        c = getopt(argc, argv, "d:");
+        if (c == EOF)
+            break;
+        switch (c) {
+        case 'd':
+            fbdev = optarg;
+            break;
+        case '?':
+            fprintf(stderr, "%s: invalid option -%c\n",
+                argv[0], optopt);
+            exit(1);
+        }
+    } while (1);
+
+    if(optind + 1 != argc) {
+        fprintf(stderr, "%s: specify rotation\n", argv[0]);
+        exit(1);
+    }
+    rotation = atoi(argv[optind]);
+
+    fd = open(fbdev, O_RDWR);
+    if(fd < 0) {
+        fprintf(stderr, "cannot open %s\n", fbdev);
+        return 1;
+    }
+
+    res = ioctl(fd, FBIOGET_VSCREENINFO, &fbinfo);
+    if(res < 0) {
+        fprintf(stderr, "failed to get fbinfo: %s\n", strerror(errno));
+        return 1;
+    }
+    if((fbinfo.rotate ^ rotation) & 1) {
+        unsigned int xres = fbinfo.yres;
+        fbinfo.yres = fbinfo.xres;
+        fbinfo.xres = xres;
+        fbinfo.xres_virtual = fbinfo.xres;
+        fbinfo.yres_virtual = fbinfo.yres * 2;
+        if(fbinfo.yoffset == xres)
+            fbinfo.yoffset = fbinfo.yres;
+    }
+    fbinfo.rotate = rotation; 
+    res = ioctl(fd, FBIOPUT_VSCREENINFO, &fbinfo);
+    if(res < 0) {
+        fprintf(stderr, "failed to set fbinfo: %s\n", strerror(errno));
+        return 1;
+    }
+
+    return 0;
+}
diff --git a/toolbox/route.c b/toolbox/route.c
new file mode 100644
index 0000000..3e10014
--- /dev/null
+++ b/toolbox/route.c
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2009, The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <linux/route.h>
+
+static inline int set_address(const char *address, struct sockaddr *sa) {
+    return inet_aton(address, &((struct sockaddr_in *)sa)->sin_addr);
+}
+
+/* current support the following routing entries */
+/* route add default dev wlan0 */
+/* route add default gw 192.168.1.1 dev wlan0 */
+/* route add -net 192.168.1.2 netmask 255.255.255.0 gw 192.168.1.1 */
+
+int route_main(int argc, char *argv[])
+{
+    struct rtentry rt = {
+        .rt_dst     = {.sa_family = AF_INET},
+        .rt_genmask = {.sa_family = AF_INET},
+        .rt_gateway = {.sa_family = AF_INET},
+    };
+
+    errno = EINVAL;
+    if (argc > 2 && !strcmp(argv[1], "add")) {
+        if (!strcmp(argv[2], "default")) {
+            /* route add default dev wlan0 */
+            if (argc > 4 && !strcmp(argv[3], "dev")) {
+                rt.rt_flags = RTF_UP;
+                rt.rt_dev = argv[4];
+                errno = 0;
+                goto apply;
+            }
+
+            /* route add default gw 192.168.1.1 dev wlan0 */
+            if (argc > 6 && !strcmp(argv[3], "gw") && !strcmp(argv[5], "dev")) {
+                rt.rt_flags = RTF_UP | RTF_GATEWAY;
+                rt.rt_dev = argv[6];
+                if (set_address(argv[4], &rt.rt_gateway)) {
+                    errno = 0;
+                }
+                goto apply;
+            }
+        }
+
+        /* route add -net 192.168.1.2 netmask 255.255.255.0 gw 192.168.1.1 */
+        if (argc > 7 && !strcmp(argv[2], "-net") &&
+                !strcmp(argv[4], "netmask")) {
+            if (!strcmp(argv[6], "gw")) {
+                rt.rt_flags = RTF_UP | RTF_GATEWAY;
+                if (set_address(argv[3], &rt.rt_dst) &&
+                    set_address(argv[5], &rt.rt_genmask) &&
+                    set_address(argv[7], &rt.rt_gateway)) {
+                    errno = 0;
+                }
+                goto apply;
+            } else if (!strcmp(argv[6], "dev")) {
+                rt.rt_flags = RTF_UP;
+                rt.rt_dev = argv[7];
+                if (set_address(argv[3], &rt.rt_dst) &&
+                    set_address(argv[5], &rt.rt_genmask)) {
+                    errno = 0;
+                }
+                goto apply;
+            }
+        }
+    }
+
+apply:
+    if (!errno) {
+        int s = socket(AF_INET, SOCK_DGRAM, 0);
+        if (s != -1 && (ioctl(s, SIOCADDRT, &rt) != -1 || errno == EEXIST)) {
+            return 0;
+        }
+    }
+    puts(strerror(errno));
+    return errno;
+}
diff --git a/toolbox/runcon.c b/toolbox/runcon.c
new file mode 100644
index 0000000..4a57bf3
--- /dev/null
+++ b/toolbox/runcon.c
@@ -0,0 +1,28 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <selinux/selinux.h>
+
+int runcon_main(int argc, char **argv)
+{
+    int rc;
+
+    if (argc < 3) {
+        fprintf(stderr, "usage:  %s context program args...\n", argv[0]);
+        exit(1);
+    }
+
+    rc = setexeccon(argv[1]);
+    if (rc < 0) {
+        fprintf(stderr, "Could not set context to %s:  %s\n", argv[1], strerror(errno));
+        exit(2);
+    }
+
+    argv += 2;
+    argc -= 2;
+    execvp(argv[0], argv);
+    fprintf(stderr, "Could not exec %s:  %s\n", argv[0], strerror(errno));
+    exit(3);
+}
diff --git a/toolbox/schedtop.c b/toolbox/schedtop.c
new file mode 100644
index 0000000..6859b50
--- /dev/null
+++ b/toolbox/schedtop.c
@@ -0,0 +1,336 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <stdint.h>
+#include <string.h>
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <signal.h>
+
+#include <pwd.h>
+
+struct thread_info {
+    int pid;
+    int tid;
+    char name[64];
+    uint64_t exec_time;
+    uint64_t delay_time;
+    uint32_t run_count;
+};
+
+struct thread_table {
+    size_t allocated;
+    size_t active;
+    struct thread_info *data;
+};
+
+enum {
+    FLAG_BATCH = 1U << 0,
+    FLAG_HIDE_IDLE = 1U << 1,
+    FLAG_SHOW_THREADS = 1U << 2,
+    FLAG_USE_ALTERNATE_SCREEN = 1U << 3,
+};
+
+static int time_dp = 9;
+static int time_div = 1;
+#define NS_TO_S_D(ns) \
+    (uint32_t)((ns) / 1000000000), time_dp, ((uint32_t)((ns) % 1000000000) / time_div)
+
+struct thread_table processes;
+struct thread_table last_processes;
+struct thread_table threads;
+struct thread_table last_threads;
+
+static void grow_table(struct thread_table *table)
+{
+    size_t size = table->allocated;
+    struct thread_info *new_table;
+    if (size < 128)
+        size = 128;
+    else
+        size *= 2;
+    
+    new_table = realloc(table->data, size * sizeof(*table->data));
+    if (new_table == NULL) {
+        fprintf(stderr, "out of memory\n");
+        exit(1);
+    }
+    table->data = new_table;
+    table->allocated = size;
+}
+
+static struct thread_info *get_item(struct thread_table *table)
+{
+    if (table->active >= table->allocated)
+        grow_table(table);
+    return table->data + table->active;
+}
+
+static void commit_item(struct thread_table *table)
+{
+    table->active++;
+}
+
+static int read_line(char *line, size_t line_size)
+{
+    int fd;
+    int len;
+    fd = open(line, O_RDONLY);
+    if(fd == 0)
+        return -1;
+    len = read(fd, line, line_size - 1);
+    close(fd);
+    if (len <= 0)
+        return -1;
+    line[len] = '\0';
+    return 0;
+}
+
+static void add_thread(int pid, int tid, struct thread_info *proc_info)
+{
+    char line[1024];
+    char *name, *name_end;
+    size_t name_len;
+    struct thread_info *info;
+    if(tid == 0)
+        info = get_item(&processes);
+    else
+        info = get_item(&threads);
+    info->pid = pid;
+    info->tid = tid;
+
+    if(tid)
+        sprintf(line, "/proc/%d/task/%d/schedstat", pid, tid);
+    else
+        sprintf(line, "/proc/%d/schedstat", pid);
+    if (read_line(line, sizeof(line)))
+        return;
+    if(sscanf(line, "%llu %llu %u", &info->exec_time, &info->delay_time, &info->run_count) != 3)
+        return;
+    if (proc_info) {
+        proc_info->exec_time += info->exec_time;
+        proc_info->delay_time += info->delay_time;
+        proc_info->run_count += info->run_count;
+    }
+
+    name = NULL;
+    if (!tid) {
+        sprintf(line, "/proc/%d/cmdline", pid);
+        if (read_line(line, sizeof(line)) == 0 && line[0]) {
+            name = line;
+            name_len = strlen(name);
+        }
+    }
+    if (!name) {
+        if (tid)
+            sprintf(line, "/proc/%d/task/%d/stat", pid, tid);
+        else
+            sprintf(line, "/proc/%d/stat", pid);
+        if (read_line(line, sizeof(line)))
+            return;
+        name = strchr(line, '(');
+        if (name == NULL)
+            return;
+        name_end = strchr(name, ')');
+        if (name_end == NULL)
+            return;
+        name++;
+        name_len = name_end - name;
+    }
+    if (name_len >= sizeof(info->name))
+        name_len = sizeof(info->name) - 1;
+    memcpy(info->name, name, name_len);
+    info->name[name_len] = '\0';
+    if(tid == 0)
+        commit_item(&processes);
+    else
+        commit_item(&threads);
+}
+
+static void add_threads(int pid, struct thread_info *proc_info)
+{
+    char path[1024];
+    DIR *d;
+    struct dirent *de;
+    sprintf(path, "/proc/%d/task", pid);
+    d = opendir(path);
+    if(d == 0) return;
+    while((de = readdir(d)) != 0){
+        if(isdigit(de->d_name[0])){
+            int tid = atoi(de->d_name);
+            add_thread(pid, tid, proc_info);
+        }
+    }
+    closedir(d);
+}
+
+static void print_threads(int pid, uint32_t flags)
+{
+    size_t i, j;
+    for (i = 0; i < last_threads.active; i++) {
+        int epid = last_threads.data[i].pid;
+        int tid = last_threads.data[i].tid;
+        if (epid != pid)
+            continue;
+        for (j = 0; j < threads.active; j++)
+            if (tid == threads.data[j].tid)
+                break;
+        if (j == threads.active)
+            printf(" %5u died\n", tid);
+        else if (!(flags & FLAG_HIDE_IDLE) || threads.data[j].run_count - last_threads.data[i].run_count)
+            printf(" %5u %2u.%0*u %2u.%0*u %5u %5u.%0*u %5u.%0*u %7u  %s\n", tid,
+                NS_TO_S_D(threads.data[j].exec_time - last_threads.data[i].exec_time),
+                NS_TO_S_D(threads.data[j].delay_time - last_threads.data[i].delay_time),
+                threads.data[j].run_count - last_threads.data[i].run_count,
+                NS_TO_S_D(threads.data[j].exec_time), NS_TO_S_D(threads.data[j].delay_time),
+                threads.data[j].run_count, threads.data[j].name);
+    }
+}
+
+static void update_table(DIR *d, uint32_t flags)
+{
+    size_t i, j;
+    struct dirent *de;
+    
+    rewinddir(d);
+    while((de = readdir(d)) != 0){
+        if(isdigit(de->d_name[0])){
+            int pid = atoi(de->d_name);
+            struct thread_info *proc_info;
+            add_thread(pid, 0, NULL);
+            proc_info = &processes.data[processes.active - 1];
+            proc_info->exec_time = 0;
+            proc_info->delay_time = 0;
+            proc_info->run_count = 0;
+            add_threads(pid, proc_info);
+        }
+    }
+    if (!(flags & FLAG_BATCH))
+        printf("\e[H\e[0J");
+    printf("Processes: %d, Threads %d\n", processes.active, threads.active);
+    switch (time_dp) {
+    case 3:
+        printf("   TID --- SINCE LAST ---- ---------- TOTAL ----------\n");
+        printf("  PID  EXEC_T  DELAY SCHED EXEC_TIME DELAY_TIM   SCHED NAME\n");
+        break;
+    case 6:
+        printf("   TID ------ SINCE LAST -------    ------------ TOTAL -----------\n");
+        printf("  PID  EXEC_TIME DELAY_TIM SCHED    EXEC_TIME   DELAY_TIME   SCHED NAME\n");
+        break;
+    default:
+        printf("   TID    -------- SINCE LAST --------       ------------- TOTAL -------------\n");
+        printf("  PID     EXEC_TIME   DELAY_TIME SCHED       EXEC_TIME      DELAY_TIME   SCHED NAME\n");
+        break;
+    }
+    for (i = 0; i < last_processes.active; i++) {
+        int pid = last_processes.data[i].pid;
+        int tid = last_processes.data[i].tid;
+        for (j = 0; j < processes.active; j++)
+            if (pid == processes.data[j].pid)
+                break;
+        if (j == processes.active)
+            printf("%5u died\n", pid);
+        else if (!(flags & FLAG_HIDE_IDLE) || processes.data[j].run_count - last_processes.data[i].run_count) {
+            printf("%5u  %2u.%0*u %2u.%0*u %5u %5u.%0*u %5u.%0*u %7u %s\n", pid,
+                NS_TO_S_D(processes.data[j].exec_time - last_processes.data[i].exec_time),
+                NS_TO_S_D(processes.data[j].delay_time - last_processes.data[i].delay_time),
+                processes.data[j].run_count - last_processes.data[i].run_count,
+                NS_TO_S_D(processes.data[j].exec_time), NS_TO_S_D(processes.data[j].delay_time),
+                processes.data[j].run_count, processes.data[j].name);
+            if (flags & FLAG_SHOW_THREADS)
+                print_threads(pid, flags);
+        }
+    }
+
+    {
+        struct thread_table tmp;
+        tmp = last_processes;
+        last_processes = processes;
+        processes = tmp;
+        processes.active = 0;
+        tmp = last_threads;
+        last_threads = threads;
+        threads = tmp;
+        threads.active = 0;
+    }
+}
+
+void
+sig_abort(int signum)
+{
+    printf("\e[?47l");
+    exit(0);
+}
+
+
+int schedtop_main(int argc, char **argv)
+{
+    int c;
+    DIR *d;
+    struct dirent *de;
+    char *namefilter = 0;
+    int pidfilter = 0;
+    uint32_t flags = 0;    
+    int delay = 3000000;
+    float delay_f;
+
+    while(1) {
+        c = getopt(argc, argv, "d:ibtamun");
+        if (c == EOF)
+            break;
+        switch (c) {
+        case 'd':
+            delay_f = atof(optarg);
+            delay = delay_f * 1000000;
+            break;
+        case 'b':
+            flags |= FLAG_BATCH;
+            break;
+        case 'i':
+            flags |= FLAG_HIDE_IDLE;
+            break;
+        case 't':
+            flags |= FLAG_SHOW_THREADS;
+            break;
+        case 'a':
+            flags |= FLAG_USE_ALTERNATE_SCREEN;
+            break;
+        case 'm':
+            time_dp = 3;
+            time_div = 1000000;
+            break;
+        case 'u':
+            time_dp = 6;
+            time_div = 1000;
+            break;
+        case 'n':
+            time_dp = 9;
+            time_div = 1;
+            break;
+        }
+    }
+
+    d = opendir("/proc");
+    if(d == 0) return -1;
+
+    if (!(flags & FLAG_BATCH)) {
+        if(flags & FLAG_USE_ALTERNATE_SCREEN) {
+            signal(SIGINT, sig_abort);
+            signal(SIGPIPE, sig_abort);
+            signal(SIGTERM, sig_abort);
+            printf("\e7\e[?47h");
+        }
+        printf("\e[2J");
+    }
+    while (1) {
+        update_table(d, flags);
+        usleep(delay);
+    }
+    closedir(d);
+    return 0;
+}
diff --git a/toolbox/sendevent.c b/toolbox/sendevent.c
new file mode 100644
index 0000000..a09b2b7
--- /dev/null
+++ b/toolbox/sendevent.c
@@ -0,0 +1,82 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#if HAVE_GLIBC
+#include <linux/input.h> // this does not compile
+#else
+// from <linux/input.h>
+
+struct input_event {
+	struct timeval time;
+	__u16 type;
+	__u16 code;
+	__s32 value;
+};
+#endif
+
+#define EVIOCGVERSION		_IOR('E', 0x01, int)			/* get driver version */
+#define EVIOCGID		_IOR('E', 0x02, struct input_id)	/* get device ID */
+#define EVIOCGKEYCODE		_IOR('E', 0x04, int[2])			/* get keycode */
+#define EVIOCSKEYCODE		_IOW('E', 0x04, int[2])			/* set keycode */
+
+#define EVIOCGNAME(len)		_IOC(_IOC_READ, 'E', 0x06, len)		/* get device name */
+#define EVIOCGPHYS(len)		_IOC(_IOC_READ, 'E', 0x07, len)		/* get physical location */
+#define EVIOCGUNIQ(len)		_IOC(_IOC_READ, 'E', 0x08, len)		/* get unique identifier */
+
+#define EVIOCGKEY(len)		_IOC(_IOC_READ, 'E', 0x18, len)		/* get global keystate */
+#define EVIOCGLED(len)		_IOC(_IOC_READ, 'E', 0x19, len)		/* get all LEDs */
+#define EVIOCGSND(len)		_IOC(_IOC_READ, 'E', 0x1a, len)		/* get all sounds status */
+#define EVIOCGSW(len)		_IOC(_IOC_READ, 'E', 0x1b, len)		/* get all switch states */
+
+#define EVIOCGBIT(ev,len)	_IOC(_IOC_READ, 'E', 0x20 + ev, len)	/* get event bits */
+#define EVIOCGABS(abs)		_IOR('E', 0x40 + abs, struct input_absinfo)		/* get abs value/limits */
+#define EVIOCSABS(abs)		_IOW('E', 0xc0 + abs, struct input_absinfo)		/* set abs value/limits */
+
+#define EVIOCSFF		_IOC(_IOC_WRITE, 'E', 0x80, sizeof(struct ff_effect))	/* send a force effect to a force feedback device */
+#define EVIOCRMFF		_IOW('E', 0x81, int)			/* Erase a force effect */
+#define EVIOCGEFFECTS		_IOR('E', 0x84, int)			/* Report number of effects playable at the same time */
+
+#define EVIOCGRAB		_IOW('E', 0x90, int)			/* Grab/Release device */
+
+// end <linux/input.h>
+
+
+
+int sendevent_main(int argc, char *argv[])
+{
+    int i;
+    int fd;
+    int ret;
+    int version;
+    struct input_event event;
+
+    if(argc != 5) {
+        fprintf(stderr, "use: %s device type code value\n", argv[0]);
+        return 1;
+    }
+
+    fd = open(argv[1], O_RDWR);
+    if(fd < 0) {
+        fprintf(stderr, "could not open %s, %s\n", argv[optind], strerror(errno));
+        return 1;
+    }
+    if (ioctl(fd, EVIOCGVERSION, &version)) {
+        fprintf(stderr, "could not get driver version for %s, %s\n", argv[optind], strerror(errno));
+        return 1;
+    }
+    memset(&event, 0, sizeof(event));
+    event.type = atoi(argv[2]);
+    event.code = atoi(argv[3]);
+    event.value = atoi(argv[4]);
+    ret = write(fd, &event, sizeof(event));
+    if(ret < sizeof(event)) {
+        fprintf(stderr, "write event failed, %s\n", strerror(errno));
+        return -1;
+    }
+    return 0;
+}
diff --git a/toolbox/setconsole.c b/toolbox/setconsole.c
new file mode 100644
index 0000000..0159c07
--- /dev/null
+++ b/toolbox/setconsole.c
@@ -0,0 +1,166 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <linux/kd.h>
+#include <linux/vt.h>
+#include <errno.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+
+static int activate_thread_switch_vc;
+static void *activate_thread(void *arg)
+{
+    int res;
+    int fd = (int)arg;
+    while(activate_thread_switch_vc >= 0) {
+        do {
+            res = ioctl(fd, VT_ACTIVATE, (void*)activate_thread_switch_vc);
+        } while(res < 0 && errno == EINTR);
+        if (res < 0) {
+            fprintf(stderr, "ioctl( vcfd, VT_ACTIVATE, vtnum) failed, %d %d %s for %d\n", res, errno, strerror(errno), activate_thread_switch_vc);
+        }
+        if(activate_thread_switch_vc >= 0)
+            sleep(1);
+    }
+    return NULL;
+}
+
+
+int setconsole_main(int argc, char *argv[])
+{
+    int c;
+    int fd;
+    int res;
+
+    int mode = -1;
+    int new_vc = 0;
+    int close_vc = 0;
+    int switch_vc = -1;
+    int printvc = 0;
+    char *ttydev = "/dev/tty0";
+
+    do {
+        c = getopt(argc, argv, "d:gtncv:poh");
+        if (c == EOF)
+            break;
+        switch (c) {
+        case 'd':
+            ttydev = optarg;
+            break;
+        case 'g':
+            if(mode == KD_TEXT) {
+                fprintf(stderr, "%s: cannot specify both -g and -t\n", argv[0]);
+                exit(1);
+            }
+            mode = KD_GRAPHICS;
+            break;
+        case 't':
+            if(mode == KD_GRAPHICS) {
+                fprintf(stderr, "%s: cannot specify both -g and -t\n", argv[0]);
+                exit(1);
+            }
+            mode = KD_TEXT;
+            break;
+        case 'n':
+            new_vc = 1;
+            break;
+        case 'c':
+            close_vc = 1;
+            break;
+        case 'v':
+            switch_vc = atoi(optarg);
+            break;
+        case 'p':
+            printvc |= 1;
+            break;
+        case 'o':
+            printvc |= 2;
+            break;
+        case 'h':
+            fprintf(stderr, "%s [-d <dev>] [-v <vc>] [-gtncpoh]\n"
+                    "  -d <dev>   Use <dev> instead of /dev/tty0\n"
+                    "  -v <vc>    Switch to virtual console <vc>\n"
+                    "  -g         Switch to graphics mode\n"
+                    "  -t         Switch to text mode\n"
+                    "  -n         Create and switch to new virtual console\n"
+                    "  -c         Close unused virtual consoles\n"
+                    "  -p         Print new virtual console\n"
+                    "  -o         Print old virtual console\n"
+                    "  -h         Print help\n", argv[0]);
+            return -1;
+        case '?':
+            fprintf(stderr, "%s: invalid option -%c\n",
+                argv[0], optopt);
+            exit(1);
+        }
+    } while (1);
+    if(mode == -1 && new_vc == 0 && close_vc == 0 && switch_vc == -1 && printvc == 0) {
+        fprintf(stderr,"%s [-d <dev>] [-v <vc>] [-gtncpoh]\n", argv[0]);
+        return -1;
+    }
+
+    fd = open(ttydev, O_RDWR | O_SYNC);
+    if (fd < 0) {
+        fprintf(stderr, "cannot open %s\n", ttydev);
+        return -1;
+    }
+
+    if ((printvc && !new_vc) || (printvc & 2)) {
+        struct vt_stat vs;
+
+        res = ioctl(fd, VT_GETSTATE, &vs);
+        if (res < 0) {
+            fprintf(stderr, "ioctl(vcfd, VT_GETSTATE, &vs) failed, %d\n", res);
+        }
+        printf("%d\n", vs.v_active);
+    }
+
+    if (new_vc) {
+        int vtnum;
+        res = ioctl(fd, VT_OPENQRY, &vtnum);
+        if (res < 0 || vtnum == -1) {
+            fprintf(stderr, "ioctl(vcfd, VT_OPENQRY, &vtnum) failed, res %d, vtnum %d\n", res, vtnum);
+        }
+        switch_vc = vtnum;
+    }
+    if (switch_vc != -1) {
+        pthread_t thread;
+        pthread_attr_t attr;
+        activate_thread_switch_vc = switch_vc;
+        pthread_attr_init(&attr);
+        pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+        pthread_create(&thread, &attr, activate_thread, (void*)fd);
+        
+        do {
+            res = ioctl(fd, VT_WAITACTIVE, (void*)switch_vc);
+        } while(res < 0 && errno == EINTR);
+        activate_thread_switch_vc = -1;
+        if (res < 0) {
+            fprintf(stderr, "ioctl( vcfd, VT_WAITACTIVE, vtnum) failed, %d %d %s for %d\n", res, errno, strerror(errno), switch_vc);
+        }
+        if(printvc & 1)
+            printf("%d\n", switch_vc);
+
+        close(fd);
+        fd = open(ttydev, O_RDWR | O_SYNC);
+        if (fd < 0) {
+            fprintf(stderr, "cannot open %s\n", ttydev);
+            return -1;
+        }
+    }
+    if (close_vc) {
+        res = ioctl(fd, VT_DISALLOCATE, 0);
+        if (res < 0) {
+            fprintf(stderr, "ioctl(vcfd, VT_DISALLOCATE, 0) failed, %d\n", res);
+        }
+    }
+    if (mode != -1) {
+        if (ioctl(fd, KDSETMODE, (void*)mode) < 0) {
+            fprintf(stderr, "KDSETMODE %d failed\n", mode);
+            return -1;
+        }
+    }
+    return 0;
+}
diff --git a/toolbox/setenforce.c b/toolbox/setenforce.c
new file mode 100644
index 0000000..1b0ea5c
--- /dev/null
+++ b/toolbox/setenforce.c
@@ -0,0 +1,44 @@
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <strings.h>
+#include <errno.h>
+#include <selinux/selinux.h>
+
+void usage(const char *progname)
+{
+    fprintf(stderr, "usage:  %s [ Enforcing | Permissive | 1 | 0 ]\n",
+            progname);
+    exit(1);
+}
+
+int setenforce_main(int argc, char **argv)
+{
+    int rc = 0;
+    if (argc != 2) {
+        usage(argv[0]);
+    }
+
+    if (is_selinux_enabled() <= 0) {
+        fprintf(stderr, "%s: SELinux is disabled\n", argv[0]);
+        return 1;
+    }
+    if (strlen(argv[1]) == 1 && (argv[1][0] == '0' || argv[1][0] == '1')) {
+        rc = security_setenforce(atoi(argv[1]));
+    } else {
+        if (strcasecmp(argv[1], "enforcing") == 0) {
+            rc = security_setenforce(1);
+        } else if (strcasecmp(argv[1], "permissive") == 0) {
+            rc = security_setenforce(0);
+        } else
+            usage(argv[0]);
+    }
+    if (rc < 0) {
+        fprintf(stderr, "%s:  Could not set enforcing status:  %s\n",
+                argv[0], strerror(errno));
+        return 2;
+    }
+    return 0;
+}
diff --git a/toolbox/setkey.c b/toolbox/setkey.c
new file mode 100644
index 0000000..1ff2774
--- /dev/null
+++ b/toolbox/setkey.c
@@ -0,0 +1,89 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <linux/kd.h>
+#include <linux/vt.h>
+#include <errno.h>
+
+static void setkey_usage(char *argv[])
+{
+    fprintf(stderr, "%s [-t <table>] [-k <index>] [-v value] [-r] [-h]\n"
+            "  -t <table> Select table\n"
+            "  -k <index> Select key\n"
+            "  -v <value> Set entry\n"
+            "  -r         Read current entry\n"
+            "  -h         Print help\n", argv[0]);
+}
+
+#define TTYDEV	"/dev/tty0"
+
+int setkey_main(int argc, char *argv[])
+{
+    int fd;
+    struct kbentry kbe;
+    int did_something = 0;
+
+    kbe.kb_table = 0;
+    kbe.kb_index = -1;
+    kbe.kb_value = 0;
+
+    fd = open(TTYDEV, O_RDWR | O_SYNC);
+    if (fd < 0) {
+        fprintf(stderr, "open %s: %s\n", TTYDEV, strerror(errno));
+        return 1;
+    }
+
+    do {
+        int c, ret;
+
+        c = getopt(argc, argv, "t:k:v:hr");
+        if (c == EOF)
+            break;
+
+        switch (c) {
+        case 't':
+            kbe.kb_table = strtol(optarg, NULL, 0);
+            break;
+        case 'k':
+            kbe.kb_index = strtol(optarg, NULL, 0);
+            break;
+        case 'v':
+            kbe.kb_value = strtol(optarg, NULL, 0);
+            ret = ioctl(fd, KDSKBENT, &kbe);
+            if (ret < 0) {
+                fprintf(stderr, "KDSKBENT %d %d %d failed: %s\n",
+                        kbe.kb_table, kbe.kb_index, kbe.kb_value,
+                        strerror(errno));
+                return 1;
+            }
+            did_something = 1;
+            break;
+        case 'r':
+            ret = ioctl(fd, KDGKBENT, &kbe);
+            if (ret < 0) {
+                fprintf(stderr, "KDGKBENT %d %d  failed: %s\n",
+                        kbe.kb_table, kbe.kb_index, strerror(errno));
+                return 1;
+            }
+            printf("0x%x 0x%x 0x%x\n",
+                   kbe.kb_table, kbe.kb_index, kbe.kb_value);
+            did_something = 1;
+            break;
+        case 'h':
+            setkey_usage(argv);
+            return 1;
+        case '?':
+            fprintf(stderr, "%s: invalid option -%c\n",
+                argv[0], optopt);
+            return 1;
+        }
+    } while (1);
+
+    if(optind != argc || !did_something) {
+        setkey_usage(argv);
+        return 1;
+    }
+
+    return 0;
+}
diff --git a/toolbox/setprop.c b/toolbox/setprop.c
new file mode 100644
index 0000000..63ad2b4
--- /dev/null
+++ b/toolbox/setprop.c
@@ -0,0 +1,18 @@
+#include <stdio.h>
+
+#include <cutils/properties.h>
+
+int setprop_main(int argc, char *argv[])
+{
+    if(argc != 3) {
+        fprintf(stderr,"usage: setprop <key> <value>\n");
+        return 1;
+    }
+
+    if(property_set(argv[1], argv[2])){
+        fprintf(stderr,"could not set property\n");
+        return 1;
+    }
+
+    return 0;
+}
diff --git a/toolbox/setsebool.c b/toolbox/setsebool.c
new file mode 100644
index 0000000..4a3d87d
--- /dev/null
+++ b/toolbox/setsebool.c
@@ -0,0 +1,55 @@
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <selinux/selinux.h>
+#include <errno.h>
+
+static int do_setsebool(int nargs, char **args) {
+    SELboolean *b = alloca(nargs * sizeof(SELboolean));
+    char *v;
+    int i;
+
+    if (is_selinux_enabled() <= 0)
+        return 0;
+
+    for (i = 1; i < nargs; i++) {
+        char *name = args[i];
+        v = strchr(name, '=');
+        if (!v) {
+            fprintf(stderr, "setsebool: argument %s had no =\n", name);
+            return -1;
+        }
+        *v++ = 0;
+        b[i-1].name = name;
+        if (!strcmp(v, "1") || !strcasecmp(v, "true") || !strcasecmp(v, "on"))
+            b[i-1].value = 1;
+        else if (!strcmp(v, "0") || !strcasecmp(v, "false") || !strcasecmp(v, "off"))
+            b[i-1].value = 0;
+        else {
+            fprintf(stderr, "setsebool: invalid value %s\n", v);
+            return -1;
+        }
+    }
+
+    if (security_set_boolean_list(nargs - 1, b, 0) < 0)
+    {
+        fprintf(stderr, "setsebool: unable to set booleans: %s", strerror(errno));
+        return -1;
+    }
+
+    return 0;
+}
+
+int setsebool_main(int argc, char **argv)
+{
+    if (argc < 2) {
+        fprintf(stderr, "Usage:  %s name=value...\n", argv[0]);
+        exit(1);
+    }
+
+    return do_setsebool(argc, argv);
+}
diff --git a/toolbox/sleep.c b/toolbox/sleep.c
new file mode 100644
index 0000000..c09ae03
--- /dev/null
+++ b/toolbox/sleep.c
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2008, The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the 
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+static void
+usage(const char *s)
+{
+    fprintf(stderr, "USAGE: %s SECONDS\n", s);
+    exit(-1);
+}
+
+int sleep_main(int argc, char *argv[])
+{
+    unsigned long seconds;
+    char *endptr;
+
+    if (argc != 2) {
+        usage(argv[0]);
+    }
+
+    seconds = strtoul(argv[1], &endptr, 10);
+
+    if (endptr == argv[1]) {
+        usage(argv[0]);
+    }
+
+
+    sleep((unsigned int)seconds);
+   
+    return 0;
+}
+
+
diff --git a/toolbox/smd.c b/toolbox/smd.c
new file mode 100644
index 0000000..91e495c
--- /dev/null
+++ b/toolbox/smd.c
@@ -0,0 +1,41 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+
+int smd_main(int argc, char **argv)
+{
+    int fd, len, r, port = 0;
+    char devname[32];
+    argc--;
+    argv++;
+
+    if((argc > 0) && (argv[0][0] == '-')) {
+        port = atoi(argv[0] + 1);
+        argc--;
+        argv++;
+    }
+
+    sprintf(devname,"/dev/smd%d",port);
+    fd = open(devname, O_WRONLY);
+    if(fd < 0) {
+        fprintf(stderr,"failed to open smd0 - %s\n",
+            strerror(errno));
+        return -1;
+    }
+    while(argc > 0) {
+        len = strlen(argv[0]);
+        r = write(fd, argv[0], len);
+        if(r != len) {
+            fprintf(stderr,"failed to write smd0 (%d) %s\n",
+                r, strerror(errno));
+            return -1;
+        }
+        argc--;
+        argv++;
+        write(fd, argc ? " " : "\r", 1);
+    }
+    close(fd);
+    return 0;       
+}
diff --git a/toolbox/sntpd.c b/toolbox/sntpd.c
new file mode 100644
index 0000000..d100f13
--- /dev/null
+++ b/toolbox/sntpd.c
@@ -0,0 +1,679 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <unistd.h>
+#include <time.h>
+
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/prctl.h>
+
+#include <linux/capability.h>
+#include <linux/prctl.h>
+
+#include <netinet/in.h>
+#include <netdb.h>
+
+#include <cutils/properties.h>
+#include <private/android_filesystem_config.h>
+
+#define SNTP_DEBUG              0
+
+/* NTP packet format */
+#define REFERENCE_TIME_OFFSET   16
+#define ORIGINATE_TIME_OFFSET   24
+#define RECEIVE_TIME_OFFSET     32
+#define TRANSMIT_TIME_OFFSET    40
+#define NTP_PACKET_SIZE         48
+#define NTP_MODE_CLIENT         3
+#define NTP_MODE_UNICAST        4
+#define NTP_MODE_BROADCAST      5
+#define NTP_VERSION             3
+
+/* NTP port */
+#define NTP_PORT                123
+
+/* NTP service */
+#define NTP_SERVICE             "123"
+
+/* timeout value for udp response - 5 sec */
+#define NTP_TIMEOUT             (5*1000)
+
+/* wait for 1 sec before retry in case of failure */
+#define RETRY_DELAY             (1*1000)
+
+/* for the 1st time, wait for 1 sec before retry in case of failure */
+#define INITIAL_RETRY_DELAY     (1*1000)
+
+/* Exponential backoff for retries */
+#define RETRY_BACKOFF           (2)
+
+#define MAX_RETRY_DELAY         (1048 * 1000)
+
+/* update internal - 20 minutes (with jitter will be 10-20 minutes) */
+#define UPDATE_INTERVAL         (20*60*1000)
+
+/* threshold to update system time - 100 ms */
+#define UPDATE_THRESHOLD        (100ll)
+
+/* maximum time drift allowed to filter out response from malfunctioning server
+ * - 20 sec */
+#define MAX_TIME_DRIFT          (20ll*1000ll)
+
+/* Reject times which are this much older than the build time */
+#define MAX_BEFORE_BUILD_TIME   (60 * 60 * 1000)
+
+#define BUF_SIZE 128
+
+extern int capset(cap_user_header_t header, cap_user_data_t data);
+
+static const unsigned long long OFFSET_1900_TO_1970 =
+    ((365LL * 70LL) + 17LL) * 24LL * 60LL * 60LL;
+static const char *default_ntp_server = "time.google.com";
+
+static const char kPathSntpdLastSync[] = "/data/share/sntpd/last_sync";
+
+static int count_getaddrinfo_failures = 0;
+
+typedef enum {
+    TIME_SYNC_SUCCESS = 0,
+    TIME_SYNC_ADDRESS_RESOLUTION_FAILURE = -1,
+    TIME_SYNC_OTHER_FAILURE = -2
+} TimeSyncStatus;
+
+// Gets the current build time. Return 0 on success.
+static inline int get_build_time(struct timeval *tv) {
+    const char kPropBuildDate[] = "ro.build.date.utc";
+    char build_time_str[PROPERTY_VALUE_MAX];
+    if (property_get(kPropBuildDate, build_time_str, "") <= 0) {
+        fprintf(stderr, "Failed to get %s\n", kPropBuildDate);
+        return -1;
+    }
+
+    errno = 0;
+    unsigned long long parsed = strtoull(build_time_str, NULL, 0);
+    if (errno != 0) {
+        fprintf(stderr, "Failed to parse %s: %s\n", kPropBuildDate,
+                strerror(errno));
+        return -1;
+    }
+
+    tv->tv_sec = parsed;
+    tv->tv_usec = 0;
+    return 0;
+}
+
+static inline void record_localtime_str(struct timeval *tv, char *buf,
+                                        size_t buf_size) {
+    time_t nowtime = tv->tv_sec;
+    struct tm *nowtm = localtime(&nowtime);
+    size_t len = strftime(buf, buf_size, "%Y-%m-%d %H:%M:%S", nowtm);
+
+    int fd = open(kPathSntpdLastSync, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+    if (fd < 0) {
+        fprintf(stderr, "Failed to open %s: %s\n", kPathSntpdLastSync,
+                strerror(errno));
+        return;
+    }
+    if (write(fd, buf, len) != (ssize_t)len) {
+        fprintf(stderr, "Write file error: %s\n", strerror(errno));
+        close(fd);
+        return;
+    }
+    // NOTE(gfhuang): For unknown reason, without this fchmod workaround, the
+    // file permission stucks at 0600.
+    if (fchmod(fd, 0644) != 0) {
+        fprintf(stderr, "fchmod error: %s\n", strerror(errno));
+    }
+    close(fd);
+}
+
+/**
+* read an unsigned 32 bit big endian number from the given offset in the buffer.
+*/
+static inline unsigned int read32(unsigned char *buffer, int offset)
+{
+    int i;
+    unsigned int ret=0;
+
+    for (i = 0; i < 4; i ++) {
+        ret <<= 8;
+        ret |= (unsigned int) buffer[offset+i];
+    }
+    return ret;
+}
+
+/**
+* read the NTP time stamp at the given offset in the buffer and returns
+* it as a system time (milliseconds since January 1, 1970).
+*/
+static inline unsigned long long read_timestamp(unsigned char *buffer,
+        unsigned int offset)
+{
+    unsigned int seconds = read32(buffer, offset);
+    unsigned int fraction = read32(buffer, offset + 4);
+
+    return ((unsigned long long)seconds - OFFSET_1900_TO_1970) * 1000L
+        + ((unsigned long long)fraction * 1000L) / (1LL<<32);
+}
+
+
+/**
+* write system time (milliseconds since January 1, 1970) as an NTP time stamp
+* at the given offset in the buffer.
+*/
+static inline void write_timeStamp(unsigned char *buffer, int offset, unsigned long long time)
+{
+    unsigned int seconds = time / 1000L;
+    unsigned int milliseconds = time % 1000L;
+
+    seconds += OFFSET_1900_TO_1970;
+    // write seconds in big endian format
+    buffer[offset++] = (unsigned char)(seconds >> 24);
+    buffer[offset++] = (unsigned char)(seconds >> 16);
+    buffer[offset++] = (unsigned char)(seconds >> 8);
+    buffer[offset++] = (unsigned char)(seconds >> 0);
+
+    long fraction = milliseconds * (1LL<<32) / 1000L;
+    // write fraction in big endian format
+    buffer[offset++] = (unsigned char)(fraction >> 24);
+    buffer[offset++] = (unsigned char)(fraction >> 16);
+    buffer[offset++] = (unsigned char)(fraction >> 8);
+    buffer[offset++] = (unsigned char)(fraction >> 0);
+}
+
+#if SNTP_DEBUG
+static void dump_timestamp(unsigned long long stamp, const char *name)
+{
+    fprintf(stdout, "%20s:\t%08x%08x\n",
+            name,
+            (int)(stamp>>32),
+            (int)stamp);
+}
+#endif // SNTP_DEBUG
+
+/* convert timeval to millisecond */
+static inline unsigned long long tv_to_ms(struct timeval *tv)
+{
+    unsigned long long ret = (unsigned long long)
+                             tv->tv_sec * 1000L
+                             + (unsigned long long)tv->tv_usec / 1000LL;
+
+    return ret;
+}
+
+/* convert millisecond to timeval */
+static inline void ms_to_tv(struct timeval *tv, unsigned long long ms)
+{
+    tv->tv_sec = ms / 1000;
+    tv->tv_usec = 1000 * (ms % 1000);
+}
+
+/* convert timeval to millisecond */
+static inline unsigned long long ts_to_ms(const struct timespec *ts)
+{
+    return (unsigned long long)ts->tv_sec * 1000LL
+            + (unsigned long long)ts->tv_nsec / 1000000LL;
+}
+
+static uint64_t random_int64() {
+    uint64_t ret = random() | ((uint64_t)random() << 32);
+    FILE* f = fopen("/dev/urandom", "r");
+    if (!f) {
+        fprintf(stderr, "Warning: Failed to read from /dev/urandom %s\n",
+                strerror(errno));
+        return ret;
+    }
+    fread(&ret, 1, sizeof(ret), f);
+    fclose(f);
+    return ret;
+}
+
+typedef int(*sleep_fn)(const struct timespec *req, struct timespec *rem);
+static void sleep_ms_with_jitter(uint64_t time_ms, sleep_fn sleep_fn_ptr) {
+    uint64_t jitter = random_int64() % (time_ms / 2);
+    time_ms = (time_ms / 2) + jitter;
+
+    fprintf(stderr, "sleeping for %" PRId64 " milliseconds\n", time_ms);
+    struct timespec req = {
+        .tv_sec = time_ms / 1000,
+        .tv_nsec = (time_ms % 1000) * 1000000,
+    };
+    sleep_fn_ptr(&req, NULL /* rem */);
+}
+
+static TimeSyncStatus sntp_gettime(struct timeval *tv_upd)
+{
+    struct addrinfo hints;
+    struct addrinfo *res = 0, *rp = 0;
+    int s = -1;
+    TimeSyncStatus ret = TIME_SYNC_OTHER_FAILURE;
+
+    memset(&hints, 0, sizeof(hints));
+    hints.ai_family = AF_UNSPEC;
+    hints.ai_flags = AI_ADDRCONFIG;
+    hints.ai_socktype = SOCK_DGRAM;
+
+    /* get addrinfo */
+    s = getaddrinfo(default_ntp_server, NTP_SERVICE, &hints, &res);
+    if (s || !res) {
+        if (count_getaddrinfo_failures++ == 10) {
+            count_getaddrinfo_failures = 0;
+            fprintf(stderr, "error getaddrinfo: %s\n", gai_strerror(s));
+        }
+        return TIME_SYNC_ADDRESS_RESOLUTION_FAILURE;
+    }
+
+    /*
+     * pool.ntp.org is multihomed. Cycle through the server list until synced
+     * with one of them.
+     */
+    for (rp = res; rp != NULL; rp = rp->ai_next) {
+        if (rp->ai_family == AF_INET) {
+#if SNTP_DEBUG
+            struct sockaddr_in *s = (struct sockaddr_in*)rp->ai_addr;
+            char buf[INET_ADDRSTRLEN];
+            inet_ntop(AF_INET, &s->sin_addr, buf, sizeof(buf));
+            fprintf(stdout, "ipv4, %s\n", buf);
+#endif // SNTP_DEBUG
+        } else if (rp->ai_family == AF_INET6) {
+#if SNTP_DEBUG
+            struct sockaddr_in6 *s = (struct sockaddr_in6*)rp->ai_addr;
+            char buf[INET6_ADDRSTRLEN];
+            inet_ntop(AF_INET6, &s->sin6_addr, buf, sizeof(buf));
+            fprintf(stdout, "ipv6, %s\n", buf);
+#endif // SNTP_DEBUG
+        } else {
+            continue;
+        }
+
+        /* create socket */
+        int sock = socket(rp->ai_family, SOCK_DGRAM, IPPROTO_UDP);
+        if (sock == -1) {
+            fprintf(stderr, "error open socket: %s\n", strerror(errno));
+            continue;
+        }
+
+        /* set socket timeout */
+        struct timeval tv;
+        ms_to_tv(&tv, NTP_TIMEOUT);
+        if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0 ) {
+             fprintf(stderr, "error set socket option: %s\n",
+                     strerror(errno));
+             close(sock);
+             continue;
+        }
+
+        if (setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) < 0 ) {
+             fprintf(stderr, "error set socket option: %s\n",
+                     strerror(errno));
+             close(sock);
+             continue;
+        }
+
+        /* write NTP packet  */
+        unsigned char send_buffer[NTP_PACKET_SIZE] = {0};
+        send_buffer[0] = NTP_MODE_CLIENT | (NTP_VERSION << 3);
+        if (gettimeofday(&tv, NULL)) {
+            fprintf(stderr, "error gettimeofday: %s\n", strerror(errno));
+            close(sock);
+            continue;
+        }
+
+        /* get a monotonic request time */
+        struct timespec now;
+        if (clock_gettime(CLOCK_MONOTONIC, &now)) {
+            fprintf(stderr, "error clock_gettime: %s\n", strerror(errno));
+            close(sock);
+            continue;
+        }
+
+        unsigned long long request_time = tv_to_ms(&tv);
+        unsigned long long real_request_time = ts_to_ms(&now);
+
+        write_timeStamp(send_buffer, TRANSMIT_TIME_OFFSET, request_time);
+
+        /* send socket */
+        if (sendto(sock, send_buffer, NTP_PACKET_SIZE, 0,
+                   (const struct sockaddr *)rp->ai_addr,
+                   rp->ai_addrlen) == -1) {
+            fprintf (stderr, "error in sendto: %s\n", strerror(errno));
+            close(sock);
+            continue;
+        }
+
+        /* receive socket */
+        struct sockaddr_in recv_addr4 = {0};
+        struct sockaddr_in6 recv_addr6 = {0};
+        bool is_ipv4 = rp->ai_family == AF_INET;
+        struct sockaddr* sock_addr = is_ipv4 ?
+            (struct sockaddr*)&recv_addr4 : (struct sockaddr*)&recv_addr6;
+        socklen_t sockaddr_len = is_ipv4 ?
+            sizeof(recv_addr4) : sizeof(recv_addr6);
+
+        unsigned char buffer[NTP_PACKET_SIZE] = {0};
+        if (recvfrom(sock, buffer, NTP_PACKET_SIZE, 0,
+                     sock_addr,
+                     &sockaddr_len) != NTP_PACKET_SIZE) {
+            fprintf (stderr, "error in recvfrom %s\n", strerror(errno));
+            close(sock);
+            continue;
+        }
+
+        close(sock);
+
+        // https://tools.ietf.org/html/rfc4330#section-5
+        // 1.  When the IP source and destination addresses are available for
+        //     the client request, they should match the interchanged addresses
+        //     in the server reply.
+        // 2.  When the UDP source and destination ports are available for the
+        //     client request, they should match the interchanged ports in the
+        //     server reply.
+        bool matches_sender = true;
+        if (is_ipv4) {
+            struct sockaddr_in *s = (struct sockaddr_in*)rp->ai_addr;
+            matches_sender =
+                (memcmp(&s->sin_addr, &recv_addr4.sin_addr,
+                        sizeof(s->sin_addr)) == 0) &&
+                (s->sin_port == recv_addr4.sin_port);
+
+        } else {
+            struct sockaddr_in6 *s = (struct sockaddr_in6*)rp->ai_addr;
+            matches_sender =
+                (memcmp(&s->sin6_addr, &recv_addr6.sin6_addr,
+                        sizeof(s->sin6_addr)) == 0) &&
+                (s->sin6_port == recv_addr6.sin6_port);
+        }
+        if (!matches_sender) {
+            fprintf (stderr, "Recived packet doesn't match sender\n");
+            continue;
+        }
+
+        // https://tools.ietf.org/html/rfc4330#section-5
+        // 3.  The Originate Timestamp in the server reply should match the
+        //     Transmit Timestamp used in the client request.
+        //
+        // We compare the buffers directly because {read,write}_timestamp do not
+        // perfectly reverse each other.
+        if (memcmp(send_buffer + TRANSMIT_TIME_OFFSET,
+                   buffer + ORIGINATE_TIME_OFFSET, 8) != 0) {
+            fprintf(stderr, "Originate response doesn't match request time\n");
+            continue;
+        }
+
+        // https://tools.ietf.org/html/rfc4330#section-5
+        // 4.  The server reply should be discarded if any of the LI, Stratum,
+        //     or Transmit Timestamp fields is 0 or the Mode field is not 4
+        //     (unicast) or 5 (broadcast).
+        //
+        // We don't care about LI (Leap (second) Indicator) because time within
+        // 1 second is good enough for us.
+
+        uint8_t stratum = buffer[1];
+        if (stratum == 0) {
+            fprintf(stderr, "Stratum is zero\n");
+            continue;
+        }
+
+        bool is_transmit_zero = true;
+        for (int i = 0; i < 8; ++i) {
+            if (buffer[TRANSMIT_TIME_OFFSET + i] != 0) {
+                is_transmit_zero = false;
+                break;
+            }
+        }
+
+        if (is_transmit_zero) {
+            fprintf(stderr, "Transmit time is zero\n");
+            continue;
+        }
+
+        uint8_t mode = buffer[0] & 0x7;
+        if (mode != NTP_MODE_UNICAST && mode != NTP_MODE_BROADCAST) {
+            fprintf(stderr, "Mode is not server or broadcast: %" PRIu8 "\n",
+                    mode);
+            continue;
+        }
+
+
+        /* receiveTime = originateTime + transit + skew
+           responseTime = transmitTime + transit - skew
+           clockOffset = ((receiveTime - originateTime) + (transmitTime
+                         - responseTime))/2
+                       = ((originateTime + transit + skew - originateTime) +
+                         (transmitTime - (transmitTime + transit - skew)))/2
+                       = ((transit + skew) + (transmitTime - transmitTime -
+                         transit + skew))/2
+                       = (transit + skew - transit + skew)/2
+                       = (2 * skew)/2 = skew
+        */
+        /* get a monotonic respond time */
+        if (clock_gettime(CLOCK_MONOTONIC, &now)) {
+            fprintf(stderr, "error clock_gettime: %s\n", strerror(errno));
+            continue;
+        }
+
+        // These must be signed values in order for the calculation to work
+        // correctly. Signed 64 bits of milliseconds is still a long time.
+        //
+        // >>> 2**63 / (1000 * 60 * 60 * 24 * 365)
+        // 292471208L
+        long long response_time =
+            request_time + (ts_to_ms(&now) - real_request_time);
+        long long originate_time = read_timestamp(buffer,
+                                                  ORIGINATE_TIME_OFFSET);
+        long long receive_time = read_timestamp(buffer, RECEIVE_TIME_OFFSET);
+        long long transmit_time = read_timestamp(buffer, TRANSMIT_TIME_OFFSET);
+
+        // https://tools.ietf.org/html/rfc4330#section-5
+        //    Timestamp Name          ID   When Generated
+        //    ------------------------------------------------------------
+        //    Originate Timestamp     T1   time request sent by client
+        //    Receive Timestamp       T2   time request received by server
+        //    Transmit Timestamp      T3   time reply sent by server
+        //    Destination Timestamp   T4   time reply received by client
+        //
+        // The roundtrip delay d and system clock offset t are defined as:
+        //
+        //    d = (T4 - T1) - (T3 - T2)     t = ((T2 - T1) + (T3 - T4)) / 2.
+
+        // d cannot be negative.
+        long long d =
+            (response_time - originate_time) - (transmit_time - receive_time);
+        if (d < 0) {
+            fprintf(stderr, "d is negative\n");
+            continue;
+        }
+
+        long long t =
+            (receive_time - originate_time) + (transmit_time - response_time);
+        t /= 2;
+
+        long long updated_time = (long long)request_time + t;
+        if (updated_time < 0) {
+            fprintf(stderr, "Reported time is negative\n");
+            continue;
+        }
+
+        ms_to_tv(tv_upd, updated_time);
+
+        struct timeval tv_build_time;
+        if (get_build_time(&tv_build_time) == 0) {
+            long long build_time_ms = tv_to_ms(&tv_build_time);
+            if (updated_time + MAX_BEFORE_BUILD_TIME < build_time_ms) {
+                fprintf(stderr, "Reported time is too old\n");
+                continue;
+            }
+        }
+
+#if SNTP_DEBUG
+        int i;
+        for (i =0; i<NTP_PACKET_SIZE; i++) {
+            fprintf(stdout, "%02x", buffer[i]);
+            if ((i&15) == 15) {
+                fprintf(stdout, "\n");
+            }
+        }
+        dump_timestamp(originate_time, "originate");
+        dump_timestamp(receive_time, "receive");
+        dump_timestamp(transmit_time, "transmit");
+        dump_timestamp(request_time, "request");
+        dump_timestamp(response_time, "response");
+        printf("updated\n");
+        dump_timestamp(updated_time, "update");
+#endif
+        ret = TIME_SYNC_SUCCESS;
+        break;
+    }
+
+    freeaddrinfo(res);
+    return ret;
+}
+
+typedef TimeSyncStatus (*sntp_gettime_fn)(struct timeval *tv_upd);
+static TimeSyncStatus try_sync_time(bool synced, sntp_gettime_fn sntp_gettime_fn_ptr) {
+   struct timeval tv_upd;
+
+   TimeSyncStatus ret = sntp_gettime_fn_ptr(&tv_upd);
+   if (ret != TIME_SYNC_SUCCESS) {
+       return ret;
+   }
+
+   struct timeval tv_cur;
+   unsigned long long ms_cur, ms_upd, ms_abs_diff;
+
+   /* check threshold */
+   if (gettimeofday(&tv_cur, NULL)) {
+        fprintf(stderr, "error gettimeofday: %s\n",
+                strerror(errno));
+        return TIME_SYNC_OTHER_FAILURE;
+   }
+   ms_cur = tv_to_ms(&tv_cur);
+   ms_upd = tv_to_ms(&tv_upd);
+
+   // unsigned abs diff
+   if (ms_cur > ms_upd) {
+       ms_abs_diff = ms_cur - ms_upd;
+   } else {
+       ms_abs_diff = ms_upd - ms_cur;
+   }
+
+   /*
+    * always sync for the first time, since we don't have
+    * battery-backed RTC.
+    *
+    * update system time if out-of-sync exceeds threshold.
+    * rarely happens after initial sync (synced==1).
+    */
+   if (!synced || ms_abs_diff > UPDATE_THRESHOLD) {
+       /*
+        * discard spurious response from server if it diffs too much
+        * with local system time.
+        */
+       if (synced && ms_abs_diff >= MAX_TIME_DRIFT) {
+         fprintf(stderr, "discarding %ullms time drift from server. \n",
+                 ms_abs_diff);
+       } else if (settimeofday(&tv_upd, NULL)) {
+           fprintf(stderr, "error settimeofday: %s\n",
+                   strerror(errno));
+           return TIME_SYNC_OTHER_FAILURE;
+       } else {
+           char buf[BUF_SIZE];
+           record_localtime_str(&tv_upd, buf, BUF_SIZE);
+           fprintf(stderr, "synced at %s\n", buf);
+       }
+   } else {
+       char buf[BUF_SIZE];
+       record_localtime_str(&tv_cur, buf, BUF_SIZE);
+       fprintf(stderr, "no change, already synced as %s\n", buf);
+   }
+
+   return TIME_SYNC_SUCCESS;
+}
+
+typedef TimeSyncStatus(*sync_time_fn)(bool, sntp_gettime_fn);
+static void do_sync(bool* synced, unsigned long long* retry_delay,
+                    sync_time_fn sync_time_fn_ptr, sleep_fn sleep_fn_ptr,
+                    sntp_gettime_fn sntp_gettime_fn_ptr) {
+    TimeSyncStatus sync_time_result = sync_time_fn_ptr(*synced, sntp_gettime_fn_ptr);
+    if (sync_time_result == TIME_SYNC_SUCCESS) {
+      *synced = true;
+      *retry_delay = RETRY_DELAY;
+      sleep_ms_with_jitter(UPDATE_INTERVAL, sleep_fn_ptr);
+    } else if (sync_time_result == TIME_SYNC_ADDRESS_RESOLUTION_FAILURE) {
+      /* Failure because of Internet offline, keeps retrying*/
+      sleep_ms_with_jitter(*retry_delay, sleep_fn_ptr);
+    } else {
+      /* Failure because of other reasons, try exponential backoff */
+      sleep_ms_with_jitter(*retry_delay, sleep_fn_ptr);
+      if (*retry_delay < MAX_RETRY_DELAY) {
+        *retry_delay *= RETRY_BACKOFF;
+      }
+    }
+}
+
+#ifdef STANDALONE
+int main(int argc, char *argv[])
+#else
+int sntpd_main(int argc, char *argv[])
+#endif
+{
+    (void)argc;
+
+    struct __user_cap_header_struct header;
+    struct __user_cap_data_struct cap[_LINUX_CAPABILITY_U32S_3];
+    bool synced = false;
+
+    /* only can run as kernel */
+    if (getuid() != 0) {
+        fprintf(stderr, "%s needs root to run\n", argv[0]);
+        return -1;
+    }
+
+    /* setuid and keep capability  */
+    prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
+
+    setgid(AID_INET);
+    setuid(AID_SNTPD);
+
+    header.version = _LINUX_CAPABILITY_VERSION_3;
+    header.pid = 0;
+    memset(cap, 0, sizeof(cap));
+    int idx = CAP_TO_INDEX(CAP_SYS_TIME);
+    cap[idx].effective = CAP_TO_MASK(CAP_SYS_TIME);
+    cap[idx].permitted = CAP_TO_MASK(CAP_SYS_TIME);
+    cap[idx].inheritable = 0;
+    if (capset(&header, cap)) {
+        fprintf(stderr, "setcap error: %s\n", strerror(errno));
+        return -1;
+    }
+
+    unsigned long long retry_delay = INITIAL_RETRY_DELAY;
+
+    while (1) {
+        do_sync(&synced, &retry_delay, try_sync_time, nanosleep, sntp_gettime);
+    }
+    return 0;
+}
diff --git a/toolbox/sntpd_unittest.c b/toolbox/sntpd_unittest.c
new file mode 100644
index 0000000..df26de1
--- /dev/null
+++ b/toolbox/sntpd_unittest.c
@@ -0,0 +1,169 @@
+#include <assert.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <sys/types.h>
+#include <time.h>
+
+#include "sntpd.c"
+
+static uint64_t g_total_sleep_ms = 0;
+static struct timeval g_updated_tv;
+static struct timeval g_tv_to_return;
+
+//override nanosleep
+int nanosleep_mock(const struct timespec *tv_up, struct timespec *rem) {
+  (void)rem;
+  g_total_sleep_ms += ts_to_ms(tv_up);
+  return 0;
+}
+
+//override settimeofday
+int settimeofday (const struct timeval* tv,
+                  const struct timezone* tz) {
+  assert(tv);
+  g_updated_tv = *tv;
+  return 0;
+}
+
+int try_sync_time_mock_error(bool synced, sntp_gettime_fn sntp_gettime_fn_ptr) {
+  return -1;
+}
+
+int try_sync_time_mock_success(bool synced, sntp_gettime_fn sntp_gettime_fn_ptr) {
+  return 0;
+}
+
+int sntp_gettime_mock(struct timeval *tv_upd) {
+  assert(tv_upd);
+  *tv_upd = g_tv_to_return;
+  return 0;
+}
+
+void test_sync_loop_on_error() {
+  g_total_sleep_ms = 0;
+  bool synced = false;
+  unsigned long long retry_delay = INITIAL_RETRY_DELAY;
+  for(int i = 0; i < 20; i++) {
+    do_sync(&synced, &retry_delay, try_sync_time_mock_error, nanosleep_mock, NULL);
+  }
+  fprintf(stdout, "Total sleep time is %" PRId64 " ms.\n", g_total_sleep_ms);
+  assert(g_total_sleep_ms >= 10239500 && g_total_sleep_ms < 20479000);
+}
+
+void test_sync_loop_on_success() {
+  g_total_sleep_ms = 0;
+  bool synced = false;
+  unsigned long long retry_delay = INITIAL_RETRY_DELAY;
+  for(int i = 0; i < 20; i++) {
+    do_sync(&synced, &retry_delay, try_sync_time_mock_success, nanosleep_mock, NULL);
+  }
+  fprintf(stdout, "Total sleep time is %" PRId64 " ms.\n", g_total_sleep_ms);
+  assert(g_total_sleep_ms >= 12000000 && g_total_sleep_ms < 24000000);
+}
+
+void test_try_sync_time() {
+  //Set the time when synced is false.
+  unsigned long long time_ms;
+  g_updated_tv.tv_sec = 0;
+  g_updated_tv.tv_usec = 0;
+  g_tv_to_return.tv_usec = 0;
+  g_tv_to_return.tv_sec = 0;
+  int rv = try_sync_time(false, sntp_gettime_mock);
+  assert(rv == 0);
+  assert(g_updated_tv.tv_sec == 0);
+  assert(g_updated_tv.tv_usec == 0);
+
+  //Synced is true, and diff > MAX_TIME_DRIFT
+  //settimeofday should not be called.
+  g_updated_tv.tv_sec = 1234;
+  g_updated_tv.tv_usec = 123456;
+  g_tv_to_return.tv_sec = 0;
+  g_tv_to_return.tv_usec = 0;
+  rv = try_sync_time(true, sntp_gettime_mock);
+  assert(rv == 0);
+  assert(g_updated_tv.tv_sec == 1234);
+  assert(g_updated_tv.tv_usec == 123456);
+
+  //Synced is true, local clock lagging, diff < UPDATE_INTERVAL
+  //settimeofday should not be called.
+  g_updated_tv.tv_sec = 1234;
+  g_updated_tv.tv_usec = 123456;
+  assert(!gettimeofday(&g_tv_to_return, NULL));
+  time_ms = tv_to_ms(&g_tv_to_return);
+  time_ms += 456;
+  ms_to_tv(&g_tv_to_return, time_ms);
+  rv = try_sync_time(true, sntp_gettime_mock);
+  assert(rv == 0);
+  assert(g_updated_tv.tv_sec == 1234);
+  assert(g_updated_tv.tv_usec == 123456);
+
+  //Synced is true, local clock ahead, diff < UPDATE_INTERVAL
+  //settimeofday should not be called.
+  g_updated_tv.tv_sec = 1234;
+  g_updated_tv.tv_usec = 123456;
+  assert(!gettimeofday(&g_tv_to_return, NULL));
+  time_ms = tv_to_ms(&g_tv_to_return);
+  time_ms -= 456;
+  ms_to_tv(&g_tv_to_return, time_ms);
+  rv = try_sync_time(true, sntp_gettime_mock);
+  assert(rv == 0);
+  assert(g_updated_tv.tv_sec == 1234);
+  assert(g_updated_tv.tv_usec == 123456);
+
+  //Synced is true, local clock lagging, UPDATE_INTERVAL < diff < MAX_TIME_DRIFT
+  g_updated_tv.tv_sec = 1234;
+  g_updated_tv.tv_usec = 123456;
+  assert(!gettimeofday(&g_tv_to_return, NULL));
+  time_ms = tv_to_ms(&g_tv_to_return);
+  time_ms += 501;
+  ms_to_tv(&g_tv_to_return, time_ms);
+  rv = try_sync_time(true, sntp_gettime_mock);
+  assert(rv == 0);
+  assert(g_updated_tv.tv_sec == g_tv_to_return.tv_sec);
+  assert(g_updated_tv.tv_usec == g_tv_to_return.tv_usec);
+
+  //Synced is true, local clock ahead, UPDATE_INTERVAL < diff < MAX_TIME_DRIFT
+  g_updated_tv.tv_sec = 1234;
+  g_updated_tv.tv_usec = 123456;
+  assert(!gettimeofday(&g_tv_to_return, NULL));
+  time_ms = tv_to_ms(&g_tv_to_return);
+  time_ms -= 501;
+  ms_to_tv(&g_tv_to_return, time_ms);
+  rv = try_sync_time(true, sntp_gettime_mock);
+  assert(rv == 0);
+  assert(g_updated_tv.tv_sec == g_tv_to_return.tv_sec);
+  assert(g_updated_tv.tv_usec == g_tv_to_return.tv_usec);
+
+  //Synced is true, local clock ahead, UPDATE_INTERVAL < diff < MAX_TIME_DRIFT
+  g_updated_tv.tv_sec = 1234;
+  g_updated_tv.tv_usec = 123456;
+  assert(!gettimeofday(&g_tv_to_return, NULL));
+  time_ms = tv_to_ms(&g_tv_to_return);
+  time_ms -= 17975;
+  ms_to_tv(&g_tv_to_return, time_ms);
+  rv = try_sync_time(true, sntp_gettime_mock);
+  assert(rv == 0);
+  assert(g_updated_tv.tv_sec == g_tv_to_return.tv_sec);
+  assert(g_updated_tv.tv_usec == g_tv_to_return.tv_usec);
+
+  //Synced is true, local clock lagging, UPDATE_INTERVAL < diff < MAX_TIME_DRIFT
+  g_updated_tv.tv_sec = 1234;
+  g_updated_tv.tv_usec = 123456;
+  assert(!gettimeofday(&g_tv_to_return, NULL));
+  time_ms = tv_to_ms(&g_tv_to_return);
+  time_ms += 11221;
+  ms_to_tv(&g_tv_to_return, time_ms);
+  rv = try_sync_time(true, sntp_gettime_mock);
+  assert(rv == 0);
+  assert(g_updated_tv.tv_sec == g_tv_to_return.tv_sec);
+  assert(g_updated_tv.tv_usec == g_tv_to_return.tv_usec);
+}
+
+int main(int argc, char *argv[]) {
+  (void)argc;
+  (void)argv;
+  test_sync_loop_on_success();
+  test_sync_loop_on_error();
+  test_try_sync_time();
+  return 0;
+}
diff --git a/toolbox/start.c b/toolbox/start.c
new file mode 100644
index 0000000..665a941
--- /dev/null
+++ b/toolbox/start.c
@@ -0,0 +1,21 @@
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <cutils/properties.h>
+
+int start_main(int argc, char *argv[])
+{
+    char buf[1024];
+
+    if(argc > 1) {
+        property_set("ctl.start", argv[1]);
+    } else {
+        /* defaults to starting the common services stopped by stop.c */
+        property_set("ctl.start", "surfaceflinger");
+        property_set("ctl.start", "zygote");
+    }
+
+    return 0;
+}
diff --git a/toolbox/stop.c b/toolbox/stop.c
new file mode 100644
index 0000000..460f377
--- /dev/null
+++ b/toolbox/stop.c
@@ -0,0 +1,19 @@
+#include <stdio.h>
+#include <string.h>
+
+#include <cutils/properties.h>
+
+int stop_main(int argc, char *argv[])
+{
+    char buf[1024];
+
+    if(argc > 1) {
+        property_set("ctl.stop", argv[1]);
+    } else{
+        /* defaults to stopping the common services */
+        property_set("ctl.stop", "zygote");
+        property_set("ctl.stop", "surfaceflinger");
+    }
+
+    return 0;
+}
diff --git a/toolbox/sudo.c b/toolbox/sudo.c
new file mode 100644
index 0000000..bec08ef
--- /dev/null
+++ b/toolbox/sudo.c
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <grp.h>
+
+#include <private/android_filesystem_config.h>
+
+/* we support up to 32 supplementary group ID */
+#define NR_SUPP_GIDS 32
+
+static void usage(int argc, char *argv[])
+{
+    fprintf(stdout,
+        "Usage: sudo -u UID [-g GID[:GID]...] FILE [ARG] ...\n");
+}
+
+int sudo_main(int argc, char *argv[])
+{
+    uid_t uid = -1, myuid;
+    gid_t gid = -1, supp_gids[NR_SUPP_GIDS];
+    int nr_supp_gids = 0, i, index;
+
+    /* only root can sudo */
+    myuid = getuid();
+    if (myuid != 0) {
+        fprintf(stderr, "sudo: only root can sudo, %d is not allowed", myuid);
+        exit(-1);
+    }
+
+    if(argc < 4) {
+        usage(argc, argv);
+        exit(-1);
+    }
+
+    index = 1;
+    while (index < argc - 1) {
+        if (!strcmp("-u", argv[index])) {
+            /* parse uid */
+            char *user = argv[++index];
+            struct passwd *pw;
+
+            pw = getpwnam(user);
+            if (pw == 0) {
+                fprintf(stderr, "%s not a valid user\n", user);
+                exit(-1);
+            } else {
+                uid = pw->pw_uid;
+                gid = pw->pw_gid;
+                setenv("HOME", pw->pw_dir, 1);
+            }
+        } else if (!strcmp("-g", argv[index])) {
+            /* parse (supplementary) gids */
+            char * current = argv[++index], *next;
+            struct group *grp;
+
+            for (i = 0; current && (i < NR_SUPP_GIDS); ++i) {
+                next = strchr(current, ':');
+                if (next) {
+                    *next++ = '\0';
+                }
+
+                grp = getgrnam(current);
+                if (grp == NULL) {
+                    fprintf(stderr, "%s is not a valid group\n", current);
+                    exit(-1);
+                } else {
+                    supp_gids[i] = grp->gr_gid;
+                }
+                current = next;
+            }
+            nr_supp_gids = i;
+        } else {
+            break;
+        }
+        ++index;
+    }
+
+    if (uid == (uid_t)-1 || uid == 0) {
+        usage (argc, argv);
+        exit(-1);
+    }
+
+    /* set supplemtary groups */
+    if (nr_supp_gids && setgroups(nr_supp_gids, supp_gids)) {
+        fprintf(stderr, "sudo: setgroups error %s\n", strerror(errno));
+        exit(-1);
+    }
+
+    /* set uid and gid */
+    if (setgid(gid) || setuid(uid)) {
+        fprintf(stderr, "sudo: %s\n", strerror(errno));
+        exit(-1);
+    }
+
+    if (index == argc) {
+        usage(argc, argv);
+        exit(-1);
+    } else {
+        /* Copy the rest of the args from main. */
+        int nr_args = argc - index;
+        char **exec_args = (char **)malloc(sizeof(char*)
+            * (nr_args + 1));
+        if (!exec_args) {
+            fprintf(stderr, "memory alloc error %s\n", strerror(errno));
+            exit(-1);
+        }
+        memcpy(exec_args, &argv[index], sizeof(*exec_args) * nr_args);
+        exec_args[nr_args] = NULL;
+
+        if (execvp(argv[index], exec_args)) {
+            fprintf(stderr, "%s: %s exec failed\n", argv[0], argv[index]);
+        }
+    }
+
+    return -1;
+}
diff --git a/toolbox/sync.c b/toolbox/sync.c
new file mode 100644
index 0000000..8284276
--- /dev/null
+++ b/toolbox/sync.c
@@ -0,0 +1,7 @@
+#include <unistd.h>
+
+int sync_main(int argc, char **argv)
+{
+	sync();
+	return 0;
+}
diff --git a/toolbox/syren.c b/toolbox/syren.c
new file mode 100644
index 0000000..06e329e
--- /dev/null
+++ b/toolbox/syren.c
@@ -0,0 +1,154 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <malloc.h>
+
+/* ioctl crap */
+#define SYREN_RD		101
+#define SYREN_WR		102
+#define SYREN_OLD_RD	108
+#define SYREN_OLD_WR	109
+
+struct syren_io_args {
+	unsigned long	page;
+	unsigned long	addr;
+	unsigned long	value;
+};
+
+typedef struct {
+	u_char			page;
+	u_char			addr;
+	const char		*name;
+} syren_reg;
+
+static syren_reg registers[] = {
+	{ 0, 0x04, "TOGBR1" },
+	{ 0, 0x05, "TOGBR2" },
+	{ 0, 0x06, "VBDCTRL" },
+	{ 1, 0x07, "VBUCTRL" },
+	{ 1, 0x08, "VBCTRL" },
+	{ 1, 0x09, "PWDNRG" },
+	{ 1, 0x0a, "VBPOP" },
+	{ 1, 0x0b, "VBCTRL2" },
+	{ 1, 0x0f, "VAUDCTRL" },
+	{ 1, 0x10, "VAUSCTRL" },
+	{ 1, 0x11, "VAUOCTRL" },
+	{ 1, 0x12, "VAUDPLL" },
+	{ 1, 0x17, "VRPCSIMR" },
+	{ 0, 0, 0 }
+};
+
+static syren_reg *find_reg(const char *name)
+{
+	int i;
+
+	for (i = 0; registers[i].name != 0; i++) {
+		if (!strcasecmp(registers[i].name, name))
+			return &registers[i];
+	}
+
+	return NULL;
+}
+
+static int usage(void)
+{
+	fprintf(stderr, "usage: syren [r/w] [REGNAME | page:addr] (value)\n");
+	return 1;
+}
+
+int
+syren_main(int argc, char **argv)
+{
+	int cmd = -1;
+	syren_reg *r;
+	struct syren_io_args sio;
+	char name[32];
+	int fd;
+
+	if (argc < 3) {
+		return usage();
+	}
+
+	switch(argv[1][0]) {
+	case 'r':
+		cmd = SYREN_RD;
+		break;
+	case 'w':
+		cmd = SYREN_WR;
+		break;
+	case 'R':
+		cmd = SYREN_OLD_RD;
+		break;
+	case 'W':
+		cmd = SYREN_OLD_WR;
+		break;
+	default:
+		return usage();
+	}
+
+	if (cmd == SYREN_WR || cmd == SYREN_OLD_WR) {
+		if (argc < 4)
+			return usage();
+		sio.value = strtoul(argv[3], 0, 0);
+	}
+
+	fd = open("/dev/eac", O_RDONLY);
+	if (fd < 0) {
+		fprintf(stderr, "can't open /dev/eac\n");
+		return 1;
+	}
+
+	if (strcasecmp(argv[2], "all") == 0) {
+		int i;
+		if (cmd != SYREN_RD && cmd != SYREN_OLD_RD) {
+			fprintf(stderr, "can only read all registers\n");
+			return 1;
+		}
+
+		for (i = 0; registers[i].name; i++) {
+			sio.page = registers[i].page;
+			sio.addr = registers[i].addr;
+			if (ioctl(fd, cmd, &sio) < 0) {
+				fprintf(stderr, "%s: error\n", registers[i].name);
+			} else {
+				fprintf(stderr, "%s: %04x\n", registers[i].name, sio.value);
+			}
+		}
+
+		close(fd);
+		return 0;
+	}
+
+	r = find_reg(argv[2]);
+	if (r == NULL) {
+		strcpy(name, argv[2]);
+		char *addr_str = strchr(argv[2], ':');
+		if (addr_str == NULL)
+			return usage();
+		*addr_str++ = 0;
+		sio.page = strtoul(argv[2], 0, 0);
+		sio.addr = strtoul(addr_str, 0, 0);
+	} else {
+		strcpy(name, r->name);
+		sio.page = r->page;
+		sio.addr = r->addr;
+	}
+
+	if (ioctl(fd, cmd, &sio) < 0) {
+		fprintf(stderr, "ioctl(%d) failed\n", cmd);
+		return 1;
+	}
+
+	if (cmd == SYREN_RD || cmd == SYREN_OLD_RD) {
+		printf("%s: %04x\n", name, sio.value);
+	} else {
+		printf("wrote %04x to %s\n", sio.value, name);
+	}
+
+	close(fd);
+
+	return 0;
+}
+
diff --git a/toolbox/toolbox.c b/toolbox/toolbox.c
new file mode 100644
index 0000000..0eac390
--- /dev/null
+++ b/toolbox/toolbox.c
@@ -0,0 +1,57 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+int main(int, char **);
+
+static int toolbox_main(int argc, char **argv)
+{
+    // "toolbox foo ..." is equivalent to "foo ..."
+    if (argc > 1) {
+        return main(argc - 1, argv + 1);
+    } else {
+        printf("Toolbox!\n");
+        return 0;
+    }
+}
+
+#define TOOL(name) int name##_main(int, char**);
+#include "tools.h"
+#undef TOOL
+
+static struct 
+{
+    const char *name;
+    int (*func)(int, char**);
+} tools[] = {
+    { "toolbox", toolbox_main },
+#define TOOL(name) { #name, name##_main },
+#include "tools.h"
+#undef TOOL
+    { 0, 0 },
+};
+
+int main(int argc, char **argv)
+{
+    int i;
+    char *name = argv[0];
+
+    if((argc > 1) && (argv[1][0] == '@')) {
+        name = argv[1] + 1;
+        argc--;
+        argv++;
+    } else {
+        char *cmd = strrchr(argv[0], '/');
+        if (cmd)
+            name = cmd + 1;
+    }
+
+    for(i = 0; tools[i].name; i++){
+        if(!strcmp(tools[i].name, name)){
+            return tools[i].func(argc, argv);
+        }
+    }
+
+    printf("%s: no such tool\n", argv[0]);
+    return -1;
+}
diff --git a/toolbox/top.c b/toolbox/top.c
new file mode 100644
index 0000000..b92948a
--- /dev/null
+++ b/toolbox/top.c
@@ -0,0 +1,604 @@
+/*
+ * Copyright (c) 2008, The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the 
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <ctype.h>
+#include <dirent.h>
+#include <grp.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <cutils/sched_policy.h>
+
+struct cpu_info {
+    long unsigned utime, ntime, stime, itime;
+    long unsigned iowtime, irqtime, sirqtime;
+};
+
+#define PROC_NAME_LEN 64
+#define THREAD_NAME_LEN 32
+
+struct proc_info {
+    struct proc_info *next;
+    pid_t pid;
+    pid_t tid;
+    uid_t uid;
+    gid_t gid;
+    char name[PROC_NAME_LEN];
+    char tname[THREAD_NAME_LEN];
+    char state;
+    long unsigned utime;
+    long unsigned stime;
+    long unsigned delta_utime;
+    long unsigned delta_stime;
+    long unsigned delta_time;
+    long vss;
+    long rss;
+    int prs;
+    int num_threads;
+    char policy[32];
+};
+
+struct proc_list {
+    struct proc_info **array;
+    int size;
+};
+
+#define die(...) { fprintf(stderr, __VA_ARGS__); exit(EXIT_FAILURE); }
+
+#define INIT_PROCS 50
+#define THREAD_MULT 8
+static struct proc_info **old_procs, **new_procs;
+static int num_old_procs, num_new_procs;
+static struct proc_info *free_procs;
+static int num_used_procs, num_free_procs;
+
+static int max_procs, delay, iterations, threads;
+static char *uid_filter, *gid_filter;
+
+static struct cpu_info old_cpu, new_cpu;
+
+static struct proc_info *alloc_proc(void);
+static void free_proc(struct proc_info *proc);
+static void read_procs(void);
+static int read_stat(char *filename, struct proc_info *proc);
+static void read_policy(int pid, struct proc_info *proc);
+static void add_proc(int proc_num, struct proc_info *proc);
+static int read_cmdline(char *filename, struct proc_info *proc);
+static int read_status(char *filename, struct proc_info *proc);
+static void print_procs(void);
+static struct proc_info *find_old_proc(pid_t pid, pid_t tid);
+static void free_old_procs(void);
+static int (*proc_cmp)(const void *a, const void *b);
+static int proc_cpu_cmp(const void *a, const void *b);
+static int proc_vss_cmp(const void *a, const void *b);
+static int proc_rss_cmp(const void *a, const void *b);
+static int proc_thr_cmp(const void *a, const void *b);
+static int numcmp(long long a, long long b);
+static void usage(char *cmd);
+
+int top_main(int argc, char *argv[]) {
+    int i;
+
+    num_used_procs = num_free_procs = 0;
+
+    max_procs = 0;
+    delay = 3;
+    iterations = -1;
+    proc_cmp = &proc_cpu_cmp;
+    for (i = 1; i < argc; i++) {
+        if (!strcmp(argv[i], "-m")) {
+            if (i + 1 >= argc) {
+                fprintf(stderr, "Option -m expects an argument.\n");
+                usage(argv[0]);
+                exit(EXIT_FAILURE);
+            }
+            max_procs = atoi(argv[++i]);
+            continue;
+        }
+        if (!strcmp(argv[i], "-n")) {
+            if (i + 1 >= argc) {
+                fprintf(stderr, "Option -n expects an argument.\n");
+                usage(argv[0]);
+                exit(EXIT_FAILURE);
+            }
+            iterations = atoi(argv[++i]);
+            continue;
+        }
+        if (!strcmp(argv[i], "-d")) {
+            if (i + 1 >= argc) {
+                fprintf(stderr, "Option -d expects an argument.\n");
+                usage(argv[0]);
+                exit(EXIT_FAILURE);
+            }
+            delay = atoi(argv[++i]);
+            continue;
+        }
+        if (!strcmp(argv[i], "-u")) {
+            if (i + 1 >= argc) {
+                fprintf(stderr, "Option -u expects an argument.\n");
+                usage(argv[0]);
+                exit(EXIT_FAILURE);
+            }
+            uid_filter = argv[++i];
+            continue;
+        }
+        if (!strcmp(argv[i], "-g")) {
+            if (i + 1 >= argc) {
+                fprintf(stderr, "Option -g expects an argument.\n");
+                usage(argv[0]);
+                exit(EXIT_FAILURE);
+            }
+            gid_filter = argv[++i];
+            continue;
+        }
+        if (!strcmp(argv[i], "-s")) {
+            if (i + 1 >= argc) {
+                fprintf(stderr, "Option -s expects an argument.\n");
+                usage(argv[0]);
+                exit(EXIT_FAILURE);
+            }
+            ++i;
+            if (!strcmp(argv[i], "cpu")) { proc_cmp = &proc_cpu_cmp; continue; }
+            if (!strcmp(argv[i], "vss")) { proc_cmp = &proc_vss_cmp; continue; }
+            if (!strcmp(argv[i], "rss")) { proc_cmp = &proc_rss_cmp; continue; }
+            if (!strcmp(argv[i], "thr")) { proc_cmp = &proc_thr_cmp; continue; }
+            fprintf(stderr, "Invalid argument \"%s\" for option -s.\n", argv[i]);
+            exit(EXIT_FAILURE);
+        }
+        if (!strcmp(argv[i], "-t")) { threads = 1; continue; }
+        if (!strcmp(argv[i], "-h")) {
+            usage(argv[0]);
+            exit(EXIT_SUCCESS);
+        }
+        fprintf(stderr, "Invalid argument \"%s\".\n", argv[i]);
+        usage(argv[0]);
+        exit(EXIT_FAILURE);
+    }
+
+    if (threads && proc_cmp == &proc_thr_cmp) {
+        fprintf(stderr, "Sorting by threads per thread makes no sense!\n");
+        exit(EXIT_FAILURE);
+    }
+
+    free_procs = NULL;
+
+    num_new_procs = num_old_procs = 0;
+    new_procs = old_procs = NULL;
+
+    read_procs();
+    while ((iterations == -1) || (iterations-- > 0)) {
+        old_procs = new_procs;
+        num_old_procs = num_new_procs;
+        memcpy(&old_cpu, &new_cpu, sizeof(old_cpu));
+        sleep(delay);
+        read_procs();
+        print_procs();
+        free_old_procs();
+    }
+
+    return 0;
+}
+
+static struct proc_info *alloc_proc(void) {
+    struct proc_info *proc;
+
+    if (free_procs) {
+        proc = free_procs;
+        free_procs = free_procs->next;
+        num_free_procs--;
+    } else {
+        proc = malloc(sizeof(*proc));
+        if (!proc) die("Could not allocate struct process_info.\n");
+    }
+
+    num_used_procs++;
+
+    return proc;
+}
+
+static void free_proc(struct proc_info *proc) {
+    proc->next = free_procs;
+    free_procs = proc;
+
+    num_used_procs--;
+    num_free_procs++;
+}
+
+#define MAX_LINE 256
+
+static void read_procs(void) {
+    DIR *proc_dir, *task_dir;
+    struct dirent *pid_dir, *tid_dir;
+    char filename[64];
+    FILE *file;
+    int proc_num;
+    struct proc_info *proc;
+    pid_t pid, tid;
+
+    int i;
+
+    proc_dir = opendir("/proc");
+    if (!proc_dir) die("Could not open /proc.\n");
+
+    new_procs = calloc(INIT_PROCS * (threads ? THREAD_MULT : 1), sizeof(struct proc_info *));
+    num_new_procs = INIT_PROCS * (threads ? THREAD_MULT : 1);
+
+    file = fopen("/proc/stat", "r");
+    if (!file) die("Could not open /proc/stat.\n");
+    fscanf(file, "cpu  %lu %lu %lu %lu %lu %lu %lu", &new_cpu.utime, &new_cpu.ntime, &new_cpu.stime,
+            &new_cpu.itime, &new_cpu.iowtime, &new_cpu.irqtime, &new_cpu.sirqtime);
+    fclose(file);
+
+    proc_num = 0;
+    while ((pid_dir = readdir(proc_dir))) {
+        if (!isdigit(pid_dir->d_name[0]))
+            continue;
+
+        pid = atoi(pid_dir->d_name);
+        
+        struct proc_info cur_proc;
+        
+        if (!threads) {
+            proc = alloc_proc();
+
+            proc->pid = proc->tid = pid;
+
+            sprintf(filename, "/proc/%d/stat", pid);
+            read_stat(filename, proc);
+
+            sprintf(filename, "/proc/%d/cmdline", pid);
+            read_cmdline(filename, proc);
+
+            sprintf(filename, "/proc/%d/status", pid);
+            read_status(filename, proc);
+
+            read_policy(pid, proc);
+
+            proc->num_threads = 0;
+        } else {
+            sprintf(filename, "/proc/%d/cmdline", pid);
+            read_cmdline(filename, &cur_proc);
+
+            sprintf(filename, "/proc/%d/status", pid);
+            read_status(filename, &cur_proc);
+            
+            proc = NULL;
+        }
+
+        sprintf(filename, "/proc/%d/task", pid);
+        task_dir = opendir(filename);
+        if (!task_dir) continue;
+
+        while ((tid_dir = readdir(task_dir))) {
+            if (!isdigit(tid_dir->d_name[0]))
+                continue;
+
+            if (threads) {
+                tid = atoi(tid_dir->d_name);
+
+                proc = alloc_proc();
+
+                proc->pid = pid; proc->tid = tid;
+
+                sprintf(filename, "/proc/%d/task/%d/stat", pid, tid);
+                read_stat(filename, proc);
+
+                read_policy(tid, proc);
+
+                strcpy(proc->name, cur_proc.name);
+                proc->uid = cur_proc.uid;
+                proc->gid = cur_proc.gid;
+
+                add_proc(proc_num++, proc);
+            } else {
+                proc->num_threads++;
+            }
+        }
+
+        closedir(task_dir);
+        
+        if (!threads)
+            add_proc(proc_num++, proc);
+    }
+
+    for (i = proc_num; i < num_new_procs; i++)
+        new_procs[i] = NULL;
+
+    closedir(proc_dir);
+}
+
+static int read_stat(char *filename, struct proc_info *proc) {
+    FILE *file;
+    char buf[MAX_LINE], *open_paren, *close_paren;
+    int res, idx;
+
+    file = fopen(filename, "r");
+    if (!file) return 1;
+    fgets(buf, MAX_LINE, file);
+    fclose(file);
+
+    /* Split at first '(' and last ')' to get process name. */
+    open_paren = strchr(buf, '(');
+    close_paren = strrchr(buf, ')');
+    if (!open_paren || !close_paren) return 1;
+
+    *open_paren = *close_paren = '\0';
+    strncpy(proc->tname, open_paren + 1, THREAD_NAME_LEN);
+    proc->tname[THREAD_NAME_LEN-1] = 0;
+    
+    /* Scan rest of string. */
+    sscanf(close_paren + 1, " %c %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d "
+                 "%lu %lu %*d %*d %*d %*d %*d %*d %*d %lu %ld "
+                 "%*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %d",
+                 &proc->state, &proc->utime, &proc->stime, &proc->vss, &proc->rss, &proc->prs);
+
+    return 0;
+}
+
+static void add_proc(int proc_num, struct proc_info *proc) {
+    int i;
+
+    if (proc_num >= num_new_procs) {
+        new_procs = realloc(new_procs, 2 * num_new_procs * sizeof(struct proc_info *));
+        if (!new_procs) die("Could not expand procs array.\n");
+        for (i = num_new_procs; i < 2 * num_new_procs; i++)
+            new_procs[i] = NULL;
+        num_new_procs = 2 * num_new_procs;
+    }
+    new_procs[proc_num] = proc;
+}
+
+static int read_cmdline(char *filename, struct proc_info *proc) {
+    FILE *file;
+    char line[MAX_LINE];
+
+    line[0] = '\0';
+    file = fopen(filename, "r");
+    if (!file) return 1;
+    fgets(line, MAX_LINE, file);
+    fclose(file);
+    if (strlen(line) > 0) {
+        strncpy(proc->name, line, PROC_NAME_LEN);
+        proc->name[PROC_NAME_LEN-1] = 0;
+    } else
+        proc->name[0] = 0;
+    return 0;
+}
+
+static void read_policy(int pid, struct proc_info *proc) {
+    SchedPolicy p;
+    if (get_sched_policy(pid, &p) < 0)
+        strcpy(proc->policy, "unk");
+    else {
+        if (p == SP_BACKGROUND)
+            strcpy(proc->policy, "bg");
+        else if (p == SP_FOREGROUND)
+            strcpy(proc->policy, "fg");
+        else
+            strcpy(proc->policy, "er");
+    }
+}
+
+static int read_status(char *filename, struct proc_info *proc) {
+    FILE *file;
+    char line[MAX_LINE];
+    unsigned int uid, gid;
+
+    file = fopen(filename, "r");
+    if (!file) return 1;
+    while (fgets(line, MAX_LINE, file)) {
+        sscanf(line, "Uid: %u", &uid);
+        sscanf(line, "Gid: %u", &gid);
+    }
+    fclose(file);
+    proc->uid = uid; proc->gid = gid;
+    return 0;
+}
+
+static void print_procs(void) {
+    int i;
+    struct proc_info *old_proc, *proc;
+    long unsigned total_delta_time;
+    struct passwd *user;
+    struct group *group;
+    char *user_str, user_buf[20];
+    char *group_str, group_buf[20];
+
+    for (i = 0; i < num_new_procs; i++) {
+        if (new_procs[i]) {
+            old_proc = find_old_proc(new_procs[i]->pid, new_procs[i]->tid);
+            if (old_proc) {
+                new_procs[i]->delta_utime = new_procs[i]->utime - old_proc->utime;
+                new_procs[i]->delta_stime = new_procs[i]->stime - old_proc->stime;
+            } else {
+                new_procs[i]->delta_utime = 0;
+                new_procs[i]->delta_stime = 0;
+            }
+            new_procs[i]->delta_time = new_procs[i]->delta_utime + new_procs[i]->delta_stime;
+        }
+    }
+
+    total_delta_time = (new_cpu.utime + new_cpu.ntime + new_cpu.stime + new_cpu.itime
+                        + new_cpu.iowtime + new_cpu.irqtime + new_cpu.sirqtime)
+                     - (old_cpu.utime + old_cpu.ntime + old_cpu.stime + old_cpu.itime
+                        + old_cpu.iowtime + old_cpu.irqtime + old_cpu.sirqtime);
+
+    qsort(new_procs, num_new_procs, sizeof(struct proc_info *), proc_cmp);
+
+    printf("\n\n\n");
+    printf("User %ld%%, System %ld%%, IOW %ld%%, IRQ %ld%%\n",
+            ((new_cpu.utime + new_cpu.ntime) - (old_cpu.utime + old_cpu.ntime)) * 100  / total_delta_time,
+            ((new_cpu.stime ) - (old_cpu.stime)) * 100 / total_delta_time,
+            ((new_cpu.iowtime) - (old_cpu.iowtime)) * 100 / total_delta_time,
+            ((new_cpu.irqtime + new_cpu.sirqtime)
+                    - (old_cpu.irqtime + old_cpu.sirqtime)) * 100 / total_delta_time);
+    printf("User %ld + Nice %ld + Sys %ld + Idle %ld + IOW %ld + IRQ %ld + SIRQ %ld = %ld\n",
+            new_cpu.utime - old_cpu.utime,
+            new_cpu.ntime - old_cpu.ntime,
+            new_cpu.stime - old_cpu.stime,
+            new_cpu.itime - old_cpu.itime,
+            new_cpu.iowtime - old_cpu.iowtime,
+            new_cpu.irqtime - old_cpu.irqtime,
+            new_cpu.sirqtime - old_cpu.sirqtime,
+            total_delta_time);
+    printf("\n");
+    if (!threads) 
+        printf("%5s %2s %4s %1s %5s %7s %7s %3s %-8s %s\n", "PID", "PR", "CPU%", "S", "#THR", "VSS", "RSS", "PCY", "UID", "Name");
+    else
+        printf("%5s %5s %2s %4s %1s %7s %7s %3s %-8s %-15s %s\n", "PID", "TID", "PR", "CPU%", "S", "VSS", "RSS", "PCY", "UID", "Thread", "Proc");
+
+    for (i = 0; i < num_new_procs; i++) {
+        proc = new_procs[i];
+
+        if (!proc || (max_procs && (i >= max_procs)))
+            break;
+        user  = getpwuid(proc->uid);
+        group = getgrgid(proc->gid);
+        snprintf(user_buf, 20, "%d", proc->uid);
+        if (user && user->pw_name) {
+            user_str = user->pw_name;
+        } else {
+            user_str = user_buf;
+        }
+        if (uid_filter && (strcmp(uid_filter,user_str) &&
+                           strcmp(uid_filter,user_buf))) {
+            continue;
+        }
+        snprintf(group_buf, 20, "%d", proc->gid);
+        if (group && group->gr_name) {
+            group_str = group->gr_name;
+        } else {
+            group_str = group_buf;
+        }
+        if (gid_filter && (strcmp(gid_filter,group_str) &&
+                           strcmp(gid_filter,group_buf))) {
+            continue;
+        }
+        if (!threads) 
+            printf("%5d %2d %3ld%% %c %5d %6ldK %6ldK %3s %-8.8s %s\n", proc->pid, proc->prs, proc->delta_time * 100 / total_delta_time, proc->state, proc->num_threads,
+                proc->vss / 1024, proc->rss * getpagesize() / 1024, proc->policy, user_str, proc->name[0] != 0 ? proc->name : proc->tname);
+        else
+            printf("%5d %5d %2d %3ld%% %c %6ldK %6ldK %3s %-8.8s %-15s %s\n", proc->pid, proc->tid, proc->prs, proc->delta_time * 100 / total_delta_time, proc->state,
+                proc->vss / 1024, proc->rss * getpagesize() / 1024, proc->policy, user_str, proc->tname, proc->name);
+    }
+}
+
+static struct proc_info *find_old_proc(pid_t pid, pid_t tid) {
+    int i;
+
+    for (i = 0; i < num_old_procs; i++)
+        if (old_procs[i] && (old_procs[i]->pid == pid) && (old_procs[i]->tid == tid))
+            return old_procs[i];
+
+    return NULL;
+}
+
+static void free_old_procs(void) {
+    int i;
+
+    for (i = 0; i < num_old_procs; i++)
+        if (old_procs[i])
+            free_proc(old_procs[i]);
+
+    free(old_procs);
+}
+
+static int proc_cpu_cmp(const void *a, const void *b) {
+    struct proc_info *pa, *pb;
+
+    pa = *((struct proc_info **)a); pb = *((struct proc_info **)b);
+
+    if (!pa && !pb) return 0;
+    if (!pa) return 1;
+    if (!pb) return -1;
+
+    return -numcmp(pa->delta_time, pb->delta_time);
+}
+
+static int proc_vss_cmp(const void *a, const void *b) {
+    struct proc_info *pa, *pb;
+
+    pa = *((struct proc_info **)a); pb = *((struct proc_info **)b);
+
+    if (!pa && !pb) return 0;
+    if (!pa) return 1;
+    if (!pb) return -1;
+
+    return -numcmp(pa->vss, pb->vss);
+}
+
+static int proc_rss_cmp(const void *a, const void *b) {
+    struct proc_info *pa, *pb;
+
+    pa = *((struct proc_info **)a); pb = *((struct proc_info **)b);
+
+    if (!pa && !pb) return 0;
+    if (!pa) return 1;
+    if (!pb) return -1;
+
+    return -numcmp(pa->rss, pb->rss);
+}
+
+static int proc_thr_cmp(const void *a, const void *b) {
+    struct proc_info *pa, *pb;
+
+    pa = *((struct proc_info **)a); pb = *((struct proc_info **)b);
+
+    if (!pa && !pb) return 0;
+    if (!pa) return 1;
+    if (!pb) return -1;
+
+    return -numcmp(pa->num_threads, pb->num_threads);
+}
+
+static int numcmp(long long a, long long b) {
+    if (a < b) return -1;
+    if (a > b) return 1;
+    return 0;
+}
+
+static void usage(char *cmd) {
+  fprintf(stderr, "Usage: %s [ -m max_procs ] [ -n iterations ] [ -d delay ]"
+                  " [ -s sort_column ] [-u user] [-g group] [ -t ] [ -h ]\n"
+                  "    -m num   Maximum number of processes to display.\n"
+                  "    -n num   Updates to show before exiting.\n"
+                  "    -d num   Seconds to wait between updates.\n"
+                  "    -s col   Column to sort by (cpu,vss,rss,thr).\n"
+                  "    -u user  Only show prcessors with the given user ID.\n"
+                  "    -g group Only show processes with the given group ID.\n"
+                  "    -t       Show threads instead of processes.\n"
+                  "    -h       Display this help screen.\n",
+          cmd);
+}
diff --git a/toolbox/touch.c b/toolbox/touch.c
new file mode 100644
index 0000000..b8ab310
--- /dev/null
+++ b/toolbox/touch.c
@@ -0,0 +1,87 @@
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <fcntl.h>
+
+static void usage(void)
+{
+        fprintf(stderr, "touch: usage: touch [-alm] [-t time_t] <file>\n");
+        exit(1);
+}
+
+int touch_main(int argc, char *argv[])
+{
+        int i, fd, aflag = 0, mflag = 0, debug = 0, flags = 0;
+        struct timespec specified_time, times[2];
+        char *file = 0;
+
+        specified_time.tv_nsec = UTIME_NOW;
+
+        for (i = 1; i < argc; i++) {
+            if (argv[i][0] == '-') {
+                /* an option */
+                const char *arg = argv[i]+1;
+                while (arg[0]) {
+                    switch (arg[0]) {
+                    case 'a': aflag = 1; break;
+                    case 'm': mflag = 1; break;
+                    case 't':
+                        if ((i+1) >= argc)
+                            usage();
+                        specified_time.tv_sec = atol(argv[++i]);
+                        if (specified_time.tv_sec == 0) {
+                            fprintf(stderr, "touch: invalid time_t\n");
+                            exit(1);
+                        }
+                        specified_time.tv_nsec = 0;
+                        break;
+                    case 'l': flags |= AT_SYMLINK_NOFOLLOW; break;
+                    case 'd': debug = 1; break;
+                    default:
+                        usage();
+                    }
+                    arg++;
+                }
+            } else {
+                /* not an option, and only accept one filename */
+                if (i+1 != argc)
+                    usage();
+                file = argv[i];
+            }
+        }
+
+        if (! file) {
+            fprintf(stderr, "touch: no file specified\n");
+            exit(1);
+        }
+
+        if (access(file, F_OK))
+            if ((fd=creat(file, 0666)) != -1)
+                close(fd);
+
+        if ((mflag == 0) && (aflag == 0))
+            aflag = mflag = 1;
+
+        if (aflag)
+            times[0] = specified_time;
+        else
+            times[0].tv_nsec = UTIME_OMIT;
+
+        if (mflag)
+            times[1] = specified_time;
+        else
+            times[1].tv_nsec = UTIME_OMIT;
+
+        if (debug) {
+            fprintf(stderr, "file = %s\n", file);
+            fprintf(stderr, "times[0].tv_sec = %ld, times[0].tv_nsec = %ld\n", times[0].tv_sec, times[0].tv_nsec);
+            fprintf(stderr, "times[1].tv_sec = %ld, times[1].tv_nsec = %ld\n", times[1].tv_sec, times[1].tv_nsec);
+            fprintf(stderr, "flags = 0x%8.8x\n", flags);
+        }
+
+        return utimensat(AT_FDCWD, file, times, flags);
+}
+
diff --git a/toolbox/umount.c b/toolbox/umount.c
new file mode 100644
index 0000000..15b7003
--- /dev/null
+++ b/toolbox/umount.c
@@ -0,0 +1,94 @@
+
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <linux/loop.h>
+#include <errno.h>
+
+// if glibc version >= 2.25
+#include <sys/sysmacros.h>
+
+#define LOOPDEV_MAXLEN 64
+#define LOOP_MAJOR 7
+
+static int is_loop(char *dev)
+{
+    struct stat st;
+    int ret = 0;
+
+    if (stat(dev, &st) == 0) {
+        if (S_ISBLK(st.st_mode) && (major(st.st_rdev) == LOOP_MAJOR)) {
+            ret = 1;
+        }
+    }
+
+    return ret;
+}
+
+static int is_loop_mount(const char* path, char *loopdev)
+{
+    FILE* f;
+    int count;
+    char device[256];
+    char mount_path[256];
+    char rest[256];
+    int result = 0;
+    int path_length = strlen(path);
+    
+    f = fopen("/proc/mounts", "r");
+    if (!f) {
+        fprintf(stdout, "could not open /proc/mounts: %s\n", strerror(errno));
+        return -1;
+    }
+
+    do {
+        count = fscanf(f, "%255s %255s %255s\n", device, mount_path, rest);
+        if (count == 3) {
+            if (is_loop(device) && strcmp(path, mount_path) == 0) {
+                strlcpy(loopdev, device, LOOPDEV_MAXLEN);
+                result = 1;
+                break;
+            }
+        }
+    } while (count == 3);
+
+    fclose(f);
+    return result;
+}
+
+int umount_main(int argc, char *argv[])
+{
+    int loop, loop_fd;
+    char loopdev[LOOPDEV_MAXLEN];
+
+    if(argc != 2) {
+        fprintf(stderr,"umount <path>\n");
+        return 1;
+    }
+
+    loop = is_loop_mount(argv[1], loopdev);
+    if (umount(argv[1])) {
+        fprintf(stderr, "failed: %s\n", strerror(errno));
+        return 1;
+    }
+
+    if (loop) {
+        // free the loop device
+        loop_fd = open(loopdev, O_RDONLY);
+        if (loop_fd < 0) {
+            fprintf(stderr, "open loop device failed: %s\n", strerror(errno));
+            return 1;
+        }
+        if (ioctl(loop_fd, LOOP_CLR_FD, 0) < 0) {
+            fprintf(stderr, "ioctl LOOP_CLR_FD failed: %s\n", strerror(errno));
+            return 1;
+        }
+
+        close(loop_fd);
+    }
+
+    return 0;
+}
diff --git a/toolbox/uptime.c b/toolbox/uptime.c
new file mode 100644
index 0000000..ebf3672
--- /dev/null
+++ b/toolbox/uptime.c
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2010, The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/time.h>
+#include <linux/ioctl.h>
+#include <linux/rtc.h>
+#include <linux/android_alarm.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <time.h>
+
+
+static void format_time(int time, char* buffer) {
+    int seconds, minutes, hours, days;
+
+    seconds = time % 60;
+    time /= 60;
+    minutes = time % 60;
+    time /= 60;
+    hours = time % 24;
+    days = time / 24;
+
+    if (days > 0)
+        sprintf(buffer, "%d days, %02d:%02d:%02d", days, hours, minutes, seconds);
+    else
+        sprintf(buffer, "%02d:%02d:%02d", hours, minutes, seconds);
+}
+
+int uptime_main(int argc, char *argv[])
+{
+    float up_time, idle_time;
+    char up_string[100], idle_string[100], sleep_string[100];
+    int elapsed;
+
+    FILE* file = fopen("/proc/uptime", "r");
+    if (!file) {
+        fprintf(stderr, "Could not open /proc/uptime\n");
+        return -1;
+    }
+    if (fscanf(file, "%f %f", &up_time, &idle_time) != 2) {
+        fprintf(stderr, "Could not parse /proc/uptime\n");
+        fclose(file);
+        return -1;
+    }
+    fclose(file);
+
+    format_time((int)up_time, up_string);
+    format_time((int)idle_time, idle_string);
+    printf("up time: %s, idle time: %s\n", up_string, idle_string);
+
+    return 0;
+}
diff --git a/toolbox/vmstat.c b/toolbox/vmstat.c
new file mode 100644
index 0000000..600f136
--- /dev/null
+++ b/toolbox/vmstat.c
@@ -0,0 +1,247 @@
+/*
+ * Copyright (c) 2008, The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the 
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/param.h>
+#include <unistd.h>
+
+struct state {
+    long procs_r;
+    long procs_b;
+
+    long mem_free;
+    long mem_mapped;
+    long mem_anon;
+    long mem_slab;
+
+    long sys_in;
+    long sys_cs;
+    long sys_flt;
+
+    long cpu_us;
+    long cpu_ni;
+    long cpu_sy;
+    long cpu_id;
+    long cpu_wa;
+    long cpu_ir;
+    long cpu_si;
+};
+
+#define MAX_LINE 256
+
+char line[MAX_LINE];
+
+static void read_state(struct state *s);
+static int read_meminfo(struct state *s);
+static int read_stat(struct state *s);
+static int read_vmstat(struct state *s);
+static void print_header(void);
+static void print_line(struct state *old, struct state *new);
+static void usage(char *cmd);
+
+int vmstat_main(int argc, char *argv[]) {
+    struct state s[2];
+    int iterations, delay, header_interval;
+    int toggle, count;
+    int i;
+
+    iterations = 0;
+    delay = 1;
+    header_interval = 20;
+
+    for (i = 1; i < argc; i++) {
+        if (!strcmp(argv[i], "-n")) { 
+            if (i >= argc - 1) {
+                fprintf(stderr, "Option -n requires an argument.\n");
+                exit(EXIT_FAILURE);
+            }
+            iterations = atoi(argv[++i]);
+            continue;
+        }
+        if (!strcmp(argv[i], "-d")) {
+            if (i >= argc - 1) {
+                fprintf(stderr, "Option -d requires an argument.\n");
+                exit(EXIT_FAILURE);
+            }
+            delay = atoi(argv[++i]);
+            continue;
+        }
+        if (!strcmp(argv[i], "-r")) {
+            if (i >= argc - 1) {
+                fprintf(stderr, "Option -r requires an argument.\n");
+                exit(EXIT_FAILURE);
+            }
+            header_interval = atoi(argv[++i]);
+            continue;
+        }
+        if (!strcmp(argv[i], "-h")) {
+            usage(argv[0]);
+            exit(EXIT_SUCCESS);
+        }
+        fprintf(stderr, "Invalid argument \"%s\".\n", argv[i]);
+        usage(argv[0]);
+	exit(EXIT_FAILURE);
+    }
+
+    toggle = 0;
+    count = 0;
+
+    if (!header_interval)
+        print_header();
+    read_state(&s[1 - toggle]);
+    while ((iterations == 0) || (iterations-- > 0)) {
+        sleep(delay);
+        read_state(&s[toggle]);
+        if (header_interval) {
+            if (count == 0)
+                print_header();
+            count = (count + 1) % header_interval;
+        }
+        print_line(&s[1 - toggle], &s[toggle]);
+        toggle = 1 - toggle;
+    }
+
+    return 0;
+}
+
+static void read_state(struct state *s) {
+    int error;
+
+    error = read_meminfo(s);
+    if (error) {
+        fprintf(stderr, "vmstat: could not read /proc/meminfo: %s\n", strerror(error));
+        exit(EXIT_FAILURE);
+    }
+
+    error = read_stat(s);
+    if (error) {
+        fprintf(stderr, "vmstat: could not read /proc/stat: %s\n", strerror(error));
+        exit(EXIT_FAILURE);
+    }
+
+    error = read_vmstat(s);
+    if (error) {
+        fprintf(stderr, "vmstat: could not read /proc/vmstat: %s\n", strerror(error));
+        exit(EXIT_FAILURE);
+    }
+}
+
+static int read_meminfo(struct state *s) {
+    FILE *f;
+
+    f = fopen("/proc/meminfo", "r");
+    if (!f) return errno;
+
+    while (fgets(line, MAX_LINE, f)) {
+        sscanf(line, "MemFree: %ld kB", &s->mem_free);
+        sscanf(line, "AnonPages: %ld kB", &s->mem_anon);
+        sscanf(line, "Mapped: %ld kB", &s->mem_mapped);
+        sscanf(line, "Slab: %ld kB", &s->mem_slab);
+    }
+
+    fclose(f);
+
+    return 0;
+}
+
+static int read_stat(struct state *s) {
+    FILE *f;
+
+    f = fopen("/proc/stat", "r");
+    if (!f) return errno;
+
+    while (fgets(line, MAX_LINE, f)) {
+        if (!strncmp(line, "cpu ", 4)) {
+            sscanf(line, "cpu  %ld %ld %ld %ld %ld %ld %ld",
+                &s->cpu_us, &s->cpu_ni, &s->cpu_sy, &s->cpu_id, &s->cpu_wa,
+                &s->cpu_ir, &s->cpu_si);
+        }
+        sscanf(line, "intr %ld", &s->sys_in);
+        sscanf(line, "ctxt %ld", &s->sys_cs);
+        sscanf(line, "procs_running %ld", &s->procs_r);
+        sscanf(line, "procs_blocked %ld", &s->procs_b);
+    }
+
+    fclose(f);
+
+    return 0;
+}
+
+static int read_vmstat(struct state *s) {
+    FILE *f;
+
+    f = fopen("/proc/vmstat", "r");
+    if (!f) return errno;
+
+    while (fgets(line, MAX_LINE, f)) {
+        sscanf(line, "pgmajfault %ld", &s->sys_flt);
+    }
+
+    fclose(f);
+
+    return 0;
+}
+
+static void print_header(void) {
+    printf("%-5s  %-27s  %-14s  %-17s\n", "procs", "memory", "system", "cpu");
+    printf("%2s %2s  %6s %6s %6s %6s  %4s %4s %4s  %2s %2s %2s %2s %2s %2s\n", "r", "b", "free", "mapped", "anon", "slab", "in", "cs", "flt", "us", "ni", "sy", "id", "wa", "ir");
+}
+
+/* Jiffies to percent conversion */
+#define JP(jif) ((jif) * 100 / (HZ))
+#define NORM(var) ((var) = (((var) > 99) ? (99) : (var)))
+
+static void print_line(struct state *old, struct state *new) {
+    int us, ni, sy, id, wa, ir;
+    us = JP(new->cpu_us - old->cpu_us); NORM(us);
+    ni = JP(new->cpu_ni - old->cpu_ni); NORM(ni);
+    sy = JP(new->cpu_sy - old->cpu_sy); NORM(sy);
+    id = JP(new->cpu_id - old->cpu_id); NORM(id);
+    wa = JP(new->cpu_wa - old->cpu_wa); NORM(wa);
+    ir = JP(new->cpu_ir - old->cpu_ir); NORM(ir);
+    printf("%2ld %2ld  %6ld %6ld %6ld %6ld  %4ld %4ld %4ld  %2d %2d %2d %2d %2d %2d\n",
+        new->procs_r ? (new->procs_r - 1) : 0, new->procs_b,
+        new->mem_free, new->mem_mapped, new->mem_anon, new->mem_slab,
+        new->sys_in - old->sys_in, new->sys_cs - old->sys_cs, new->sys_flt - old->sys_flt,
+        us, ni, sy, id, wa, ir);
+}
+
+static void usage(char *cmd) {
+    fprintf(stderr, "Usage: %s [ -h ] [ -n iterations ] [ -d delay ] [ -r header_repeat ]\n"
+                    "    -n iterations     How many rows of data to print.\n"
+                    "    -d delay          How long to sleep between rows.\n"
+                    "    -r header_repeat  How many rows to print before repeating\n"
+                    "                      the header.  Zero means never repeat.\n"
+                    "    -h                Displays this help screen.\n",
+        cmd);
+}
diff --git a/toolbox/watchprops.c b/toolbox/watchprops.c
new file mode 100644
index 0000000..d311992
--- /dev/null
+++ b/toolbox/watchprops.c
@@ -0,0 +1,76 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include <cutils/properties.h>
+
+#include <sys/atomics.h>
+
+#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
+#include <sys/_system_properties.h>
+
+
+extern prop_area *__system_property_area__;
+
+typedef struct pwatch pwatch;
+
+struct pwatch
+{
+    const prop_info *pi;
+    unsigned serial;
+};
+
+static pwatch watchlist[1024];
+
+static void announce(const prop_info *pi)
+{
+    char name[PROP_NAME_MAX];
+    char value[PROP_VALUE_MAX];
+    char *x;
+    
+    __system_property_read(pi, name, value);
+
+    for(x = value; *x; x++) {
+        if((*x < 32) || (*x > 127)) *x = '.';
+    }
+
+    fprintf(stderr,"%10d %s = '%s'\n", (int) time(0), name, value);
+}
+
+int watchprops_main(int argc, char *argv[])
+{
+    prop_area *pa = __system_property_area__;
+    unsigned serial = pa->serial;
+    unsigned count = pa->count;
+    unsigned n;
+    
+    if(count >= 1024) exit(1);
+
+    for(n = 0; n < count; n++) {
+        watchlist[n].pi = __system_property_find_nth(n);
+        watchlist[n].serial = watchlist[n].pi->serial;
+    }
+
+    for(;;) {
+        do {
+            __futex_wait(&pa->serial, serial, 0);
+        } while(pa->serial == serial);
+
+        while(count < pa->count){
+            watchlist[count].pi = __system_property_find_nth(count);
+            watchlist[count].serial = watchlist[n].pi->serial;
+            announce(watchlist[count].pi);
+            count++;
+            if(count == 1024) exit(1);
+        }
+
+        for(n = 0; n < count; n++){
+            unsigned tmp = watchlist[n].pi->serial;
+            if(watchlist[n].serial != tmp) {
+                announce(watchlist[n].pi);
+                watchlist[n].serial = tmp;
+            }
+        }
+    }
+    return 0;
+}
diff --git a/toolbox/wipe.c b/toolbox/wipe.c
new file mode 100644
index 0000000..650a0d6
--- /dev/null
+++ b/toolbox/wipe.c
@@ -0,0 +1,176 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <cutils/android_reboot.h>
+#include <sys/stat.h>
+
+#ifndef PATH_MAX
+#define PATH_MAX 4096
+#endif
+
+
+/* Directories created by init defined in system/rootdir/init.rc */
+static char *INIT_DIRS[] = {
+    "/system/etc/ppp",
+    "/data/misc",
+    "/data/local",
+    "/data/local/tmp",
+    "/data/data",
+    "/data/app_private",
+    "/data/app",
+    NULL
+};
+
+static void wipe (const char *path);
+
+static int usage()
+{
+    fprintf(stderr, "wipe <system|data|all>\n\n"
+                    "system means '/system'\n"
+                    "data means '/data'\n");
+
+    return -1;
+}
+
+int wipe_main (int argc, char *argv[])
+{
+    char *whatToWipe;
+
+    if (argc != 2) return usage();
+
+    whatToWipe = argv[1];
+
+    if (0 == strcmp (whatToWipe, "system")) {
+        fprintf(stdout, "Wiping /system\n");
+        wipe ("/system");
+        fprintf(stdout, "Done wiping /android\n");
+    } else if (0 == strcmp (whatToWipe, "data")) {
+        fprintf(stdout, "Wiping /data\n");
+        wipe ("/data");
+        fprintf(stdout, "Done wiping /data\n");
+    } else if (0 == strcmp (whatToWipe, "all")) {
+        fprintf(stdout, "Wiping /system and /data\n");
+        wipe ("/system");
+        wipe ("/data");
+        fprintf(stdout, "Done wiping /system and /data\n");
+    } else if (0 == strcmp(whatToWipe, "nuke")) {
+		int ret;
+		fprintf(stdout, "Nuking the device...\n");
+		wipe ("/system");
+        wipe ("/data");
+		fprintf(stdout, "Device nuked! Rebooting...\n");
+		ret = android_reboot(ANDROID_RB_RESTART, 0, 0);
+	    if (ret < 0) {
+	        fprintf(stderr, "Reboot failed, %s\n", strerror(errno));
+	        return 1;
+	    }
+	} else {
+        return usage();
+    }
+
+    return 0;
+}
+
+static char nameBuffer[PATH_MAX];
+static struct stat statBuffer;
+
+static void wipe (const char *path) 
+{
+    DIR *dir;
+    struct dirent *de;
+    int ret;
+
+    dir = opendir(path);
+
+    if (dir == NULL) {
+        fprintf (stderr, "Error opendir'ing %s '%s'\n",
+                    path, strerror(errno));
+        return;
+    }
+
+    char *filenameOffset;
+
+    strcpy(nameBuffer, path);
+    strcat(nameBuffer, "/");
+
+    filenameOffset = nameBuffer + strlen(nameBuffer);
+
+    for (;;) {
+        de = readdir(dir);
+
+        if (de == NULL) {
+            break;
+        }
+
+        if (0 == strcmp(de->d_name, ".")
+                || 0 == strcmp(de->d_name, "..")
+                || 0 == strcmp(de->d_name, "lost+found")
+        ) {
+            continue;
+        }
+
+        strcpy(filenameOffset, de->d_name);
+
+        ret = lstat (nameBuffer, &statBuffer);
+
+        if (ret != 0) {
+            fprintf(stderr, "stat() error on '%s' '%s'\n", 
+                    nameBuffer, strerror(errno));
+        }
+
+        if(S_ISDIR(statBuffer.st_mode)) {
+            int i;
+            char *newpath;
+
+#if 0
+            closedir(dir);
+#endif
+
+            newpath = strdup(nameBuffer);
+            wipe(newpath);
+
+            /* Leave directories created by init, they have special permissions. */
+            for (i = 0; INIT_DIRS[i]; i++) {
+                if (strcmp(INIT_DIRS[i], newpath) == 0) {
+                    break;
+                }
+            }
+            if (INIT_DIRS[i] == NULL) {
+                ret = rmdir(newpath);
+                if (ret != 0) {
+                    fprintf(stderr, "rmdir() error on '%s' '%s'\n", 
+                        newpath, strerror(errno));
+                }
+            }
+
+            free(newpath);
+
+#if 0
+            dir = opendir(path);
+            if (dir == NULL) {
+                fprintf (stderr, "Error opendir'ing %s '%s'\n",
+                            path, strerror(errno));
+                return;
+            }
+#endif
+
+            strcpy(nameBuffer, path);
+            strcat(nameBuffer, "/");
+
+        } else {
+            ret = unlink(nameBuffer);
+
+            if (ret != 0) {
+                fprintf(stderr, "unlink() error on '%s' '%s'\n", 
+                    nameBuffer, strerror(errno));
+            }
+        }
+    }
+
+    closedir(dir);
+
+}