am f9ad69f4: am 803b245e: (-s ours) am 469dbc0a: (-s ours) Reconcile with jb-mr1-release - do not merge

* commit 'f9ad69f467c2207e6e8ec995c2b73dde0f6a787f':
diff --git a/ext4_utils/Android.mk b/ext4_utils/Android.mk
index 99145a4..6eba3b1 100644
--- a/ext4_utils/Android.mk
+++ b/ext4_utils/Android.mk
@@ -12,7 +12,8 @@
     indirect.c \
     uuid.c \
     sha1.c \
-    wipe.c
+    wipe.c \
+    crc16.c
 
 #
 # -- All host/targets including windows
@@ -21,13 +22,12 @@
 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 ($(HAVE_SELINUX), true)
-  LOCAL_C_INCLUDES += external/libselinux/include
+LOCAL_STATIC_LIBRARIES := \
+    libsparse_host \
+    libz
+ifneq ($(HOST_OS),windows)
   LOCAL_STATIC_LIBRARIES += libselinux
-  LOCAL_CFLAGS += -DHAVE_SELINUX
-endif # HAVE_SELINUX
+endif
 include $(BUILD_HOST_STATIC_LIBRARY)
 
 
@@ -41,11 +41,8 @@
 ifeq ($(HOST_OS),windows)
   LOCAL_LDLIBS += -lws2_32
 else
-  ifeq ($(HAVE_SELINUX), true)
-    LOCAL_C_INCLUDES += external/libselinux/include
-    LOCAL_STATIC_LIBRARIES += libselinux
-    LOCAL_CFLAGS += -DHAVE_SELINUX
-  endif # HAVE_SELINUX
+  LOCAL_STATIC_LIBRARIES += libselinux
+  LOCAL_CFLAGS := -DHOST
 endif
 include $(BUILD_HOST_EXECUTABLE)
 
@@ -59,41 +56,29 @@
 include $(CLEAR_VARS)
 LOCAL_SRC_FILES := $(libext4_utils_src_files)
 LOCAL_MODULE := libext4_utils
-LOCAL_C_INCLUDES += external/zlib
 LOCAL_SHARED_LIBRARIES := \
+    libselinux \
     libsparse \
     libz
-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 += \
+    libselinux \
     libsparse_static
-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_SHARED_LIBRARIES += libext4_utils libz
-ifeq ($(HAVE_SELINUX), true)
-  LOCAL_C_INCLUDES += external/libselinux/include
-  LOCAL_SHARED_LIBRARIES += libselinux
-  LOCAL_CFLAGS += -DHAVE_SELINUX
-endif # HAVE_SELINUX
+LOCAL_SHARED_LIBRARIES := \
+    libext4_utils \
+    libselinux \
+    libz
 include $(BUILD_EXECUTABLE)
 
 
@@ -102,13 +87,9 @@
 LOCAL_MODULE := ext2simg
 LOCAL_SHARED_LIBRARIES += \
     libext4_utils \
+    libselinux \
     libsparse \
     libz
-ifeq ($(HAVE_SELINUX), true)
-  LOCAL_C_INCLUDES += external/libselinux/include
-  LOCAL_SHARED_LIBRARIES += libselinux
-  LOCAL_CFLAGS += -DHAVE_SELINUX
-endif # HAVE_SELINUX
 include $(BUILD_EXECUTABLE)
 
 
@@ -117,13 +98,9 @@
 LOCAL_MODULE := ext2simg
 LOCAL_STATIC_LIBRARIES += \
     libext4_utils_host \
+    libselinux \
     libsparse_host \
     libz
-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)
 
 
diff --git a/ext4_utils/allocate.c b/ext4_utils/allocate.c
index adf91ba..5c60e92 100644
--- a/ext4_utils/allocate.c
+++ b/ext4_utils/allocate.c
@@ -56,9 +56,16 @@
 	u32 first_free_block;
 	u32 free_inodes;
 	u32 first_free_inode;
+	u16 flags;
 	u16 used_dirs;
 };
 
+struct xattr_list_element {
+	struct ext4_inode *inode;
+	struct ext4_xattr_header *header;
+	struct xattr_list_element *next;
+};
+
 struct block_allocation *create_allocation()
 {
 	struct block_allocation *alloc = malloc(sizeof(struct block_allocation));
@@ -73,6 +80,25 @@
 	return alloc;
 }
 
+static struct ext4_xattr_header *xattr_list_find(struct ext4_inode *inode)
+{
+	struct xattr_list_element *element;
+	for (element = aux_info.xattrs; element != NULL; element = element->next) {
+		if (element->inode == inode)
+			return element->header;
+	}
+	return NULL;
+}
+
+static void xattr_list_insert(struct ext4_inode *inode, struct ext4_xattr_header *header)
+{
+	struct xattr_list_element *element = malloc(sizeof(struct xattr_list_element));
+	element->inode = inode;
+	element->header = header;
+	element->next = aux_info.xattrs;
+	aux_info.xattrs = element;
+}
+
 static void region_list_remove(struct region_list *list, struct region *reg)
 {
 	if (reg->prev)
@@ -157,24 +183,8 @@
 
 	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);
-		}
-	}
+	bg->flags &= ~EXT4_BG_INODE_UNINIT;
 }
 
 static int bitmap_set_bit(u8 *bitmap, u32 bit)
@@ -297,6 +307,7 @@
 	bg->first_free_block = 0;
 	bg->free_inodes = info.inodes_per_group;
 	bg->first_free_inode = 1;
+	bg->flags = EXT4_BG_INODE_UNINIT;
 
 	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);
@@ -687,6 +698,35 @@
 		info.inode_size);
 }
 
+struct ext4_xattr_header *get_xattr_block_for_inode(struct ext4_inode *inode)
+{
+	struct ext4_xattr_header *block = xattr_list_find(inode);
+	if (block != NULL)
+		return block;
+
+	u32 block_num = allocate_block();
+	block = calloc(info.block_size, 1);
+	if (block == NULL) {
+		error("get_xattr: failed to allocate %d", info.block_size);
+		return NULL;
+	}
+
+	block->h_magic = cpu_to_le32(EXT4_XATTR_MAGIC);
+	block->h_refcount = cpu_to_le32(1);
+	block->h_blocks = cpu_to_le32(1);
+	inode->i_blocks_lo = cpu_to_le32(le32_to_cpu(inode->i_blocks_lo) + (info.block_size / 512));
+	inode->i_file_acl_lo = cpu_to_le32(block_num);
+
+	int result = sparse_file_add_data(info.sparse_file, block, info.block_size, block_num);
+	if (result != 0) {
+		error("get_xattr: sparse_file_add_data failure %d", result);
+		free(block);
+		return NULL;
+	}
+	xattr_list_insert(inode, block);
+	return block;
+}
+
 /* Mark the first len inodes in a block group as used */
 u32 reserve_inodes(int bg, u32 num)
 {
@@ -744,6 +784,12 @@
 	return aux_info.bgs[bg].used_dirs;
 }
 
+/* Returns the flags for a block group */
+u16 get_bg_flags(int bg)
+{
+	return aux_info.bgs[bg].flags;
+}
+
 /* Frees the memory used by a linked list of allocation regions */
 void free_alloc(struct block_allocation *alloc)
 {
diff --git a/ext4_utils/allocate.h b/ext4_utils/allocate.h
index fb6e0f7..7a3ffed 100644
--- a/ext4_utils/allocate.h
+++ b/ext4_utils/allocate.h
@@ -21,6 +21,7 @@
 
 #include "ext4_utils.h"
 #include "ext4.h"
+#include "xattr.h"
 
 struct block_allocation;
 
@@ -31,6 +32,7 @@
 int block_allocation_num_regions(struct block_allocation *alloc);
 int block_allocation_len(struct block_allocation *alloc);
 struct ext4_inode *get_inode(u32 inode);
+struct ext4_xattr_header *get_xattr_block_for_inode(struct ext4_inode *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);
@@ -41,6 +43,7 @@
 u32 reserve_inodes(int bg, u32 inodes);
 void add_directory(u32 inode);
 u16 get_directories(int bg);
+u16 get_bg_flags(int bg);
 void init_unused_inode_tables(void);
 u32 allocate_inode();
 void free_alloc(struct block_allocation *alloc);
diff --git a/ext4_utils/contents.c b/ext4_utils/contents.c
index aeed31e..3abbdc3 100644
--- a/ext4_utils/contents.c
+++ b/ext4_utils/contents.c
@@ -18,6 +18,15 @@
 #include <string.h>
 #include <stdio.h>
 
+#ifdef HAVE_ANDROID_OS
+#include <linux/capability.h>
+#else
+#include <private/android_filesystem_capability.h>
+#endif
+
+#define XATTR_SELINUX_SUFFIX "selinux"
+#define XATTR_CAPS_SUFFIX "capability"
+
 #include "ext4_utils.h"
 #include "ext4.h"
 #include "make_ext4fs.h"
@@ -190,7 +199,7 @@
 }
 
 /* Creates a file on disk.  Returns the inode number of the new file */
-u32 make_link(const char *filename, const char *link)
+u32 make_link(const char *link)
 {
 	struct ext4_inode *inode;
 	u32 inode_num;
@@ -242,51 +251,231 @@
 	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)
+/*
+ * Returns the amount of free space available in the specified
+ * xattr region
+ */
+static size_t xattr_free_space(struct ext4_xattr_entry *entry, char *end)
 {
-	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;
+	while(!IS_LAST_ENTRY(entry) && (((char *) entry) < end)) {
+		end   -= EXT4_XATTR_SIZE(le32_to_cpu(entry->e_value_size));
+		entry  = EXT4_XATTR_NEXT(entry);
+	}
 
-	if (!secon)
+	if (((char *) entry) > end) {
+		error("unexpected read beyond end of xattr space");
 		return 0;
+	}
 
-	if (!inode)
+	return end - ((char *) entry);
+}
+
+/*
+ * Returns a pointer to the free space immediately after the
+ * last xattr element
+ */
+static struct ext4_xattr_entry* xattr_get_last(struct ext4_xattr_entry *entry)
+{
+	for (; !IS_LAST_ENTRY(entry); entry = EXT4_XATTR_NEXT(entry)) {
+		// skip entry
+	}
+	return entry;
+}
+
+/*
+ * assert that the elements in the ext4 xattr section are in sorted order
+ *
+ * The ext4 filesystem requires extended attributes to be sorted when
+ * they're not stored in the inode. The kernel ext4 code uses the following
+ * sorting algorithm:
+ *
+ * 1) First sort extended attributes by their name_index. For example,
+ *    EXT4_XATTR_INDEX_USER (1) comes before EXT4_XATTR_INDEX_SECURITY (6).
+ * 2) If the name_indexes are equal, then sorting is based on the length
+ *    of the name. For example, XATTR_SELINUX_SUFFIX ("selinux") comes before
+ *    XATTR_CAPS_SUFFIX ("capability") because "selinux" is shorter than "capability"
+ * 3) If the name_index and name_length are equal, then memcmp() is used to determine
+ *    which name comes first. For example, "selinux" would come before "yelinux".
+ *
+ * This method is intended to implement the sorting function defined in
+ * the Linux kernel file fs/ext4/xattr.c function ext4_xattr_find_entry().
+ */
+static void xattr_assert_sane(struct ext4_xattr_entry *entry)
+{
+	for( ; !IS_LAST_ENTRY(entry); entry = EXT4_XATTR_NEXT(entry)) {
+		struct ext4_xattr_entry *next = EXT4_XATTR_NEXT(entry);
+		if (IS_LAST_ENTRY(next)) {
+			return;
+		}
+
+		int cmp = next->e_name_index - entry->e_name_index;
+		if (cmp == 0)
+			cmp = next->e_name_len - entry->e_name_len;
+		if (cmp == 0)
+			cmp = memcmp(next->e_name, entry->e_name, next->e_name_len);
+		if (cmp < 0) {
+			error("BUG: extended attributes are not sorted\n");
+			return;
+		}
+		if (cmp == 0) {
+			error("BUG: duplicate extended attributes detected\n");
+			return;
+		}
+	}
+}
+
+#define NAME_HASH_SHIFT 5
+#define VALUE_HASH_SHIFT 16
+
+static void ext4_xattr_hash_entry(struct ext4_xattr_header *header,
+		struct ext4_xattr_entry *entry)
+{
+	__u32 hash = 0;
+	char *name = entry->e_name;
+	int n;
+
+	for (n = 0; n < entry->e_name_len; n++) {
+		hash = (hash << NAME_HASH_SHIFT) ^
+			(hash >> (8*sizeof(hash) - NAME_HASH_SHIFT)) ^
+			*name++;
+	}
+
+	if (entry->e_value_block == 0 && entry->e_value_size != 0) {
+		__le32 *value = (__le32 *)((char *)header +
+			le16_to_cpu(entry->e_value_offs));
+		for (n = (le32_to_cpu(entry->e_value_size) +
+			EXT4_XATTR_ROUND) >> EXT4_XATTR_PAD_BITS; n; n--) {
+			hash = (hash << VALUE_HASH_SHIFT) ^
+				(hash >> (8*sizeof(hash) - VALUE_HASH_SHIFT)) ^
+				le32_to_cpu(*value++);
+		}
+	}
+	entry->e_hash = cpu_to_le32(hash);
+}
+
+#undef NAME_HASH_SHIFT
+#undef VALUE_HASH_SHIFT
+
+static struct ext4_xattr_entry* xattr_addto_range(
+		void *block_start,
+		void *block_end,
+		struct ext4_xattr_entry *first,
+		int name_index,
+		const char *name,
+		const void *value,
+		size_t value_len)
+{
+	size_t name_len = strlen(name);
+	if (name_len > 255)
+		return NULL;
+
+	size_t available_size = xattr_free_space(first, block_end);
+	size_t needed_size = EXT4_XATTR_LEN(name_len) + EXT4_XATTR_SIZE(value_len);
+
+	if (needed_size > available_size)
+		return NULL;
+
+	struct ext4_xattr_entry *new_entry = xattr_get_last(first);
+	memset(new_entry, 0, EXT4_XATTR_LEN(name_len));
+
+	new_entry->e_name_len = name_len;
+	new_entry->e_name_index = name_index;
+	memcpy(new_entry->e_name, name, name_len);
+	new_entry->e_value_block = 0;
+	new_entry->e_value_size = cpu_to_le32(value_len);
+
+	char *val = (char *) new_entry + available_size - EXT4_XATTR_SIZE(value_len);
+	size_t e_value_offs = val - (char *) block_start;
+
+	new_entry->e_value_offs = cpu_to_le16(e_value_offs);
+	memset(val, 0, EXT4_XATTR_SIZE(value_len));
+	memcpy(val, value, value_len);
+
+	xattr_assert_sane(first);
+	return new_entry;
+}
+
+static int xattr_addto_inode(struct ext4_inode *inode, int name_index,
+		const char *name, const void *value, size_t value_len)
+{
+	struct ext4_xattr_ibody_header *hdr = (struct ext4_xattr_ibody_header *) (inode + 1);
+	struct ext4_xattr_entry *first = (struct ext4_xattr_entry *) (hdr + 1);
+	char *block_end = ((char *) inode) + info.inode_size;
+
+	struct ext4_xattr_entry *result =
+		xattr_addto_range(first, block_end, first, name_index, name, value, value_len);
+
+	if (result == NULL)
 		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);
+	hdr->h_magic = cpu_to_le32(EXT4_XATTR_MAGIC);
 	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)
+
+static int xattr_addto_block(struct ext4_inode *inode, int name_index,
+		const char *name, const void *value, size_t value_len)
 {
+	struct ext4_xattr_header *header = get_xattr_block_for_inode(inode);
+	if (!header)
+		return -1;
+
+	struct ext4_xattr_entry *first = (struct ext4_xattr_entry *) (header + 1);
+	char *block_end = ((char *) header) + info.block_size;
+
+	struct ext4_xattr_entry *result =
+		xattr_addto_range(header, block_end, first, name_index, name, value, value_len);
+
+	if (result == NULL)
+		return -1;
+
+	ext4_xattr_hash_entry(header, result);
 	return 0;
 }
-#endif
+
+
+static int xattr_add(u32 inode_num, int name_index, const char *name,
+		const void *value, size_t value_len)
+{
+	if (!value)
+		return 0;
+
+	struct ext4_inode *inode = get_inode(inode_num);
+
+	if (!inode)
+		return -1;
+
+	int result = xattr_addto_inode(inode, name_index, name, value, value_len);
+	if (result != 0) {
+		result = xattr_addto_block(inode, name_index, name, value, value_len);
+	}
+	return result;
+}
+
+int inode_set_selinux(u32 inode_num, const char *secon)
+{
+	if (!secon)
+		return 0;
+
+	return xattr_add(inode_num, EXT4_XATTR_INDEX_SECURITY,
+		XATTR_SELINUX_SUFFIX, secon, strlen(secon) + 1);
+}
+
+int inode_set_capabilities(u32 inode_num, uint64_t capabilities) {
+	if (capabilities == 0)
+		return 0;
+
+	struct vfs_cap_data cap_data;
+	memset(&cap_data, 0, sizeof(cap_data));
+
+	cap_data.magic_etc = VFS_CAP_REVISION | VFS_CAP_FLAGS_EFFECTIVE;
+	cap_data.data[0].permitted = (uint32_t) (capabilities & 0xffffffff);
+	cap_data.data[0].inheritable = 0;
+	cap_data.data[1].permitted = (uint32_t) (capabilities >> 32);
+	cap_data.data[1].inheritable = 0;
+
+	return xattr_add(inode_num, EXT4_XATTR_INDEX_SECURITY,
+		XATTR_CAPS_SUFFIX, &cap_data, sizeof(cap_data));
+}
+
diff --git a/ext4_utils/contents.h b/ext4_utils/contents.h
index 35867fd..4272000 100644
--- a/ext4_utils/contents.h
+++ b/ext4_utils/contents.h
@@ -30,12 +30,14 @@
 	u32 *inode;
 	u32 mtime;
 	char *secon;
+	uint64_t capabilities;
 };
 
 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);
+u32 make_link(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);
+int inode_set_capabilities(u32 inode_num, uint64_t capabilities);
 #endif
diff --git a/ext4_utils/crc16.c b/ext4_utils/crc16.c
new file mode 100644
index 0000000..2575812
--- /dev/null
+++ b/ext4_utils/crc16.c
@@ -0,0 +1,58 @@
+/*-
+ *  COPYRIGHT (C) 1986 Gary S. Brown.  You may use this program, or
+ *  code or tables extracted from it, as desired without restriction.
+ */
+
+/* CRC32 code derived from work by Gary S. Brown. */
+
+/* Code taken from FreeBSD 8 */
+
+/* Converted to crc16 */
+
+#include "ext4_utils.h"
+
+static u16 crc16_tab[] = {
+		0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
+		0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
+		0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
+		0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
+		0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
+		0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,
+		0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,
+		0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,
+		0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
+		0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,
+		0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
+		0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
+		0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
+		0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,
+		0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,
+		0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,
+		0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
+		0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
+		0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,
+		0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
+		0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
+		0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
+		0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
+		0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
+		0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
+		0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
+		0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
+		0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
+		0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
+		0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
+		0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
+		0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040,
+};
+
+u16 ext4_crc16(u16 crc_in, const void *buf, int size)
+{
+        const u8 *p = buf;
+        u16 crc = crc_in;
+
+        while (size--)
+                crc = crc16_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8);
+
+        return crc;
+}
diff --git a/ext4_utils/ext2simg.c b/ext4_utils/ext2simg.c
index f4b6c93..7b63836 100644
--- a/ext4_utils/ext2simg.c
+++ b/ext4_utils/ext2simg.c
@@ -57,7 +57,6 @@
 {
 	off64_t ret;
 	struct ext4_super_block sb;
-	unsigned int i;
 
 	ret = lseek64(fd, 1024, SEEK_SET);
 	if (ret < 0)
diff --git a/ext4_utils/ext4_utils.c b/ext4_utils/ext4_utils.c
index 43b4480..c3bec96 100644
--- a/ext4_utils/ext4_utils.c
+++ b/ext4_utils/ext4_utils.c
@@ -25,6 +25,7 @@
 #include <fcntl.h>
 #include <sys/stat.h>
 #include <sys/types.h>
+#include <stddef.h>
 #include <string.h>
 
 #ifdef USE_MINGW
@@ -125,6 +126,7 @@
 	aux_info.bg_desc = calloc(info.block_size, aux_info.bg_desc_blocks);
 	if (!aux_info.bg_desc)
 		critical_error_errno("calloc");
+	aux_info.xattrs = NULL;
 }
 
 void ext4_free_fs_aux_info()
@@ -362,11 +364,12 @@
    block group */
 void ext4_update_free()
 {
-	unsigned int i;
+	u32 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);
+		u16 crc;
 
 		aux_info.bg_desc[i].bg_free_blocks_count = bg_free_blocks;
 		aux_info.sb->s_free_blocks_count_lo += bg_free_blocks;
@@ -375,6 +378,13 @@
 		aux_info.sb->s_free_inodes_count += bg_free_inodes;
 
 		aux_info.bg_desc[i].bg_used_dirs_count += get_directories(i);
+
+		aux_info.bg_desc[i].bg_flags = get_bg_flags(i);
+
+		crc = ext4_crc16(~0, aux_info.sb->s_uuid, sizeof(aux_info.sb->s_uuid));
+		crc = ext4_crc16(crc, &i, sizeof(i));
+		crc = ext4_crc16(crc, &aux_info.bg_desc[i], offsetof(struct ext2_group_desc, bg_checksum));
+		aux_info.bg_desc[i].bg_checksum = crc;
 	}
 }
 
diff --git a/ext4_utils/ext4_utils.h b/ext4_utils/ext4_utils.h
index cad2eae..0a9bd56 100644
--- a/ext4_utils/ext4_utils.h
+++ b/ext4_utils/ext4_utils.h
@@ -36,6 +36,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <setjmp.h>
+#include <stdint.h>
 
 #if defined(__APPLE__) && defined(__MACH__)
 #define lseek64 lseek
@@ -84,6 +85,12 @@
 #define __u16 u16
 #define __u8 u8
 
+/* XXX */
+#define cpu_to_le32(x) (x)
+#define cpu_to_le16(x) (x)
+#define le32_to_cpu(x) (x)
+#define le16_to_cpu(x) (x)
+
 typedef unsigned long long u64;
 typedef signed long long s64;
 typedef unsigned int u32;
@@ -91,6 +98,7 @@
 typedef unsigned char u8;
 
 struct block_group_info;
+struct xattr_list_element;
 
 struct ext2_group_desc {
 	__le32 bg_block_bitmap;
@@ -99,8 +107,10 @@
 	__le16 bg_free_blocks_count;
 	__le16 bg_free_inodes_count;
 	__le16 bg_used_dirs_count;
-	__le16 bg_pad;
-	__le32 bg_reserved[3];
+	__le16 bg_flags;
+	__le32 bg_reserved[2];
+	__le16 bg_reserved16;
+	__le16 bg_checksum;
 };
 
 struct fs_info {
@@ -128,6 +138,7 @@
 	struct ext4_super_block **backup_sb;
 	struct ext2_group_desc *bg_desc;
 	struct block_group_info *bgs;
+	struct xattr_list_element *xattrs;
 	u32 first_data_block;
 	u64 len_blocks;
 	u32 inode_table_blocks;
@@ -166,6 +177,17 @@
 u64 get_file_size(int fd);
 u64 parse_num(const char *arg);
 void ext4_parse_sb(struct ext4_super_block *sb);
+u16 ext4_crc16(u16 crc_in, const void *buf, int size);
+
+typedef void (*fs_config_func_t)(const char *path, int dir, unsigned *uid, unsigned *gid,
+        unsigned *mode, uint64_t *capabilities);
+
+struct selabel_handle;
+
+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,
+                         struct selabel_handle *sehnd, int verbose);
 
 #ifdef __cplusplus
 }
diff --git a/ext4_utils/ext4fixup.c b/ext4_utils/ext4fixup.c
index f0124f8..d271116 100644
--- a/ext4_utils/ext4fixup.c
+++ b/ext4_utils/ext4fixup.c
@@ -200,7 +200,6 @@
 {
     off64_t ret;
     struct ext4_super_block sb;
-    unsigned int i;
 
     read_sb(fd, &sb);
 
@@ -510,7 +509,7 @@
     return count;
 }
 
-static int get_extent_ents(int fd, struct ext4_extent_header *ext_hdr, unsigned long long *block_list)
+static int get_extent_ents(struct ext4_extent_header *ext_hdr, unsigned long long *block_list)
 {
     int i, j;
     struct ext4_extent *extent;
@@ -560,7 +559,7 @@
          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 */
+             get_extent_ents(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 */
          }
@@ -581,7 +580,7 @@
     }
 
     if (extent_hdr->eh_depth == 0) {
-         get_extent_ents(fd, (struct ext4_extent_header *)inode->i_block, block_list);
+         get_extent_ents((struct ext4_extent_header *)inode->i_block, block_list);
          return 0;
     }
 
diff --git a/ext4_utils/indirect.c b/ext4_utils/indirect.c
index 3d97ec8..cd826ac 100644
--- a/ext4_utils/indirect.c
+++ b/ext4_utils/indirect.c
@@ -387,7 +387,7 @@
 }
 
 static struct block_allocation *do_inode_allocate_indirect(
-		struct ext4_inode *inode, u32 block_len)
+		u32 block_len)
 {
 	u32 indirect_len = indirect_blocks_needed(block_len);
 
@@ -408,7 +408,7 @@
 	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);
+	alloc = do_inode_allocate_indirect(block_len);
 	if (alloc == NULL) {
 		error("failed to allocate extents for %lu bytes", len);
 		return;
@@ -494,7 +494,7 @@
 	u32 block_len = DIV_ROUND_UP(len, info.block_size);
 	u8 *data = NULL;
 
-	alloc = do_inode_allocate_indirect(inode, block_len);
+	alloc = do_inode_allocate_indirect(block_len);
 	if (alloc == NULL) {
 		error("failed to allocate extents for %lu bytes", len);
 		return NULL;
diff --git a/ext4_utils/make_ext4fs.c b/ext4_utils/make_ext4fs.c
index db6fbe6..c2a2665 100644
--- a/ext4_utils/make_ext4fs.c
+++ b/ext4_utils/make_ext4fs.c
@@ -59,6 +59,10 @@
 
 #else
 
+#include <selinux/selinux.h>
+#include <selinux/label.h>
+#include <selinux/android.h>
+
 #define O_BINARY 0
 
 #endif
@@ -97,10 +101,15 @@
 
 #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 */
+   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)
+		struct selabel_handle *sehnd, int verbose)
 {
 	int entries = 0;
 	struct dentry *dentries;
@@ -139,8 +148,8 @@
 		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);
+		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]);
 
@@ -155,30 +164,30 @@
 		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;
+		uint64_t capabilities;
 		if (fs_config_func != NULL) {
 #ifdef ANDROID
 			unsigned int mode = 0;
 			unsigned int uid = 0;
 			unsigned int gid = 0;
 			int dir = S_ISDIR(stat.st_mode);
-			fs_config_func(dentries[i].path, dir, &uid, &gid, &mode);
+			fs_config_func(dentries[i].path, dir, &uid, &gid, &mode, &capabilities);
 			dentries[i].mode = mode;
 			dentries[i].uid = uid;
 			dentries[i].gid = gid;
+			dentries[i].capabilities = capabilities;
 #else
 			error("can't set android permissions - built without android support");
 #endif
 		}
-#ifdef HAVE_SELINUX
+#ifndef USE_MINGW
 		if (sehnd) {
-			char *sepath = NULL;
-			asprintf(&sepath, "/%s", dentries[i].path);
-			if (selabel_lookup(sehnd, &dentries[i].secon, sepath, stat.st_mode) < 0) {
-				error("cannot lookup security context for %s", sepath);
+			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", sepath, dentries[i].secon);
-			free(sepath);
+
+			if (dentries[i].secon && verbose)
+				printf("Labeling %s as %s\n", dentries[i].path, dentries[i].secon);
 		}
 #endif
 
@@ -215,22 +224,17 @@
 		dentries = tmp;
 
 		dentries[0].filename = strdup("lost+found");
-		asprintf(&dentries[0].path, "%s/lost+found", dir_path);
+		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) {
-			char *sepath = NULL;
-			asprintf(&sepath, "/%s", dentries[0].path);
-			if (selabel_lookup(sehnd, &dentries[0].secon, sepath, dentries[0].mode) < 0)
+			if (selabel_lookup(sehnd, &dentries[0].secon, dentries[0].path, dentries[0].mode) < 0)
 				error("cannot lookup security context for %s", dentries[0].path);
-			free(sepath);
 		}
-#endif
 		entries++;
 		dirs++;
 	}
@@ -241,10 +245,22 @@
 		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) {
-			entry_inode = build_directory_structure(dentries[i].full_path,
-					dentries[i].path, inode, fs_config_func, sehnd);
+			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, verbose);
+			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);
+			entry_inode = make_link(dentries[i].link);
 		} else {
 			error("unknown file type on %s", dentries[i].path);
 			entry_inode = 0;
@@ -256,9 +272,20 @@
 			dentries[i].mtime);
 		if (ret)
 			error("failed to set permissions on %s\n", dentries[i].path);
+
+		/*
+		 * It's important to call inode_set_selinux() before
+		 * inode_set_capabilities(). Extended attributes need to
+		 * be stored sorted order, and we guarantee this by making
+		 * the calls in the proper order.
+		 * Please see xattr_assert_sane() in contents.c
+		 */
 		ret = inode_set_selinux(entry_inode, dentries[i].secon);
 		if (ret)
 			error("failed to set SELinux context on %s\n", dentries[i].path);
+		ret = inode_set_capabilities(entry_inode, dentries[i].capabilities);
+		if (ret)
+			error("failed to set capability on %s\n", dentries[i].path);
 
 		free(dentries[i].path);
 		free(dentries[i].full_path);
@@ -341,7 +368,16 @@
     }
 }
 
-int make_ext4fs(const char *filename, s64 len,
+int make_ext4fs_sparse_fd(int fd, long long len,
+                const char *mountpoint, struct selabel_handle *sehnd)
+{
+	reset_ext4fs_info();
+	info.len = len;
+
+	return make_ext4fs_internal(fd, NULL, mountpoint, NULL, 0, 1, 0, 0, sehnd, 0);
+}
+
+int make_ext4fs(const char *filename, long long len,
                 const char *mountpoint, struct selabel_handle *sehnd)
 {
 	int fd;
@@ -356,22 +392,92 @@
 		return EXIT_FAILURE;
 	}
 
-	status = make_ext4fs_internal(fd, NULL, mountpoint, NULL, 0, 0, 0, 1, 0, sehnd);
+	status = make_ext4fs_internal(fd, NULL, mountpoint, NULL, 0, 0, 0, 1, sehnd, 0);
 	close(fd);
 
 	return status;
 }
 
-int make_ext4fs_internal(int fd, const char *directory,
-                         char *mountpoint, fs_config_func_t fs_config_func, int gzip, int sparse,
-                         int crc, int wipe, int init_itabs, struct selabel_handle *sehnd)
+/* return a newly-malloc'd string that is a copy of str.  The new string
+   is guaranteed to have a trailing slash.  If absolute is true, the new string
+   is also guaranteed to have a leading slash.
+*/
+static char *canonicalize_slashes(const char *str, bool absolute)
+{
+	char *ret;
+	int len = strlen(str);
+	int newlen = len;
+	char *ptr;
+
+	if (len == 0 && absolute) {
+		return strdup("/");
+	}
+
+	if (str[0] != '/' && absolute) {
+		newlen++;
+	}
+	if (str[len - 1] != '/') {
+		newlen++;
+	}
+	ret = malloc(newlen + 1);
+	if (!ret) {
+		critical_error("malloc");
+	}
+
+	ptr = ret;
+	if (str[0] != '/' && absolute) {
+		*ptr++ = '/';
+	}
+
+	strcpy(ptr, str);
+	ptr += len;
+
+	if (str[len - 1] != '/') {
+		*ptr++ = '/';
+	}
+
+	if (ptr != ret + newlen) {
+		critical_error("assertion failed\n");
+	}
+
+	*ptr = '\0';
+
+	return ret;
+}
+
+static char *canonicalize_abs_slashes(const char *str)
+{
+	return canonicalize_slashes(str, true);
+}
+
+static char *canonicalize_rel_slashes(const char *str)
+{
+	return canonicalize_slashes(str, false);
+}
+
+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,
+                         struct selabel_handle *sehnd, int verbose)
 {
 	u32 root_inode_num;
 	u16 root_mode;
+	char *mountpoint;
+	char *directory = NULL;
 
 	if (setjmp(setjmp_env))
 		return EXIT_FAILURE; /* Handle a call to longjmp() */
 
+	if (_mountpoint == NULL) {
+		mountpoint = strdup("");
+	} else {
+		mountpoint = canonicalize_abs_slashes(_mountpoint);
+	}
+
+	if (_directory) {
+		directory = canonicalize_rel_slashes(_directory);
+	}
+
 	if (info.len <= 0)
 		info.len = get_file_size(fd);
 
@@ -409,11 +515,13 @@
 	info.inodes_per_group = compute_inodes_per_group();
 
 	info.feat_compat |=
-			EXT4_FEATURE_COMPAT_RESIZE_INODE;
+			EXT4_FEATURE_COMPAT_RESIZE_INODE |
+			EXT4_FEATURE_COMPAT_EXT_ATTR;
 
 	info.feat_ro_compat |=
 			EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER |
-			EXT4_FEATURE_RO_COMPAT_LARGE_FILE;
+			EXT4_FEATURE_RO_COMPAT_LARGE_FILE |
+			EXT4_FEATURE_RO_COMPAT_GDT_CSUM;
 
 	info.feat_incompat |=
 			EXT4_FEATURE_INCOMPAT_EXTENTS |
@@ -459,7 +567,7 @@
 #else
 	if (directory)
 		root_inode_num = build_directory_structure(directory, mountpoint, 0,
-                        fs_config_func, sehnd);
+                        fs_config_func, sehnd, verbose);
 	else
 		root_inode_num = build_default_directory_structure();
 #endif
@@ -467,34 +575,25 @@
 	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
+#ifndef USE_MINGW
 	if (sehnd) {
-		char *sepath = NULL;
 		char *secontext = NULL;
 
-		if (mountpoint[0] == '/')
-			sepath = strdup(mountpoint);
-		else
-			asprintf(&sepath, "/%s", mountpoint);
-		if (!sepath)
-			critical_error_errno("malloc");
-		if (selabel_lookup(sehnd, &secontext, sepath, S_IFDIR) < 0) {
-			error("cannot lookup security context for %s", sepath);
+		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", sepath, secontext);
+			if (verbose) {
+				printf("Labeling %s as %s\n", mountpoint, secontext);
+			}
 			inode_set_selinux(root_inode_num, secontext);
 		}
-		free(sepath);
 		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",
@@ -511,5 +610,8 @@
 	sparse_file_destroy(info.sparse_file);
 	info.sparse_file = NULL;
 
+	free(mountpoint);
+	free(directory);
+
 	return 0;
 }
diff --git a/ext4_utils/make_ext4fs.h b/ext4_utils/make_ext4fs.h
index c217c3d..3784a9e 100644
--- a/ext4_utils/make_ext4fs.h
+++ b/ext4_utils/make_ext4fs.h
@@ -17,29 +17,16 @@
 #ifndef _MAKE_EXT4FS_H_
 #define _MAKE_EXT4FS_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, unsigned *uid, unsigned *gid,
-        unsigned *mode);
-
-void reset_ext4fs_info();
-int make_ext4fs(const char *filename, s64 len,
+int make_ext4fs(const char *filename, long long len,
                 const char *mountpoint, struct selabel_handle *sehnd);
-int make_ext4fs_internal(int fd, const char *directory,
-                         char *mountpoint, fs_config_func_t fs_config_func, int gzip, int sparse,
-                         int crc, int wipe, int init_itabs, struct selabel_handle *sehnd);
+int make_ext4fs_sparse_fd(int fd, long long len,
+                const char *mountpoint, struct selabel_handle *sehnd);
 
 #ifdef __cplusplus
 }
diff --git a/ext4_utils/make_ext4fs_main.c b/ext4_utils/make_ext4fs_main.c
index f7beeb5..b6c740d 100644
--- a/ext4_utils/make_ext4fs_main.c
+++ b/ext4_utils/make_ext4fs_main.c
@@ -16,6 +16,7 @@
 
 #include <fcntl.h>
 #include <libgen.h>
+#include <stdio.h>
 #include <unistd.h>
 
 #if defined(__linux__)
@@ -28,7 +29,16 @@
 #include <private/android_filesystem_config.h>
 #endif
 
+#ifndef USE_MINGW
+#include <selinux/selinux.h>
+#include <selinux/label.h>
+#include <selinux/android.h>
+#else
+struct selabel_handle;
+#endif
+
 #include "make_ext4fs.h"
+#include "ext4_utils.h"
 
 #ifndef USE_MINGW /* O_BINARY is windows-specific flag */
 #define O_BINARY 0
@@ -43,7 +53,7 @@
 	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 ]\n");
+	fprintf(stderr, "    [ -z | -s ] [ -w ] [ -c ] [ -J ] [ -v ]\n");
 	fprintf(stderr, "    <filename> [<directory>]\n");
 }
 
@@ -52,21 +62,21 @@
 	int opt;
 	const char *filename = NULL;
 	const char *directory = NULL;
-	char *mountpoint = "";
+	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 fd;
 	int exitcode;
+	int verbose = 0;
 	struct selabel_handle *sehnd = NULL;
-#ifdef HAVE_SELINUX
+#ifndef USE_MINGW
 	struct selinux_opt seopts[] = { { SELABEL_OPT_PATH, "" } };
 #endif
 
-	while ((opt = getopt(argc, argv, "l:j:b:g:i:I:L:a:fwzJsctS:")) != -1) {
+	while ((opt = getopt(argc, argv, "l:j:b:g:i:I:L:a:S:fwzJsctv")) != -1) {
 		switch (opt) {
 		case 'l':
 			info.len = parse_num(optarg);
@@ -118,10 +128,10 @@
 			sparse = 1;
 			break;
 		case 't':
-			init_itabs = 1;
+			fprintf(stderr, "Warning: -t (initialize inode tables) is deprecated\n");
 			break;
 		case 'S':
-#ifdef HAVE_SELINUX
+#ifndef USE_MINGW
 			seopts[0].value = optarg;
 			sehnd = selabel_open(SELABEL_CTX_FILE, seopts, 1);
 			if (!sehnd) {
@@ -129,13 +139,28 @@
 				exit(EXIT_FAILURE);
 			}
 #endif
-			   break;
+			break;
+		case 'v':
+			verbose = 1;
+			break;
 		default: /* '?' */
 			usage(argv[0]);
 			exit(EXIT_FAILURE);
 		}
 	}
 
+#if !defined(HOST)
+	// Use only if -S option not requested
+	if (!sehnd && mountpoint) {
+		sehnd = selinux_android_file_context_handle();
+
+		if (!sehnd) {
+			perror(optarg);
+			exit(EXIT_FAILURE);
+		}
+	}
+#endif
+
 	if (wipe && sparse) {
 		fprintf(stderr, "Cannot specifiy both wipe and sparse\n");
 		usage(argv[0]);
@@ -168,7 +193,7 @@
 	if (strcmp(filename, "-")) {
 		fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
 		if (fd < 0) {
-			error_errno("open");
+			perror("open");
 			return EXIT_FAILURE;
 		}
 	} else {
@@ -176,7 +201,7 @@
 	}
 
 	exitcode = make_ext4fs_internal(fd, directory, mountpoint, fs_config_func, gzip,
-			sparse, crc, wipe, init_itabs, sehnd);
+			sparse, crc, wipe, sehnd, verbose);
 	close(fd);
 
 	return exitcode;
diff --git a/ext4_utils/mkuserimg.sh b/ext4_utils/mkuserimg.sh
index 1136a9e..c44129e 100755
--- a/ext4_utils/mkuserimg.sh
+++ b/ext4_utils/mkuserimg.sh
@@ -17,7 +17,7 @@
   shift
 fi
 
-if [ $# -ne 4 -a $# -ne 5 -a $# -ne 6 ]; then
+if [ $# -ne 5 -a $# -ne 6 ]; then
   usage
   exit 1
 fi
@@ -45,7 +45,8 @@
 fi
 
 if [ -z $SIZE ]; then
-    SIZE=128M
+  echo "Need size of filesystem"
+  exit 2
 fi
 
 if [ -n "$FC" ]; then
diff --git a/ext4_utils/xattr.h b/ext4_utils/xattr.h
index 2c6d9cc..60c01ce 100644
--- a/ext4_utils/xattr.h
+++ b/ext4_utils/xattr.h
@@ -1,8 +1,24 @@
 #include <sys/types.h>
 
+#ifndef _SYSTEM_EXTRAS_EXT4_UTILS_XATTR_H
+#define _SYSTEM_EXTRAS_EXT4_UTILS_XATTR_H 1
+
 #define EXT4_XATTR_MAGIC 0xEA020000
 #define EXT4_XATTR_INDEX_SECURITY 6
 
+struct ext4_xattr_header {
+    __le32  h_magic;
+    __le32  h_refcount;
+    __le32  h_blocks;
+    __le32  h_hash;
+    __le32  h_checksum;
+    __u32   h_reserved[3];
+};
+
+struct ext4_xattr_ibody_header {
+    __le32  h_magic;
+};
+
 struct ext4_xattr_entry {
     __u8 e_name_len;
     __u8 e_name_index;
@@ -19,5 +35,11 @@
 #define EXT4_XATTR_LEN(name_len) \
     (((name_len) + EXT4_XATTR_ROUND + \
     sizeof(struct ext4_xattr_entry)) & ~EXT4_XATTR_ROUND)
+#define EXT4_XATTR_NEXT(entry) \
+    ((struct ext4_xattr_entry *)( \
+     (char *)(entry) + EXT4_XATTR_LEN((entry)->e_name_len)))
 #define EXT4_XATTR_SIZE(size) \
     (((size) + EXT4_XATTR_ROUND) & ~EXT4_XATTR_ROUND)
+#define IS_LAST_ENTRY(entry) (*(__u32 *)(entry) == 0)
+
+#endif /* !_SYSTEM_EXTRAS_EXT4_UTILS_XATTR_H */
diff --git a/micro_bench/Android.mk b/micro_bench/Android.mk
index 0e819c3..09913ff 100644
--- a/micro_bench/Android.mk
+++ b/micro_bench/Android.mk
@@ -1,7 +1,7 @@
 LOCAL_PATH:= $(call my-dir)
 include $(CLEAR_VARS)
 
-LOCAL_SRC_FILES := micro_bench.c
+LOCAL_SRC_FILES := micro_bench.cpp
 
 LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
 LOCAL_MODULE_TAGS := debug
diff --git a/micro_bench/micro_bench.c b/micro_bench/micro_bench.c
deleted file mode 100644
index df6e169..0000000
--- a/micro_bench/micro_bench.c
+++ /dev/null
@@ -1,201 +0,0 @@
-/*
-** Copyright 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.
-*/
-
-/*
- * Some quick and dirty micro-benchmarks
- */
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <errno.h>
-#include <sys/uio.h>
-#include <unistd.h>
-#include <sys/time.h>
-#include <stdint.h>
-#include <string.h>
-
-/* tv2 -= tv1 */
-static void tv_sub(struct timeval *tv2, struct timeval *tv1) {
-        tv2->tv_sec -= tv1->tv_sec;
-        tv2->tv_usec -= tv1->tv_usec;
-        while (tv2->tv_usec < 0) {
-            tv2->tv_usec += 1000000;
-            tv2->tv_sec -= 1;
-        }
-}
-
-static int do_sleep(int iters, int delay) {
-    struct timeval tv1;
-    struct timeval tv2;
-    int i;
-
-    for (i = 0; iters == -1 || i < iters; i++) {
-        gettimeofday(&tv1, NULL);
-        sleep(delay);
-        gettimeofday(&tv2, NULL);
-
-        tv_sub(&tv2, &tv1);
-
-        printf("sleep(%d) took %ld.%06ld seconds\n", delay, tv2.tv_sec, tv2.tv_usec);
-    }
-
-    return 0;
-}
-
-int cpu_foo;
-
-static int do_cpu(int iters, int a) {
-    struct timeval tv1;
-    struct timeval tv2;
-    int i;
-
-    for (i = 0; iters == -1 || i < iters; i++) {
-        gettimeofday(&tv1, NULL);
-        for (cpu_foo = 0; cpu_foo < 100000000; cpu_foo++);
-        gettimeofday(&tv2, NULL);
-
-        tv_sub(&tv2, &tv1);
-
-        printf("cpu took %ld.%06ld seconds\n", tv2.tv_sec, tv2.tv_usec);
-    }
-    return 0;
-}
-
-static double mb_sec(unsigned long bytes, struct timeval *delta) {
-    unsigned long us = delta->tv_sec * 1000000 + delta->tv_usec;
-    return (double)bytes * 1000000.0 / 1048576.0 / (double)us;
-}
-
-static int do_memset(int iters, int sz) {
-    struct timeval tv1;
-    struct timeval tv2;
-    int i, j;
-
-    uint8_t *b = malloc(sz);
-    if (!b) return -1;
-    int c = 1000000000/sz;
-
-    for (i = 0; iters == -1 || i < iters; i++) {
-        gettimeofday(&tv1, NULL);
-        for (j = 0; j < c; j++)
-            memset(b, 0, sz);
-
-        gettimeofday(&tv2, NULL);
-
-        tv_sub(&tv2, &tv1);
-
-        printf("memset %dx%d bytes took %ld.%06ld seconds (%f MB/s)\n",
-                c, sz, tv2.tv_sec, tv2.tv_usec, mb_sec(c*sz, &tv2));
-    }
-    return 0;
-}
-
-static int do_memcpy(int iters, int sz) {
-    struct timeval tv1;
-    struct timeval tv2;
-    int i, j;
-
-    uint8_t *a = malloc(sz);
-    if (!a) return -1;
-    uint8_t *b = malloc(sz);
-    if (!b) return -1;
-    int c = 1000000000/sz;
-
-    for (i = 0; iters == -1 || i < iters; i++) {
-        gettimeofday(&tv1, NULL);
-        for (j = 0; j < c; j++)
-            memcpy(b, a, sz);
-
-        gettimeofday(&tv2, NULL);
-
-        tv_sub(&tv2, &tv1);
-
-        printf("memcpy %dx%d bytes took %ld.%06ld seconds (%f MB/s)\n",
-                c, sz, tv2.tv_sec, tv2.tv_usec, mb_sec(c*sz, &tv2));
-    }
-    return 0;
-}
-
-int foo;
-
-static int do_memread(int iters, int sz) {
-    struct timeval tv1;
-    struct timeval tv2;
-    int i, j, k;
-
-    int *b = malloc(sz);
-    if (!b) return -1;
-    int c = 1000000000/sz;
-
-    for (i = 0; iters == -1 || i < iters; i++) {
-        gettimeofday(&tv1, NULL);
-        for (j = 0; j < c; j++)
-            for (k = 0; k < sz/4; k++)
-                foo = b[k];
-
-        gettimeofday(&tv2, NULL);
-
-        tv_sub(&tv2, &tv1);
-
-        printf("read %dx%d bytes took %ld.%06ld seconds (%f MB/s)\n",
-                c, sz, tv2.tv_sec, tv2.tv_usec, mb_sec(c*sz, &tv2));
-    }
-    return 0;
-}
-
-struct {
-    char *name;
-    int (*ptr)(int, int);
-} function_table[]  = {
-    {"sleep", do_sleep},
-    {"cpu", do_cpu},
-    {"memset", do_memset},
-    {"memcpy", do_memcpy},
-    {"memread", do_memread},
-    {NULL, NULL},
-};
-
-static void usage() {
-    int i;
-
-    printf("Usage:\n");
-    for (i = 0; function_table[i].name; i++) {
-        printf("\tmicro_bench %s ARG [ITERS]\n", function_table[i].name);
-    }
-}
-
-int main(int argc, char **argv) {
-    int i;
-    int iters;
-
-    if (argc < 3 || argc > 4) {
-        usage();
-        return -1;
-    }
-    if (argc == 3) {
-        iters = -1;
-    } else {
-        iters = atoi(argv[3]);
-    }
-    for (i = 0; function_table[i].name; i++) {
-        if (!strcmp(argv[1], function_table[i].name)) {
-            printf("%s\n", function_table[i].name);
-            return (*function_table[i].ptr)(iters, atoi(argv[2]));
-        }
-    }
-    usage();
-    return -1;
-}
diff --git a/micro_bench/micro_bench.cpp b/micro_bench/micro_bench.cpp
new file mode 100644
index 0000000..b8d82f6
--- /dev/null
+++ b/micro_bench/micro_bench.cpp
@@ -0,0 +1,484 @@
+/*
+** Copyright 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.
+*/
+
+/*
+ * Micro-benchmarking of sleep/cpu speed/memcpy/memset/memory reads.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <math.h>
+#include <sched.h>
+#include <sys/resource.h>
+#include <time.h>
+#include <unistd.h>
+
+// The default size of data that will be manipulated in each iteration of
+// a memory benchmark. Can be modified with the --data_size option.
+#define DEFAULT_DATA_SIZE       1000000000
+
+// Number of nanoseconds in a second.
+#define NS_PER_SEC              1000000000
+
+// The maximum number of arguments that a benchmark will accept.
+#define MAX_ARGS    2
+
+// Use macros to compute values to try and avoid disturbing memory as much
+// as possible after each iteration.
+#define COMPUTE_AVERAGE_KB(avg_kb, bytes, time_ns) \
+        avg_kb = ((bytes) / 1024.0) / ((double)(time_ns) / NS_PER_SEC);
+
+#define COMPUTE_RUNNING(avg, running_avg, square_avg, cur_idx) \
+    running_avg = ((running_avg) / ((cur_idx) + 1)) * (cur_idx) + (avg) / ((cur_idx) + 1); \
+    square_avg = ((square_avg) / ((cur_idx) + 1)) * (cur_idx) + ((avg) / ((cur_idx) + 1)) * (avg);
+
+#define GET_STD_DEV(running_avg, square_avg) \
+    sqrt((square_avg) - (running_avg) * (running_avg))
+
+// Contains information about benchmark options.
+typedef struct {
+    bool print_average;
+    bool print_each_iter;
+
+    int dst_align;
+    int src_align;
+
+    int cpu_to_lock;
+
+    int data_size;
+
+    int args[MAX_ARGS];
+    int num_args;
+} command_data_t;
+
+// Struct that contains a mapping of benchmark name to benchmark function.
+typedef struct {
+    const char *name;
+    int (*ptr)(const command_data_t &cmd_data);
+} function_t;
+
+// Get the current time in nanoseconds.
+uint64_t nanoTime() {
+  struct timespec t;
+
+  t.tv_sec = t.tv_nsec = 0;
+  clock_gettime(CLOCK_MONOTONIC, &t);
+  return static_cast<uint64_t>(t.tv_sec) * NS_PER_SEC + t.tv_nsec;
+}
+
+// Allocate memory with a specific alignment and return that pointer.
+// This function assumes an alignment value that is a power of 2.
+// If the alignment is 0, then use the pointer returned by malloc.
+uint8_t *allocateAlignedMemory(size_t size, int alignment) {
+  uint64_t ptr = reinterpret_cast<uint64_t>(malloc(size + 2 * alignment));
+  if (!ptr)
+      return NULL;
+  if (alignment > 0) {
+      // When setting the alignment, set it to exactly the alignment chosen.
+      // The pointer returned will be guaranteed not to be aligned to anything
+      // more than that.
+      ptr += alignment - (ptr & (alignment - 1));
+      ptr |= alignment;
+  }
+
+  return reinterpret_cast<uint8_t*>(ptr);
+}
+
+int benchmarkSleep(const command_data_t &cmd_data) {
+    uint64_t time_ns;
+
+    int delay = cmd_data.args[0];
+    int iters = cmd_data.args[1];
+    bool print_each_iter = cmd_data.print_each_iter;
+    bool print_average = cmd_data.print_average;
+    double avg, running_avg = 0.0, square_avg = 0.0;
+    for (int i = 0; iters == -1 || i < iters; i++) {
+        time_ns = nanoTime();
+        sleep(delay);
+        time_ns = nanoTime() - time_ns;
+
+        avg = (double)time_ns / NS_PER_SEC;
+
+        if (print_average) {
+            COMPUTE_RUNNING(avg, running_avg, square_avg, i);
+        }
+
+        if (print_each_iter) {
+            printf("sleep(%d) took %.06f seconds\n", delay, avg);
+        }
+    }
+
+    if (print_average) {
+        printf("  sleep(%d) average %.06f seconds std dev %f\n", delay,
+               running_avg, GET_STD_DEV(running_avg, square_avg));
+    }
+
+    return 0;
+}
+
+int benchmarkCpu(const command_data_t &cmd_data) {
+    // Use volatile so that the loop is not optimized away by the compiler.
+    volatile int cpu_foo;
+
+    uint64_t time_ns;
+    int iters = cmd_data.args[1];
+    bool print_each_iter = cmd_data.print_each_iter;
+    bool print_average = cmd_data.print_average;
+    double avg, running_avg = 0.0, square_avg = 0.0;
+    for (int i = 0; iters == -1 || i < iters; i++) {
+        time_ns = nanoTime();
+        for (cpu_foo = 0; cpu_foo < 100000000; cpu_foo++);
+        time_ns = nanoTime() - time_ns;
+
+        avg = (double)time_ns / NS_PER_SEC;
+
+        if (print_average) {
+            COMPUTE_RUNNING(avg, running_avg, square_avg, i);
+        }
+
+        if (print_each_iter) {
+            printf("cpu took %.06f seconds\n", avg);
+        }
+    }
+
+    if (print_average) {
+        printf("  cpu average %.06f seconds std dev %f\n",
+               running_avg, GET_STD_DEV(running_avg, square_avg));
+    }
+
+    return 0;
+}
+
+int benchmarkMemset(const command_data_t &cmd_data) {
+    int size = cmd_data.args[0];
+    int iters = cmd_data.args[1];
+
+    uint8_t *dst = allocateAlignedMemory(size, cmd_data.dst_align);
+    if (!dst)
+        return -1;
+
+    double avg_kb, running_avg_kb = 0.0, square_avg_kb = 0.0;
+    uint64_t time_ns;
+    int j;
+    bool print_average = cmd_data.print_average;
+    bool print_each_iter = cmd_data.print_each_iter;
+    int copies = cmd_data.data_size/size;
+    for (int i = 0; iters == -1 || i < iters; i++) {
+        time_ns = nanoTime();
+        for (j = 0; j < copies; j++)
+            memset(dst, 0, size);
+        time_ns = nanoTime() - time_ns;
+
+        // Compute in kb to avoid any overflows.
+        COMPUTE_AVERAGE_KB(avg_kb, copies * size, time_ns);
+
+        if (print_average) {
+            COMPUTE_RUNNING(avg_kb, running_avg_kb, square_avg_kb, i);
+        }
+
+        if (print_each_iter) {
+            printf("memset %dx%d bytes took %.06f seconds (%f MB/s)\n",
+                   copies, size, (double)time_ns / NS_PER_SEC, avg_kb / 1024.0);
+        }
+    }
+
+    if (print_average) {
+        printf("  memset %dx%d bytes average %.2f MB/s std dev %.4f\n",
+               copies, size, running_avg_kb / 1024.0,
+               GET_STD_DEV(running_avg_kb, square_avg_kb) / 1024.0);
+    }
+    return 0;
+}
+
+int benchmarkMemcpy(const command_data_t &cmd_data) {
+    int size = cmd_data.args[0];
+    int iters = cmd_data.args[1];
+
+    uint8_t *src = allocateAlignedMemory(size, cmd_data.src_align);
+    if (!src)
+        return -1;
+    uint8_t *dst = allocateAlignedMemory(size, cmd_data.dst_align);
+    if (!dst)
+        return -1;
+
+    uint64_t time_ns;
+    double avg_kb, running_avg_kb = 0.0, square_avg_kb = 0.0;
+    int j;
+    bool print_average = cmd_data.print_average;
+    bool print_each_iter = cmd_data.print_each_iter;
+    int copies = cmd_data.data_size / size;
+    for (int i = 0; iters == -1 || i < iters; i++) {
+        time_ns = nanoTime();
+        for (j = 0; j < copies; j++)
+            memcpy(dst, src, size);
+        time_ns = nanoTime() - time_ns;
+
+        // Compute in kb to avoid any overflows.
+        COMPUTE_AVERAGE_KB(avg_kb, copies * size, time_ns);
+
+        if (print_average) {
+            COMPUTE_RUNNING(avg_kb, running_avg_kb, square_avg_kb, i);
+        }
+
+        if (print_each_iter) {
+            printf("memcpy %dx%d bytes took %.06f seconds (%f MB/s)\n",
+                   copies, size, (double)time_ns / NS_PER_SEC, avg_kb / 1024.0);
+        }
+    }
+    if (print_average) {
+        printf("  memcpy %dx%d bytes average %.2f MB/s std dev %.4f\n",
+               copies, size, running_avg_kb/1024.0,
+               GET_STD_DEV(running_avg_kb, square_avg_kb) / 1024.0);
+    }
+    return 0;
+}
+
+int benchmarkMemread(const command_data_t &cmd_data) {
+    int size = cmd_data.args[0];
+    int iters = cmd_data.args[1];
+
+    int *src = reinterpret_cast<int*>(malloc(size));
+    if (!src)
+        return -1;
+
+    // Use volatile so the compiler does not optimize away the reads.
+    volatile int foo;
+    uint64_t time_ns;
+    int j, k;
+    double avg_kb, running_avg_kb = 0.0, square_avg_kb = 0.0;
+    bool print_average = cmd_data.print_average;
+    bool print_each_iter = cmd_data.print_each_iter;
+    int c = cmd_data.data_size / size;
+    for (int i = 0; iters == -1 || i < iters; i++) {
+        time_ns = nanoTime();
+        for (j = 0; j < c; j++)
+            for (k = 0; k < size/4; k++)
+                foo = src[k];
+        time_ns = nanoTime() - time_ns;
+
+        // Compute in kb to avoid any overflows.
+        COMPUTE_AVERAGE_KB(avg_kb, c * size, time_ns);
+
+        if (print_average) {
+            COMPUTE_RUNNING(avg_kb, running_avg_kb, square_avg_kb, i);
+        }
+
+        if (print_each_iter) {
+            printf("read %dx%d bytes took %.06f seconds (%f MB/s)\n",
+                   c, size, (double)time_ns / NS_PER_SEC, avg_kb / 1024.0);
+        }
+    }
+
+    if (print_average) {
+        printf("  read %dx%d bytes average %.2f MB/s std dev %.4f\n",
+               c, size, running_avg_kb/1024.0,
+               GET_STD_DEV(running_avg_kb, square_avg_kb) / 1024.0);
+    }
+
+    return 0;
+}
+
+// Create the mapping structure.
+function_t function_table[] = {
+    { "sleep", benchmarkSleep },
+    { "cpu", benchmarkCpu },
+    { "memset", benchmarkMemset },
+    { "memcpy", benchmarkMemcpy },
+    { "memread", benchmarkMemread },
+    { NULL, NULL }
+};
+
+void usage() {
+    printf("Usage:\n");
+    printf("  micro_bench [--data_size DATA_BYTES] [--print_average]\n");
+    printf("              [--no_print_each_iter] [--lock_to_cpu CORE]\n");
+    printf("    --data_size DATA_BYTES\n");
+    printf("      For the data benchmarks (memcpy/memset/memread) the approximate\n");
+    printf("      size of data, in bytes, that will be manipulated in each iteration.\n");
+    printf("    --print_average\n");
+    printf("      Print the average and standard deviation of all iterations.\n");
+    printf("    --no_print_each_iter\n");
+    printf("      Do not print any values in each iteration.\n");
+    printf("    --lock_to_cpu CORE\n");
+    printf("      Lock to the specified CORE. The default is to use the last core found.\n");
+    printf("    ITERS\n");
+    printf("      The number of iterations to execute each benchmark. If not\n");
+    printf("      passed in then run forever.\n");
+    printf("  micro_bench sleep TIME_TO_SLEEP [ITERS]\n");
+    printf("    TIME_TO_SLEEP\n");
+    printf("      The time in seconds to sleep.\n");
+    printf("  micro_bench cpu UNUSED [ITERS]\n");
+    printf("  micro_bench [--dst_align ALIGN] memset NUM_BYTES [ITERS]\n");
+    printf("    --dst_align ALIGN\n");
+    printf("      Align the memset destination pointer to ALIGN. The default is to use the\n");
+    printf("      value returned by malloc.\n");
+    printf("  micro_bench [--src_align ALIGN] [--dst_align ALIGN] memcpy NUM_BYTES [ITERS]\n");
+    printf("    --src_align ALIGN\n");
+    printf("      Align the memcpy source pointer to ALIGN. The default is to use the\n");
+    printf("      value returned by malloc.\n");
+    printf("    --dst_align ALIGN\n");
+    printf("      Align the memcpy destination pointer to ALIGN. The default is to use the\n");
+    printf("      value returned by malloc.\n");
+    printf("  micro_bench memread NUM_BYTES [ITERS]\n");
+}
+
+function_t *processOptions(int argc, char **argv, command_data_t *cmd_data) {
+    function_t *command = NULL;
+
+    // Initialize the command_flags.
+    cmd_data->print_average = false;
+    cmd_data->print_each_iter = true;
+    cmd_data->dst_align = 0;
+    cmd_data->src_align = 0;
+    cmd_data->num_args = 0;
+    cmd_data->cpu_to_lock = -1;
+    cmd_data->data_size = DEFAULT_DATA_SIZE;
+    for (int i = 0; i < MAX_ARGS; i++) {
+        cmd_data->args[i] = -1;
+    }
+
+    for (int i = 1; i < argc; i++) {
+        if (argv[i][0] == '-') {
+            int *save_value = NULL;
+            if (strcmp(argv[i], "--print_average") == 0) {
+              cmd_data->print_average = true;
+            } else if (strcmp(argv[i], "--no_print_each_iter") == 0) {
+              cmd_data->print_each_iter = false;
+            } else if (strcmp(argv[i], "--dst_align") == 0) {
+              save_value = &cmd_data->dst_align;
+            } else if (strcmp(argv[i], "--src_align") == 0) {
+              save_value = &cmd_data->src_align;
+            } else if (strcmp(argv[i], "--lock_to_cpu") == 0) {
+              save_value = &cmd_data->cpu_to_lock;
+            } else if (strcmp(argv[i], "--data_size") == 0) {
+              save_value = &cmd_data->data_size;
+            } else {
+                printf("Unknown option %s\n", argv[i]);
+                return NULL;
+            }
+            if (save_value) {
+                // Checking both characters without a strlen() call should be
+                // safe since as long as the argument exists, one character will
+                // be present (\0). And if the first character is '-', then
+                // there will always be a second character (\0 again).
+                if (i == argc - 1 || (argv[i + 1][0] == '-' && !isdigit(argv[i + 1][1]))) {
+                    printf("The option %s requires one argument.\n",
+                           argv[i]);
+                    return NULL;
+                }
+                *save_value = atoi(argv[++i]);
+            }
+        } else if (!command) {
+            for (function_t *function = function_table; function->name != NULL; function++) {
+                if (strcmp(argv[i], function->name) == 0) {
+                    command = function;
+                    break;
+                }
+            }
+            if (!command) {
+                printf("Uknown command %s\n", argv[i]);
+                return NULL;
+            }
+        } else if (cmd_data->num_args > MAX_ARGS) {
+            printf("More than %d number arguments passed in.\n", MAX_ARGS);
+            return NULL;
+        } else {
+            cmd_data->args[cmd_data->num_args++] = atoi(argv[i]);
+        }
+    }
+
+    // Check the arguments passed in make sense.
+    if (cmd_data->num_args != 1 && cmd_data->num_args != 2) {
+        printf("Not enough arguments passed in.\n");
+        return NULL;
+    } else if (cmd_data->dst_align < 0) {
+        printf("The --dst_align option must be greater than or equal to 0.\n");
+        return NULL;
+    } else if (cmd_data->src_align < 0) {
+        printf("The --src_align option must be greater than or equal to 0.\n");
+        return NULL;
+    } else if (cmd_data->data_size <= 0) {
+        printf("The --data_size option must be a positive number.\n");
+        return NULL;
+    } else if ((cmd_data->dst_align & (cmd_data->dst_align - 1))) {
+        printf("The --dst_align option must be a power of 2.\n");
+        return NULL;
+    } else if ((cmd_data->src_align & (cmd_data->src_align - 1))) {
+        printf("The --src_align option must be a power of 2.\n");
+        return NULL;
+    }
+
+    return command;
+}
+
+bool raisePriorityAndLock(int cpu_to_lock) {
+    cpu_set_t cpuset;
+
+    if (setpriority(PRIO_PROCESS, 0, -20)) {
+        perror("Unable to raise priority of process.\n");
+        return false;
+    }
+
+    CPU_ZERO(&cpuset);
+    if (sched_getaffinity(0, sizeof(cpuset), &cpuset) != 0) {
+        perror("sched_getaffinity failed");
+        return false;
+    }
+
+    if (cpu_to_lock < 0) {
+        // Lock to the last active core we find.
+        for (int i = 0; i < CPU_SETSIZE; i++) {
+            if (CPU_ISSET(i, &cpuset)) {
+                cpu_to_lock = i;
+            }
+        }
+    } else if (!CPU_ISSET(cpu_to_lock, &cpuset)) {
+        printf("Cpu %d does not exist.\n", cpu_to_lock);
+        return false;
+    }
+
+    if (cpu_to_lock < 0) {
+        printf("Cannot find any valid cpu to lock.\n");
+        return false;
+    }
+
+    CPU_ZERO(&cpuset);
+    CPU_SET(cpu_to_lock, &cpuset);
+    if (sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0) {
+        perror("sched_setaffinity failed");
+        return false;
+    }
+
+    return true;
+}
+
+int main(int argc, char **argv) {
+    command_data_t cmd_data;
+
+    function_t *command = processOptions(argc, argv, &cmd_data);
+    if (!command) {
+      usage();
+      return -1;
+    }
+
+    if (!raisePriorityAndLock(cmd_data.cpu_to_lock)) {
+      return -1;
+    }
+
+    printf("%s\n", command->name);
+    return (*command->ptr)(cmd_data);
+}
diff --git a/tests/bionic/libc/Android.mk b/tests/bionic/libc/Android.mk
index 52ef01e..fc3a00e 100644
--- a/tests/bionic/libc/Android.mk
+++ b/tests/bionic/libc/Android.mk
@@ -152,9 +152,7 @@
 
 sources := \
     other/bench_locks.c \
-    other/test_aligned.c \
     other/test_arc4random.c \
-    other/test_atomics.c \
     other/test_sysconf.c \
     other/test_system.c \
     other/test_thread_max.c \
@@ -163,6 +161,11 @@
     other/test_timer_create3.c \
     other/test_vfprintf_leak.c \
 
+ifeq ($(TARGET_ARCH),arm)
+sources += \
+    other/test_atomics.c
+endif
+
 $(call device-test, $(sources))
 
 # The relocations test is a bit special, since we need
diff --git a/tests/bionic/libc/other/test_aligned.c b/tests/bionic/libc/other/test_aligned.c
deleted file mode 100644
index 8a66dd6..0000000
--- a/tests/bionic/libc/other/test_aligned.c
+++ /dev/null
@@ -1,117 +0,0 @@
-#include <stdio.h>
-#include <arpa/inet.h>  /* for htons() etc.. */
-
-static char  tab[8];
-
-static void
-read4( int  o, unsigned val )
-{
-    unsigned  v = htonl(val);
-    unsigned  v2;
-
-    tab[o+0] = (char)(v >> 24);
-    tab[o+1] = (char)(v >> 16);
-    tab[o+2] = (char)(v >> 8);
-    tab[o+3] = (char)(v);
-
-    printf( "read4: offset=%d value=%08x: ", o, val );
-    fflush(stdout);
-
-    v2 = *(unsigned*)(tab+o);
-
-    if (v2 != val) {
-        printf( "FAIL (%08x)\n", v2 );
-    } else {
-        printf( "ok\n" );
-    }
-}
-
-static void
-writ4( int  o, unsigned val )
-{
-    unsigned  v = htonl(val);
-    unsigned  v2;
-
-    printf( "writ4: offset=%d value=%08x: ", o, val );
-    fflush(stdout);
-
-    *(unsigned*)(tab+o) = v;
-
-    v2 = ((unsigned)tab[o+0] << 24) |
-         ((unsigned)tab[o+1] << 16) |
-         ((unsigned)tab[o+2] << 8 ) |
-         ((unsigned)tab[o+3]      );
-
-    if (v2 != val) {
-        printf( "FAIL (%08x)\n", v2 );
-    } else {
-        printf( "ok\n" );
-    }
-}
-
-static void
-read2( int  o, unsigned val )
-{
-    unsigned short v = htons(val);
-    unsigned short v2;
-
-    tab[o+0] = (char)(v >> 8);
-    tab[o+1] = (char)(v);
-
-    printf( "read2: offset=%d value=%08x: ", o, val );
-    fflush(stdout);
-
-    v2 = *(unsigned short*)(tab+o);
-
-    if (v2 != val) {
-        printf( "FAIL (%04x)\n", v2 );
-    } else {
-        printf( "ok\n" );
-    }
-}
-
-static void
-writ2( int  o, unsigned val )
-{
-    unsigned short v = htons(val);
-    unsigned short v2;
-
-    printf( "writ2: offset=%d value=%08x: ", o, val );
-    fflush(stdout);
-
-    *(unsigned short*)(tab+o) = v;
-
-    v2 = ((unsigned)tab[o+0] << 8) |
-         ((unsigned)tab[o+1]       );
-
-    if (v2 != val) {
-        printf( "FAIL (%08x)\n", v2 );
-    } else {
-        printf( "ok\n" );
-    }
-}
-
-
-
-int  main(void)
-{
-    read4( 0, 0x12345678 );
-    writ4( 0, 0x12345678 );
-    read4( 1, 0x12345678 );
-    writ4( 1, 0x12345678 );
-    read4( 2, 0x12345678 );
-    writ4( 2, 0x12345678 );
-    read4( 3, 0x12345678 );
-    writ4( 3, 0x12345678 );
-
-    read2( 0, 0x1234 );
-    writ2( 0, 0x1234 );
-    read2( 1, 0x1234 );
-    writ2( 1, 0x1234 );
-    read2( 2, 0x1234 );
-    writ2( 2, 0x1234 );
-    read2( 3, 0x1234 );
-    writ2( 3, 0x1234 );
-
-    return 0;
-}