Merge from ClockworkMod recovery

Change-Id: Id5b312147173ced559a62d97029acede6c2f8766
diff --git a/Android.mk b/Android.mk
index 1098121..b0fc158 100644
--- a/Android.mk
+++ b/Android.mk
@@ -8,33 +8,19 @@
 # LOCAL_CPP_EXTENSION := .c
 
 LOCAL_SRC_FILES := \
+    recovery.c \
+    bootloader.c \
+    install.c \
+    roots.c \
+    ui.c \
+    verifier.c \
+    encryptedfs_provisioning.c \
     mounts.c \
 	extendedcommands.c \
 	nandroid.c \
-	legacy.c \
-	commands.c \
-	recovery.c \
-	install.c \
-	roots.c \
-	ui.c \
-	verifier.c
-
-LOCAL_SRC_FILES += \
     reboot.c \
     setprop.c
 
-ifndef BOARD_HAS_NO_MISC_PARTITION
-    LOCAL_SRC_FILES += \
-        firmware.c \
-        bootloader.c
-endif
-
-ifdef BOARD_HIJACK_RECOVERY_PATH
-    LOCAL_CFLAGS += -DBOARD_HIJACK_RECOVERY_PATH=\"$(BOARD_HIJACK_RECOVERY_PATH)\"
-endif
-
-LOCAL_SRC_FILES += test_roots.c
-
 LOCAL_MODULE := recovery
 
 LOCAL_FORCE_STATIC_EXECUTABLE := true
@@ -44,7 +30,7 @@
 RECOVERY_API_VERSION := 2
 LOCAL_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION)
 
-BOARD_RECOVERY_DEFINES := BOARD_HAS_NO_SELECT_BUTTON BOARD_SDCARD_DEVICE_PRIMARY BOARD_SDCARD_DEVICE_SECONDARY BOARD_SDEXT_DEVICE BOARD_SDEXT_FILESYSTEM BOARD_DATA_DEVICE BOARD_DATA_FILESYSTEM BOARD_DATADATA_DEVICE BOARD_DATADATA_FILESYSTEM BOARD_CACHE_DEVICE BOARD_CACHE_FILESYSTEM BOARD_SYSTEM_DEVICE BOARD_SYSTEM_FILESYSTEM BOARD_HAS_DATADATA BOARD_DATA_FILESYSTEM_OPTIONS BOARD_DATADATA_FILESYSTEM_OPTIONS BOARD_CACHE_FILESYSTEM_OPTIONS BOARD_SYSTEM_FILESYSTEM_OPTIONS BOARD_HAS_MTD_CACHE BOARD_USES_BMLUTILS BOARD_USES_MMCUTILS BOARD_HAS_SMALL_RECOVERY BOARD_LDPI_RECOVERY BOARD_RECOVERY_IGNORE_BOOTABLES BOARD_HAS_NO_MISC_PARTITION BOARD_HAS_SDCARD_INTERNAL BOARD_SDCARD_DEVICE_INTERNAL
+BOARD_RECOVERY_DEFINES := BOARD_HAS_NO_SELECT_BUTTON BOARD_SDCARD_DEVICE_PRIMARY BOARD_SDCARD_DEVICE_SECONDARY BOARD_SDEXT_DEVICE BOARD_SDEXT_FILESYSTEM BOARD_DATA_DEVICE BOARD_DATA_FILESYSTEM BOARD_DATADATA_DEVICE BOARD_DATADATA_FILESYSTEM BOARD_CACHE_DEVICE BOARD_CACHE_FILESYSTEM BOARD_SYSTEM_DEVICE BOARD_SYSTEM_FILESYSTEM BOARD_HAS_DATADATA BOARD_DATA_FILESYSTEM_OPTIONS BOARD_DATADATA_FILESYSTEM_OPTIONS BOARD_CACHE_FILESYSTEM_OPTIONS BOARD_SYSTEM_FILESYSTEM_OPTIONS BOARD_HAS_MTD_CACHE BOARD_USES_BMLUTILS BOARD_USES_MMCUTILS BOARD_HAS_SMALL_RECOVERY BOARD_LDPI_RECOVERY BOARD_RECOVERY_IGNORE_BOOTABLES BOARD_HAS_NO_MISC_PARTITION BOARD_HAS_SDCARD_INTERNAL BOARD_SDCARD_DEVICE_INTERNAL BOARD_HIJACK_RECOVERY_PATH
 
 $(foreach board_define,$(BOARD_RECOVERY_DEFINES), \
   $(if $($(board_define)), \
@@ -65,41 +51,44 @@
 else
   LOCAL_SRC_FILES += $(BOARD_CUSTOM_RECOVERY_KEYMAPPING)
 endif
-LOCAL_STATIC_LIBRARIES += libbusybox libclearsilverregex libmkyaffs2image libunyaffs liberase_image libdump_image libflash_image
 
+LOCAL_STATIC_LIBRARIES += libext4_utils libz
+LOCAL_STATIC_LIBRARIES += libminzip libunz libmincrypt
+
+LOCAL_STATIC_LIBRARIES += libbusybox libclearsilverregex libmkyaffs2image libunyaffs liberase_image libdump_image libflash_image
 LOCAL_STATIC_LIBRARIES += libflashutils libmtdutils libmmcutils libbmlutils
 
-LOCAL_STATIC_LIBRARIES += libamend
-LOCAL_STATIC_LIBRARIES += libminzip libunz libmincrypt
 LOCAL_STATIC_LIBRARIES += libminui libpixelflinger_static libpng libcutils
 LOCAL_STATIC_LIBRARIES += libstdc++ libc
 
+LOCAL_C_INCLUDES += system/extras/ext4_utils
+
 include $(BUILD_EXECUTABLE)
 
-RECOVERY_LINKS := amend busybox flash_image dump_image mkyaffs2image unyaffs erase_image nandroid reboot
+RECOVERY_LINKS := edify busybox flash_image dump_image mkyaffs2image unyaffs erase_image nandroid reboot
 
 # nc is provided by external/netcat
-SYMLINKS := $(addprefix $(TARGET_RECOVERY_ROOT_OUT)/sbin/,$(RECOVERY_LINKS))
-$(SYMLINKS): RECOVERY_BINARY := $(LOCAL_MODULE)
-$(SYMLINKS): $(LOCAL_INSTALLED_MODULE)
+RECOVERY_SYMLINKS := $(addprefix $(TARGET_RECOVERY_ROOT_OUT)/sbin/,$(RECOVERY_LINKS))
+$(RECOVERY_SYMLINKS): RECOVERY_BINARY := $(LOCAL_MODULE)
+$(RECOVERY_SYMLINKS): $(LOCAL_INSTALLED_MODULE)
 	@echo "Symlink: $@ -> $(RECOVERY_BINARY)"
 	@mkdir -p $(dir $@)
 	@rm -rf $@
 	$(hide) ln -sf $(RECOVERY_BINARY) $@
 
-ALL_DEFAULT_INSTALLED_MODULES += $(SYMLINKS)
+ALL_DEFAULT_INSTALLED_MODULES += $(RECOVERY_SYMLINKS)
 
 # Now let's do recovery symlinks
 BUSYBOX_LINKS := $(shell cat external/busybox/busybox-minimal.links)
-SYMLINKS := $(addprefix $(TARGET_RECOVERY_ROOT_OUT)/sbin/,$(filter-out $(exclude),$(notdir $(BUSYBOX_LINKS))))
-$(SYMLINKS): BUSYBOX_BINARY := busybox
-$(SYMLINKS): $(LOCAL_INSTALLED_MODULE)
+RECOVERY_BUSYBOX_SYMLINKS := $(addprefix $(TARGET_RECOVERY_ROOT_OUT)/sbin/,$(filter-out $(exclude),$(notdir $(BUSYBOX_LINKS))))
+$(RECOVERY_BUSYBOX_SYMLINKS): BUSYBOX_BINARY := busybox
+$(RECOVERY_BUSYBOX_SYMLINKS): $(LOCAL_INSTALLED_MODULE)
 	@echo "Symlink: $@ -> $(BUSYBOX_BINARY)"
 	@mkdir -p $(dir $@)
 	@rm -rf $@
 	$(hide) ln -sf $(BUSYBOX_BINARY) $@
 
-ALL_DEFAULT_INSTALLED_MODULES += $(SYMLINKS)
+ALL_DEFAULT_INSTALLED_MODULES += $(RECOVERY_BUSYBOX_SYMLINKS)
 
 include $(CLEAR_VARS)
 LOCAL_MODULE := nandroid-md5.sh
@@ -132,7 +121,6 @@
 include $(BUILD_EXECUTABLE)
 
 
-include $(commands_recovery_local_path)/amend/Android.mk
 include $(commands_recovery_local_path)/bmlutils/Android.mk
 include $(commands_recovery_local_path)/flashutils/Android.mk
 include $(commands_recovery_local_path)/minui/Android.mk
diff --git a/amend/Android.mk b/amend/Android.mk
deleted file mode 100644
index ae2d44a..0000000
--- a/amend/Android.mk
+++ /dev/null
@@ -1,53 +0,0 @@
-# Copyright 2007 The Android Open Source Project
-#
-
-LOCAL_PATH := $(call my-dir)
-
-amend_src_files := \
-	amend.c \
-	lexer.l \
-	parser_y.y \
-	ast.c \
-	symtab.c \
-	commands.c \
-	permissions.c \
-	execute.c
-
-amend_test_files := \
-	test_symtab.c \
-	test_commands.c \
-	test_permissions.c
-
-# "-x c" forces the lex/yacc files to be compiled as c;
-# the build system otherwise forces them to be c++.
-amend_cflags := -Wall -x c
-
-#
-# Build the host-side command line tool
-#
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
-		$(amend_src_files) \
-		$(amend_test_files) \
-		register.c \
-		main.c
-
-LOCAL_CFLAGS := $(amend_cflags) -g -O0
-LOCAL_MODULE := amend
-LOCAL_YACCFLAGS := -v
-
-include $(BUILD_HOST_EXECUTABLE)
-
-#
-# Build the device-side library
-#
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(amend_src_files)
-LOCAL_SRC_FILES += $(amend_test_files)
-
-LOCAL_CFLAGS := $(amend_cflags)
-LOCAL_MODULE := libamend
-
-include $(BUILD_STATIC_LIBRARY)
diff --git a/amend/amend.c b/amend/amend.c
deleted file mode 100644
index 49cd64e..0000000
--- a/amend/amend.c
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2007 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 <stdlib.h>
-#include "amend.h"
-#include "lexer.h"
-
-extern const AmCommandList *gCommands;
-
-const AmCommandList *
-parseAmendScript(const char *buf, size_t bufLen)
-{
-    setLexerInputBuffer(buf, bufLen);
-    int ret = yyparse();
-    if (ret != 0) {
-        return NULL;
-    }
-    return gCommands;
-}
diff --git a/amend/amend.h b/amend/amend.h
deleted file mode 100644
index 416f974..0000000
--- a/amend/amend.h
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (C) 2007 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 AMEND_H_
-#define AMEND_H_
-
-#include "ast.h"
-#include "execute.h"
-
-const AmCommandList *parseAmendScript(const char *buf, size_t bufLen);
-
-#endif  // AMEND_H_
diff --git a/amend/ast.c b/amend/ast.c
deleted file mode 100644
index f53efdc..0000000
--- a/amend/ast.c
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * Copyright (C) 2007 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 "ast.h"
-
-static const char gSpaces[] =
-    "                                                                "
-    "                                                                "
-    "                                                                "
-    "                                                                "
-    "                                                                "
-    "                                                                "
-    "                                                                ";
-const int gSpacesMax = sizeof(gSpaces) - 1;
-
-static const char *
-pad(int level)
-{
-    level *= 4;
-    if (level > gSpacesMax) {
-        level = gSpacesMax;
-    }
-    return gSpaces + gSpacesMax - level;
-}
-
-void dumpBooleanValue(int level, const AmBooleanValue *booleanValue);
-void dumpStringValue(int level, const AmStringValue *stringValue);
-
-void
-dumpBooleanExpression(int level, const AmBooleanExpression *booleanExpression)
-{
-    const char *op;
-    bool unary = false;
-
-    switch (booleanExpression->op) {
-    case AM_BOP_NOT:
-        op = "NOT";
-        unary = true;
-        break;
-    case AM_BOP_EQ:
-        op = "EQ";
-        break;
-    case AM_BOP_NE:
-        op = "NE";
-        break;
-    case AM_BOP_AND:
-        op = "AND";
-        break;
-    case AM_BOP_OR:
-        op = "OR";
-        break;
-    default:
-        op = "??";
-        break;
-    }
-
-    printf("%sBOOLEAN %s {\n", pad(level), op);
-    dumpBooleanValue(level + 1, booleanExpression->arg1);
-    if (!unary) {
-        dumpBooleanValue(level + 1, booleanExpression->arg2);
-    }
-    printf("%s}\n", pad(level));
-}
-
-void
-dumpFunctionArguments(int level, const AmFunctionArguments *functionArguments)
-{
-    int i;
-    for (i = 0; i < functionArguments->argc; i++) {
-        dumpStringValue(level, &functionArguments->argv[i]);
-    }
-}
-
-void
-dumpFunctionCall(int level, const AmFunctionCall *functionCall)
-{
-    printf("%sFUNCTION %s (\n", pad(level), functionCall->name);
-    dumpFunctionArguments(level + 1, functionCall->args);
-    printf("%s)\n", pad(level));
-}
-
-void
-dumpStringValue(int level, const AmStringValue *stringValue)
-{
-    switch (stringValue->type) {
-    case AM_SVAL_LITERAL:
-        printf("%s\"%s\"\n", pad(level), stringValue->u.literal);
-        break;
-    case AM_SVAL_FUNCTION:
-        dumpFunctionCall(level, stringValue->u.function);
-        break;
-    default:
-        printf("%s<UNKNOWN SVAL TYPE %d>\n", pad(level), stringValue->type);
-        break;
-    }
-}
-
-void
-dumpStringComparisonExpression(int level,
-        const AmStringComparisonExpression *stringComparisonExpression)
-{
-    const char *op;
-
-    switch (stringComparisonExpression->op) {
-    case AM_SOP_LT:
-        op = "LT";
-        break;
-    case AM_SOP_LE:
-        op = "LE";
-        break;
-    case AM_SOP_GT:
-        op = "GT";
-        break;
-    case AM_SOP_GE:
-        op = "GE";
-        break;
-    case AM_SOP_EQ:
-        op = "EQ";
-        break;
-    case AM_SOP_NE:
-        op = "NE";
-        break;
-    default:
-        op = "??";
-        break;
-    }
-    printf("%sSTRING %s {\n", pad(level), op);
-    dumpStringValue(level + 1, stringComparisonExpression->arg1);
-    dumpStringValue(level + 1, stringComparisonExpression->arg2);
-    printf("%s}\n", pad(level));
-}
-
-void
-dumpBooleanValue(int level, const AmBooleanValue *booleanValue)
-{
-    switch (booleanValue->type) {
-    case AM_BVAL_EXPRESSION:
-        dumpBooleanExpression(level, &booleanValue->u.expression);
-        break;
-    case AM_BVAL_STRING_COMPARISON:
-        dumpStringComparisonExpression(level,
-                &booleanValue->u.stringComparison);
-        break;
-    default:
-        printf("%s<UNKNOWN BVAL TYPE %d>\n", pad(1), booleanValue->type);
-        break;
-    }
-}
-
-void
-dumpWordList(const AmWordList *wordList)
-{
-    int i;
-    for (i = 0; i < wordList->argc; i++) {
-        printf("%s\"%s\"\n", pad(1), wordList->argv[i]);
-    }
-}
-
-void
-dumpCommandArguments(const AmCommandArguments *commandArguments)
-{
-    if (commandArguments->booleanArgs) {
-        dumpBooleanValue(1, commandArguments->u.b);
-    } else {
-        dumpWordList(commandArguments->u.w);
-    }
-}
-
-void
-dumpCommand(const AmCommand *command)
-{
-    printf("command \"%s\" {\n", command->name);
-    dumpCommandArguments(command->args);
-    printf("}\n");
-}
-
-void
-dumpCommandList(const AmCommandList *commandList)
-{
-    int i;
-    for (i = 0; i < commandList->commandCount; i++) {
-        dumpCommand(commandList->commands[i]);
-    }
-}
diff --git a/amend/ast.h b/amend/ast.h
deleted file mode 100644
index 7834a2b..0000000
--- a/amend/ast.h
+++ /dev/null
@@ -1,165 +0,0 @@
-/*
- * Copyright (C) 2007 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 AMEND_AST_H_
-#define AMEND_AST_H_
-
-#include "commands.h"
-
-typedef struct AmStringValue AmStringValue;
-
-typedef struct {
-    int argc;
-    AmStringValue *argv;
-} AmFunctionArguments;
-
-/* An internal structure used only by the parser;
- * will not appear in the output AST.
-xxx try to move this into parser.h
- */
-typedef struct AmFunctionArgumentBuilder AmFunctionArgumentBuilder;
-struct AmFunctionArgumentBuilder {
-    AmFunctionArgumentBuilder *next;
-    AmStringValue *arg;
-    int argCount;
-};
-
-typedef struct AmWordListBuilder AmWordListBuilder;
-struct AmWordListBuilder {
-    AmWordListBuilder *next;
-    const char *word;
-    int wordCount;
-};
-
-typedef struct {
-    const char *name;
-    Function *fn;
-    AmFunctionArguments *args;
-} AmFunctionCall;
-
-
-/* <string-value> ::=
- *      <literal-string> |
- *      <function-call>
- */
-struct AmStringValue {
-    unsigned int line;
-
-    enum {
-        AM_SVAL_LITERAL,
-        AM_SVAL_FUNCTION,
-    } type;
-    union {
-        const char *literal;
-//xxx inline instead of using pointers
-        AmFunctionCall *function;
-    } u;
-};
-
-
-/* <string-comparison-expression> ::=
- *      <string-value> <string-comparison-operator> <string-value>
- */
-typedef struct {
-    unsigned int line;
-
-    enum {
-        AM_SOP_LT,
-        AM_SOP_LE,
-        AM_SOP_GT,
-        AM_SOP_GE,
-        AM_SOP_EQ,
-        AM_SOP_NE,
-    } op;
-    AmStringValue *arg1;
-    AmStringValue *arg2;
-} AmStringComparisonExpression;
-
-
-/* <boolean-expression> ::=
- *      ! <boolean-value> |
- *      <boolean-value> <binary-boolean-operator> <boolean-value>
- */
-typedef struct AmBooleanValue AmBooleanValue;
-typedef struct {
-    unsigned int line;
-
-    enum {
-        AM_BOP_NOT,
-
-        AM_BOP_EQ,
-        AM_BOP_NE,
-
-        AM_BOP_AND,
-
-        AM_BOP_OR,
-    } op;
-    AmBooleanValue *arg1;
-    AmBooleanValue *arg2;
-} AmBooleanExpression;
-
-
-/* <boolean-value> ::=
- *      <boolean-expression> |
- *      <string-comparison-expression>
- */
-struct AmBooleanValue {
-    unsigned int line;
-
-    enum {
-        AM_BVAL_EXPRESSION,
-        AM_BVAL_STRING_COMPARISON,
-    } type;
-    union {
-        AmBooleanExpression expression;
-        AmStringComparisonExpression stringComparison;
-    } u;
-};
-
-
-typedef struct {
-    unsigned int line;
-
-    int argc;
-    const char **argv;
-} AmWordList;
-
-
-typedef struct {
-    bool booleanArgs;
-    union {
-        AmWordList *w;
-        AmBooleanValue *b;
-    } u;
-} AmCommandArguments;
-
-typedef struct {
-    unsigned int line;
-
-    const char *name;
-    Command *cmd;
-    AmCommandArguments *args;
-} AmCommand;
-
-typedef struct {
-    AmCommand **commands;
-    int commandCount;
-    int arraySize;
-} AmCommandList;
-
-void dumpCommandList(const AmCommandList *commandList);
-
-#endif  // AMEND_AST_H_
diff --git a/amend/commands.c b/amend/commands.c
deleted file mode 100644
index 75ff828..0000000
--- a/amend/commands.c
+++ /dev/null
@@ -1,273 +0,0 @@
-/*
- * Copyright (C) 2007 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 <stdlib.h>
-#include <string.h>
-#include <stdio.h>
-#include "symtab.h"
-#include "commands.h"
-
-#if 1
-#define TRACE(...)  printf(__VA_ARGS__)
-#else
-#define TRACE(...)  /**/
-#endif
-
-typedef enum {
-    CMD_TYPE_UNKNOWN = -1,
-    CMD_TYPE_COMMAND = 0,
-    CMD_TYPE_FUNCTION
-} CommandType;
-
-typedef struct {
-    const char *name;
-    void *cookie;
-    CommandType type;
-    CommandArgumentType argType;
-    CommandHook hook;
-} CommandEntry;
-
-static struct {
-    SymbolTable *symbolTable;
-    bool commandStateInitialized;
-} gCommandState;
-
-int
-commandInit()
-{
-    if (gCommandState.commandStateInitialized) {
-        return -1;
-    }
-    gCommandState.symbolTable = createSymbolTable();
-    if (gCommandState.symbolTable == NULL) {
-        return -1;
-    }
-    gCommandState.commandStateInitialized = true;
-    return 0;
-}
-
-void
-commandCleanup()
-{
-    if (gCommandState.commandStateInitialized) {
-        gCommandState.commandStateInitialized = false;
-        deleteSymbolTable(gCommandState.symbolTable);
-        gCommandState.symbolTable = NULL;
-//xxx need to free the entries and names in the symbol table
-    }
-}
-
-static int
-registerCommandInternal(const char *name, CommandType type,
-        CommandArgumentType argType, CommandHook hook, void *cookie)
-{
-    CommandEntry *entry;
-
-    if (!gCommandState.commandStateInitialized) {
-        return -1;
-    }
-    if (name == NULL || hook == NULL) {
-        return -1;
-    }
-    if (type != CMD_TYPE_COMMAND && type != CMD_TYPE_FUNCTION) {
-        return -1;
-    }
-    if (argType != CMD_ARGS_BOOLEAN && argType != CMD_ARGS_WORDS) {
-        return -1;
-    }
-
-    entry = (CommandEntry *)malloc(sizeof(CommandEntry));
-    if (entry != NULL) {
-        entry->name = strdup(name);
-        if (entry->name != NULL) {
-            int ret;
-
-            entry->cookie = cookie;
-            entry->type = type;
-            entry->argType = argType;
-            entry->hook = hook;
-            ret = addToSymbolTable(gCommandState.symbolTable,
-                        entry->name, entry->type, entry);
-            if (ret == 0) {
-                return 0;
-            }
-        }
-        free(entry);
-    }
-
-    return -1;
-}
-
-int
-registerCommand(const char *name,
-        CommandArgumentType argType, CommandHook hook, void *cookie)
-{
-    return registerCommandInternal(name,
-            CMD_TYPE_COMMAND, argType, hook, cookie);
-}
-
-int
-registerFunction(const char *name, FunctionHook hook, void *cookie)
-{
-    return registerCommandInternal(name,
-            CMD_TYPE_FUNCTION, CMD_ARGS_WORDS, (CommandHook)hook, cookie);
-}
-
-Command *
-findCommand(const char *name)
-{
-    return (Command *)findInSymbolTable(gCommandState.symbolTable,
-            name, CMD_TYPE_COMMAND);
-}
-
-Function *
-findFunction(const char *name)
-{
-    return (Function *)findInSymbolTable(gCommandState.symbolTable,
-            name, CMD_TYPE_FUNCTION);
-}
-
-CommandArgumentType
-getCommandArgumentType(Command *cmd)
-{
-    CommandEntry *entry = (CommandEntry *)cmd;
-
-    if (entry != NULL) {
-        return entry->argType;
-    }
-    return CMD_ARGS_UNKNOWN;
-}
-
-static int
-callCommandInternal(CommandEntry *entry, int argc, const char *argv[],
-        PermissionRequestList *permissions)
-{
-    if (entry != NULL && entry->argType == CMD_ARGS_WORDS &&
-            (argc == 0 || (argc > 0 && argv != NULL)))
-    {
-        if (permissions == NULL) {
-            int i;
-            for (i = 0; i < argc; i++) {
-                if (argv[i] == NULL) {
-                    goto bail;
-                }
-            }
-        }
-        TRACE("calling command %s\n", entry->name);
-        return entry->hook(entry->name, entry->cookie, argc, argv, permissions);
-//xxx if permissions, make sure the entry has added at least one element.
-    }
-bail:
-    return -1;
-}
-
-static int
-callBooleanCommandInternal(CommandEntry *entry, bool arg,
-        PermissionRequestList *permissions)
-{
-    if (entry != NULL && entry->argType == CMD_ARGS_BOOLEAN) {
-        TRACE("calling boolean command %s\n", entry->name);
-        return entry->hook(entry->name, entry->cookie, arg ? 1 : 0, NULL,
-                permissions);
-//xxx if permissions, make sure the entry has added at least one element.
-    }
-    return -1;
-}
-
-int
-callCommand(Command *cmd, int argc, const char *argv[])
-{
-    return callCommandInternal((CommandEntry *)cmd, argc, argv, NULL);
-}
-
-int
-callBooleanCommand(Command *cmd, bool arg)
-{
-    return callBooleanCommandInternal((CommandEntry *)cmd, arg, NULL);
-}
-
-int
-getCommandPermissions(Command *cmd, int argc, const char *argv[],
-        PermissionRequestList *permissions)
-{
-    if (permissions != NULL) {
-        return callCommandInternal((CommandEntry *)cmd, argc, argv,
-                permissions);
-    }
-    return -1;
-}
-
-int
-getBooleanCommandPermissions(Command *cmd, bool arg,
-        PermissionRequestList *permissions)
-{
-    if (permissions != NULL) {
-        return callBooleanCommandInternal((CommandEntry *)cmd, arg,
-                permissions);
-    }
-    return -1;
-}
-
-int
-callFunctionInternal(CommandEntry *entry, int argc, const char *argv[],
-        char **result, size_t *resultLen, PermissionRequestList *permissions)
-{
-    if (entry != NULL && entry->argType == CMD_ARGS_WORDS &&
-            (argc == 0 || (argc > 0 && argv != NULL)))
-    {
-        if ((permissions == NULL && result != NULL) ||
-                (permissions != NULL && result == NULL))
-        {
-            if (permissions == NULL) {
-                /* This is the actual invocation of the function,
-                 * which means that none of the arguments are allowed
-                 * to be NULL.
-                 */
-                int i;
-                for (i = 0; i < argc; i++) {
-                    if (argv[i] == NULL) {
-                        goto bail;
-                    }
-                }
-            }
-            TRACE("calling function %s\n", entry->name);
-            return ((FunctionHook)entry->hook)(entry->name, entry->cookie,
-                    argc, argv, result, resultLen, permissions);
-//xxx if permissions, make sure the entry has added at least one element.
-        }
-    }
-bail:
-    return -1;
-}
-
-int
-callFunction(Function *fn, int argc, const char *argv[],
-        char **result, size_t *resultLen)
-{
-    return callFunctionInternal((CommandEntry *)fn, argc, argv,
-            result, resultLen, NULL);
-}
-
-int
-getFunctionPermissions(Function *fn, int argc, const char *argv[],
-        PermissionRequestList *permissions)
-{
-    if (permissions != NULL) {
-        return callFunctionInternal((CommandEntry *)fn, argc, argv,
-                NULL, NULL, permissions);
-    }
-    return -1;
-}
diff --git a/amend/commands.h b/amend/commands.h
deleted file mode 100644
index 38931c0..0000000
--- a/amend/commands.h
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2007 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 AMEND_COMMANDS_H_
-#define AMEND_COMMANDS_H_
-
-#include "permissions.h"
-
-/* Invoke or dry-run a command.  If "permissions" is non-NULL,
- * the hook should fill it out with the list of files and operations that
- * it would need to complete its operation.  If "permissions" is NULL,
- * the hook should do the actual work specified by its arguments.
- *
- * When a command is called with non-NULL "permissions", some arguments
- * may be NULL.  A NULL argument indicates that the argument is actually
- * the output of another function, so is not known at permissions time.
- * The permissions of leaf-node functions (those that have only literal
- * strings as arguments) will get appended to the permissions of the
- * functions that call them.  However, to be completely safe, functions
- * that receive a NULL argument should request the broadest-possible
- * permissions for the range of the input argument.
- *
- * When a boolean command is called, "argc" is the boolean value and
- * "argv" is NULL.
- */
-typedef int (*CommandHook)(const char *name, void *cookie,
-                                int argc, const char *argv[],
-                                PermissionRequestList *permissions);
-
-int commandInit(void);
-void commandCleanup(void);
-
-/*
- * Command management
- */
-
-struct Command;
-typedef struct Command Command;
-
-typedef enum {
-    CMD_ARGS_UNKNOWN = -1,
-    CMD_ARGS_BOOLEAN = 0,
-    CMD_ARGS_WORDS
-} CommandArgumentType;
-
-int registerCommand(const char *name,
-        CommandArgumentType argType, CommandHook hook, void *cookie);
-
-Command *findCommand(const char *name);
-
-CommandArgumentType getCommandArgumentType(Command *cmd);
-
-int callCommand(Command *cmd, int argc, const char *argv[]);
-int callBooleanCommand(Command *cmd, bool arg);
-
-int getCommandPermissions(Command *cmd, int argc, const char *argv[],
-        PermissionRequestList *permissions);
-int getBooleanCommandPermissions(Command *cmd, bool arg,
-        PermissionRequestList *permissions);
-
-/*
- * Function management
- */
-
-typedef int (*FunctionHook)(const char *name, void *cookie,
-                                int argc, const char *argv[],
-                                char **result, size_t *resultLen,
-                                PermissionRequestList *permissions);
-
-struct Function;
-typedef struct Function Function;
-
-int registerFunction(const char *name, FunctionHook hook, void *cookie);
-
-Function *findFunction(const char *name);
-
-int callFunction(Function *fn, int argc, const char *argv[],
-        char **result, size_t *resultLen);
-
-int getFunctionPermissions(Function *fn, int argc, const char *argv[],
-        PermissionRequestList *permissions);
-
-#endif  // AMEND_COMMANDS_H_
diff --git a/amend/execute.c b/amend/execute.c
deleted file mode 100644
index 9162ad6..0000000
--- a/amend/execute.c
+++ /dev/null
@@ -1,315 +0,0 @@
-/*
- * Copyright (C) 2007 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 <stdlib.h>
-#include <string.h>
-#include <stdio.h>
-#undef NDEBUG
-#include <assert.h>
-#include "ast.h"
-#include "execute.h"
-
-typedef struct {
-    int c;
-    const char **v;
-} StringList;
-
-static int execBooleanValue(ExecContext *ctx,
-        const AmBooleanValue *booleanValue, bool *result);
-static int execStringValue(ExecContext *ctx, const AmStringValue *stringValue,
-        const char **result);
-
-static int
-execBooleanExpression(ExecContext *ctx,
-        const AmBooleanExpression *booleanExpression, bool *result)
-{
-    int ret;
-    bool arg1, arg2;
-    bool unary;
-
-    assert(ctx != NULL);
-    assert(booleanExpression != NULL);
-    assert(result != NULL);
-    if (ctx == NULL || booleanExpression == NULL || result == NULL) {
-        return -__LINE__;
-    }
-
-    if (booleanExpression->op == AM_BOP_NOT) {
-        unary = true;
-    } else {
-        unary = false;
-    }
-
-    ret = execBooleanValue(ctx, booleanExpression->arg1, &arg1);
-    if (ret != 0) return ret;
-
-    if (!unary) {
-        ret = execBooleanValue(ctx, booleanExpression->arg2, &arg2);
-        if (ret != 0) return ret;
-    } else {
-        arg2 = false;
-    }
-
-    switch (booleanExpression->op) {
-    case AM_BOP_NOT:
-        *result = !arg1;
-        break;
-    case AM_BOP_EQ:
-        *result = (arg1 == arg2);
-        break;
-    case AM_BOP_NE:
-        *result = (arg1 != arg2);
-        break;
-    case AM_BOP_AND:
-        *result = (arg1 && arg2);
-        break;
-    case AM_BOP_OR:
-        *result = (arg1 || arg2);
-        break;
-    default:
-        return -__LINE__;
-    }
-
-    return 0;
-}
-
-static int
-execFunctionArguments(ExecContext *ctx,
-        const AmFunctionArguments *functionArguments, StringList *result)
-{
-    int ret;
-
-    assert(ctx != NULL);
-    assert(functionArguments != NULL);
-    assert(result != NULL);
-    if (ctx == NULL || functionArguments == NULL || result == NULL) {
-        return -__LINE__;
-    }
-
-    result->c = functionArguments->argc;
-    result->v = (const char **)malloc(result->c * sizeof(const char *));
-    if (result->v == NULL) {
-        result->c = 0;
-        return -__LINE__;
-    }
-
-    int i;
-    for (i = 0; i < functionArguments->argc; i++) {
-        ret = execStringValue(ctx, &functionArguments->argv[i], &result->v[i]);
-        if (ret != 0) {
-            result->c = 0;
-            free(result->v);
-            //TODO: free the individual args, if we're responsible for them.
-            result->v = NULL;
-            return ret;
-        }
-    }
-
-    return 0;
-}
-
-static int
-execFunctionCall(ExecContext *ctx, const AmFunctionCall *functionCall,
-        const char **result)
-{
-    int ret;
-
-    assert(ctx != NULL);
-    assert(functionCall != NULL);
-    assert(result != NULL);
-    if (ctx == NULL || functionCall == NULL || result == NULL) {
-        return -__LINE__;
-    }
-
-    StringList args;
-    ret = execFunctionArguments(ctx, functionCall->args, &args);
-    if (ret != 0) {
-        return ret;
-    }
-
-    ret = callFunction(functionCall->fn, args.c, args.v, (char **)result, NULL);
-    if (ret != 0) {
-        return ret;
-    }
-
-    //TODO: clean up args
-
-    return 0;
-}
-
-static int
-execStringValue(ExecContext *ctx, const AmStringValue *stringValue,
-        const char **result)
-{
-    int ret;
-
-    assert(ctx != NULL);
-    assert(stringValue != NULL);
-    assert(result != NULL);
-    if (ctx == NULL || stringValue == NULL || result == NULL) {
-        return -__LINE__;
-    }
-
-    switch (stringValue->type) {
-    case AM_SVAL_LITERAL:
-        *result = strdup(stringValue->u.literal);
-        break;
-    case AM_SVAL_FUNCTION:
-        ret = execFunctionCall(ctx, stringValue->u.function, result);
-        if (ret != 0) {
-            return ret;
-        }
-        break;
-    default:
-        return -__LINE__;
-    }
-
-    return 0;
-}
-
-static int
-execStringComparisonExpression(ExecContext *ctx,
-        const AmStringComparisonExpression *stringComparisonExpression,
-        bool *result)
-{
-    int ret;
-
-    assert(ctx != NULL);
-    assert(stringComparisonExpression != NULL);
-    assert(result != NULL);
-    if (ctx == NULL || stringComparisonExpression == NULL || result == NULL) {
-        return -__LINE__;
-    }
-
-    const char *arg1, *arg2;
-    ret = execStringValue(ctx, stringComparisonExpression->arg1, &arg1);
-    if (ret != 0) {
-        return ret;
-    }
-    ret = execStringValue(ctx, stringComparisonExpression->arg2, &arg2);
-    if (ret != 0) {
-        return ret;
-    }
-
-    int cmp = strcmp(arg1, arg2);
-
-    switch (stringComparisonExpression->op) {
-    case AM_SOP_LT:
-        *result = (cmp < 0);
-        break;
-    case AM_SOP_LE:
-        *result = (cmp <= 0);
-        break;
-    case AM_SOP_GT:
-        *result = (cmp > 0);
-        break;
-    case AM_SOP_GE:
-        *result = (cmp >= 0);
-        break;
-    case AM_SOP_EQ:
-        *result = (cmp == 0);
-        break;
-    case AM_SOP_NE:
-        *result = (cmp != 0);
-        break;
-    default:
-        return -__LINE__;
-        break;
-    }
-
-    return 0;
-}
-
-static int
-execBooleanValue(ExecContext *ctx, const AmBooleanValue *booleanValue,
-        bool *result)
-{
-    int ret;
-
-    assert(ctx != NULL);
-    assert(booleanValue != NULL);
-    assert(result != NULL);
-    if (ctx == NULL || booleanValue == NULL || result == NULL) {
-        return -__LINE__;
-    }
-
-    switch (booleanValue->type) {
-    case AM_BVAL_EXPRESSION:
-        ret = execBooleanExpression(ctx, &booleanValue->u.expression, result);
-        break;
-    case AM_BVAL_STRING_COMPARISON:
-        ret = execStringComparisonExpression(ctx,
-                &booleanValue->u.stringComparison, result);
-        break;
-    default:
-        ret = -__LINE__;
-        break;
-    }
-
-    return ret;
-}
-
-static int
-execCommand(ExecContext *ctx, const AmCommand *command)
-{
-    int ret;
-
-    assert(ctx != NULL);
-    assert(command != NULL);
-    if (ctx == NULL || command == NULL) {
-        return -__LINE__;
-    }
-
-    CommandArgumentType argType;
-    argType = getCommandArgumentType(command->cmd);
-    switch (argType) {
-    case CMD_ARGS_BOOLEAN:
-        {
-            bool bVal;
-            ret = execBooleanValue(ctx, command->args->u.b, &bVal);
-            if (ret == 0) {
-                ret = callBooleanCommand(command->cmd, bVal);
-            }
-        }
-        break;
-    case CMD_ARGS_WORDS:
-        {
-            AmWordList *words = command->args->u.w;
-            ret = callCommand(command->cmd, words->argc, words->argv);
-        }
-        break;
-    default:
-        ret = -__LINE__;
-        break;
-    }
-
-    return ret;
-}
-
-int
-execCommandList(ExecContext *ctx, const AmCommandList *commandList)
-{
-    int i;
-    for (i = 0; i < commandList->commandCount; i++) {
-        int ret = execCommand(ctx, commandList->commands[i]);
-        if (ret != 0) {
-            int line = commandList->commands[i]->line;
-            return line > 0 ? line : ret;
-        }
-    }
-
-    return 0;
-}
diff --git a/amend/execute.h b/amend/execute.h
deleted file mode 100644
index 3becb48..0000000
--- a/amend/execute.h
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (C) 2007 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 AMEND_EXECUTE_H_
-#define AMEND_EXECUTE_H_
-
-typedef struct ExecContext ExecContext;
-
-/* Returns 0 on success, otherwise the line number that failed. */
-int execCommandList(ExecContext *ctx, const AmCommandList *commandList);
-
-#endif  // AMEND_EXECUTE_H_
diff --git a/amend/lexer.h b/amend/lexer.h
deleted file mode 100644
index fc716fd..0000000
--- a/amend/lexer.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2007 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 AMEND_LEXER_H_
-#define AMEND_LEXER_H_
-
-#define AMEND_LEXER_BUFFER_INPUT 1
-
-void yyerror(const char *msg);
-int yylex(void);
-
-#if AMEND_LEXER_BUFFER_INPUT
-void setLexerInputBuffer(const char *buf, size_t buflen);
-#else
-#include <stdio.h>
-void yyset_in(FILE *in_str);
-#endif
-
-const char *tokenToString(int token);
-
-typedef enum {
-    AM_UNKNOWN_ARGS,
-    AM_WORD_ARGS,
-    AM_BOOLEAN_ARGS,
-} AmArgumentType;
-
-void setLexerArgumentType(AmArgumentType type);
-int getLexerLineNumber(void);
-
-#endif  // AMEND_LEXER_H_
diff --git a/amend/lexer.l b/amend/lexer.l
deleted file mode 100644
index 80896d1..0000000
--- a/amend/lexer.l
+++ /dev/null
@@ -1,299 +0,0 @@
-/*
- * Copyright (C) 2007 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 "ast.h"
-    #include "lexer.h"
-    #include "parser.h"
-
-    const char *tokenToString(int token)
-    {
-        static char scratch[128];
-
-        switch (token) {
-        case TOK_AND:
-            return "&&";
-        case TOK_OR:
-            return "||";
-        case TOK_EQ:
-            return "==";
-        case TOK_NE:
-            return "!=";
-        case TOK_GE:
-            return ">=";
-        case TOK_LE:
-            return "<=";
-        case TOK_EOF:
-            return "EOF";
-        case TOK_EOL:
-            return "EOL\n";
-        case TOK_STRING:
-            snprintf(scratch, sizeof(scratch),
-                    "STRING<%s>", yylval.literalString);
-            return scratch;
-        case TOK_IDENTIFIER:
-            snprintf(scratch, sizeof(scratch), "IDENTIFIER<%s>",
-                    yylval.literalString);
-            return scratch;
-        case TOK_WORD:
-            snprintf(scratch, sizeof(scratch), "WORD<%s>",
-                    yylval.literalString);
-            return scratch;
-        default:
-            if (token > ' ' && token <= '~') {
-                scratch[0] = (char)token;
-                scratch[1] = '\0';
-            } else {
-                snprintf(scratch, sizeof(scratch), "??? <%d>", token);
-            }
-            return scratch;
-        }
-    }
-
-    typedef struct {
-        char *value;
-        char *nextc;
-        unsigned int alloc_size;
-    } AmString;
-
-    static int addCharToString(AmString *str, char c)
-    {
-        if ((unsigned int)(str->nextc - str->value) >= str->alloc_size) {
-            char *new_value;
-            unsigned int new_size;
-
-            new_size = (str->alloc_size + 1) * 2;
-            if (new_size < 64) {
-                new_size = 64;
-            }
-
-            new_value = (char *)realloc(str->value, new_size);
-            if (new_value == NULL) {
-                yyerror("out of memory");
-                return -1;
-            }
-            str->nextc = str->nextc - str->value + new_value;
-            str->value = new_value;
-            str->alloc_size = new_size;
-        }
-        *str->nextc++ = c;
-        return 0;
-    }
-
-    static int setString(AmString *str, const char *p)
-    {
-        str->nextc = str->value;
-        while (*p != '\0') {
-//TODO: add the whole string at once
-            addCharToString(str, *p++);
-        }
-        return addCharToString(str, '\0');
-    }
-
-    static AmString gStr = { NULL, NULL, 0 };
-    static int gLineNumber = 1;
-    static AmArgumentType gArgumentType = AM_UNKNOWN_ARGS;
-    static const char *gErrorMessage = NULL;
-
-#if AMEND_LEXER_BUFFER_INPUT
-    static const char *gInputBuffer;
-    static const char *gInputBufferNext;
-    static const char *gInputBufferEnd;
-
-# define YY_INPUT(buf, result, max_size) \
-    do { \
-        int nbytes = gInputBufferEnd - gInputBufferNext; \
-        if (nbytes > 0) { \
-            if (nbytes > max_size) { \
-                nbytes = max_size; \
-            } \
-            memcpy(buf, gInputBufferNext, nbytes); \
-            gInputBufferNext += nbytes; \
-            result = nbytes; \
-        } else { \
-            result = YY_NULL; \
-        } \
-    } while (false)
-#endif  // AMEND_LEXER_BUFFER_INPUT
-
-%}
-
-%option noyywrap
-
-%x QUOTED_STRING BOOLEAN WORDS
-
-ident [a-zA-Z_][a-zA-Z_0-9]*
-word [^ \t\r\n"]+
-
-%%
-    /* This happens at the beginning of each call to yylex().
-     */
-    if (gArgumentType == AM_WORD_ARGS) {
-        BEGIN(WORDS);
-    } else if (gArgumentType == AM_BOOLEAN_ARGS) {
-        BEGIN(BOOLEAN);
-    }
-
-        /*xxx require everything to be 7-bit-clean, printable characters */
-<INITIAL>{
-        {ident}/[ \t\r\n] {
-                /* The only token we recognize in the initial
-                 * state is an identifier followed by whitespace.
-                 */
-                setString(&gStr, yytext);
-                yylval.literalString = gStr.value;
-                return TOK_IDENTIFIER;
-            }
-    }
-
-<BOOLEAN>{
-        {ident} {
-                /* Non-quoted identifier-style string */
-                setString(&gStr, yytext);
-                yylval.literalString = gStr.value;
-                return TOK_IDENTIFIER;
-            }
-        "&&"    return TOK_AND;
-        "||"    return TOK_OR;
-        "=="    return TOK_EQ;
-        "!="    return TOK_NE;
-        ">="    return TOK_GE;
-        "<="    return TOK_LE;
-        [<>()!,] return yytext[0];
-    }
-
-    /* Double-quoted string handling */
-
-<WORDS,BOOLEAN>\"  {
-        /* Initial quote */
-        gStr.nextc = gStr.value;
-        BEGIN(QUOTED_STRING);
-    }
-
-<QUOTED_STRING>{
-        \"  {
-                /* Closing quote */
-                BEGIN(INITIAL);
-                addCharToString(&gStr, '\0');
-                yylval.literalString = gStr.value;
-                if (gArgumentType == AM_WORD_ARGS) {
-                    return TOK_WORD;
-                } else {
-                    return TOK_STRING;
-                }
-            }
-
-        <<EOF>> |
-        \n  {
-                /* Unterminated string */
-                yyerror("unterminated string");
-                return TOK_ERROR;
-            }
-
-        \\\" {
-                /* Escaped quote */
-                addCharToString(&gStr, '"');
-            }
-
-        \\\\ {
-                /* Escaped backslash */
-                addCharToString(&gStr, '\\');
-            }
-
-        \\. {
-                /* No other escapes allowed. */
-                gErrorMessage = "illegal escape";
-                return TOK_ERROR;
-            }
-
-        [^\\\n\"]+ {
-                /* String contents */
-                char *p = yytext;
-                while (*p != '\0') {
-        /* TODO: add the whole string at once */
-                    addCharToString(&gStr, *p++);
-                }
-            }
-    }
-
-<WORDS>{
-        /*xxx look out for backslashes; escape backslashes and quotes */
-        /*xxx if a quote is right against a char, we should append */
-        {word} {
-                /* Whitespace-separated word */
-                setString(&gStr, yytext);
-                yylval.literalString = gStr.value;
-                return TOK_WORD;
-            }
-    }
-
-<INITIAL,WORDS,BOOLEAN>{
-        \n  {
-                /* Count lines */
-                gLineNumber++;
-                gArgumentType = AM_UNKNOWN_ARGS;
-                BEGIN(INITIAL);
-                return TOK_EOL;
-            }
-
-        /*xxx backslashes to extend lines? */
-            /* Skip whitespace and comments.
-             */
-        [ \t\r]+ ;
-        #.*      ;
-
-        .   {
-                /* Fail on anything we didn't expect. */
-                gErrorMessage = "unexpected character";
-                return TOK_ERROR;
-            }
-    }
-%%
-
-void
-yyerror(const char *msg)
-{
-    if (!strcmp(msg, "syntax error") && gErrorMessage != NULL) {
-        msg = gErrorMessage;
-        gErrorMessage = NULL;
-    }
-    fprintf(stderr, "line %d: %s at '%s'\n", gLineNumber, msg, yytext);
-}
-
-#if AMEND_LEXER_BUFFER_INPUT
-void
-setLexerInputBuffer(const char *buf, size_t buflen)
-{
-    gLineNumber = 1;
-    gInputBuffer = buf;
-    gInputBufferNext = gInputBuffer;
-    gInputBufferEnd = gInputBuffer + buflen;
-}
-#endif  // AMEND_LEXER_BUFFER_INPUT
-
-void
-setLexerArgumentType(AmArgumentType type)
-{
-    gArgumentType = type;
-}
-
-int
-getLexerLineNumber(void)
-{
-    return gLineNumber;
-}
diff --git a/amend/main.c b/amend/main.c
deleted file mode 100644
index 9bb0785..0000000
--- a/amend/main.c
+++ /dev/null
@@ -1,195 +0,0 @@
-/*
- * Copyright (C) 2007 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 <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include "ast.h"
-#include "lexer.h"
-#include "parser.h"
-#include "register.h"
-#include "execute.h"
-
-void
-lexTest()
-{
-    int token;
-    do {
-        token = yylex();
-        if (token == 0) {
-            printf(" EOF");
-            fflush(stdout);
-            break;
-        } else {
-            printf(" %s", tokenToString(token));
-            fflush(stdout);
-            if (token == TOK_IDENTIFIER) {
-                if (strcmp(yylval.literalString, "assert") == 0) {
-                    setLexerArgumentType(AM_BOOLEAN_ARGS);
-                } else {
-                    setLexerArgumentType(AM_WORD_ARGS);
-                }
-                do {
-                    token = yylex();
-                    printf(" %s", tokenToString(token));
-                    fflush(stdout);
-                } while (token != TOK_EOL && token != TOK_EOF && token != 0);
-            } else if (token != TOK_EOL) {
-                fprintf(stderr, "syntax error: expected identifier\n");
-                break;
-            }
-        }
-    } while (token != 0);
-    printf("\n");
-}
-
-void
-usage()
-{
-    printf("usage: amend [--debug-lex|--debug-ast] [<filename>]\n");
-    exit(1);
-}
-
-extern const AmCommandList *gCommands;
-int
-main(int argc, char *argv[])
-{
-    FILE *inputFile = NULL;
-    bool debugLex = false;
-    bool debugAst = false;
-    const char *fileName = NULL;
-    int err;
-
-#if 1
-    extern int test_symtab(void);
-    int ret = test_symtab();
-    if (ret != 0) {
-        fprintf(stderr, "test_symtab() failed: %d\n", ret);
-        exit(ret);
-    }
-    extern int test_cmd_fn(void);
-    ret = test_cmd_fn();
-    if (ret != 0) {
-        fprintf(stderr, "test_cmd_fn() failed: %d\n", ret);
-        exit(ret);
-    }
-    extern int test_permissions(void);
-    ret = test_permissions();
-    if (ret != 0) {
-        fprintf(stderr, "test_permissions() failed: %d\n", ret);
-        exit(ret);
-    }
-#endif
-
-    argc--;
-    argv++;
-    while (argc > 0) {
-        if (strcmp("--debug-lex", argv[0]) == 0) {
-            debugLex = true;
-        } else if (strcmp("--debug-ast", argv[0]) == 0) {
-            debugAst = true;
-        } else if (argv[0][0] == '-') {
-            fprintf(stderr, "amend: Unknown option \"%s\"\n", argv[0]);
-            usage();
-        } else {
-            fileName = argv[0];
-        }
-        argc--;
-        argv++;
-    }
-
-    if (fileName != NULL) {
-        inputFile = fopen(fileName, "r");
-        if (inputFile == NULL) {
-            fprintf(stderr, "amend: Can't open input file '%s'\n", fileName);
-            usage();
-        }
-    }
-
-    commandInit();
-//xxx clean up
-
-    err = registerUpdateCommands();
-    if (err < 0) {
-        fprintf(stderr, "amend: Error registering commands: %d\n", err);
-        exit(-err);
-    }
-    err = registerUpdateFunctions();
-    if (err < 0) {
-        fprintf(stderr, "amend: Error registering functions: %d\n", err);
-        exit(-err);
-    }
-
-#if AMEND_LEXER_BUFFER_INPUT
-    if (inputFile == NULL) {
-        fprintf(stderr, "amend: No input file\n");
-        usage();
-    }
-    char *fileData;
-    int fileDataLen;
-    fseek(inputFile, 0, SEEK_END);
-    fileDataLen = ftell(inputFile);
-    rewind(inputFile);
-    if (fileDataLen < 0) {
-        fprintf(stderr, "amend: Can't get file length\n");
-        exit(2);
-    } else if (fileDataLen == 0) {
-        printf("amend: Empty input file\n");
-        exit(0);
-    }
-    fileData = (char *)malloc(fileDataLen + 1);
-    if (fileData == NULL) {
-        fprintf(stderr, "amend: Can't allocate %d bytes\n", fileDataLen + 1);
-        exit(2);
-    }
-    size_t nread = fread(fileData, 1, fileDataLen, inputFile);
-    if (nread != (size_t)fileDataLen) {
-        fprintf(stderr, "amend: Didn't read %d bytes, only %zd\n", fileDataLen,
-                nread);
-        exit(2);
-    }
-    fileData[fileDataLen] = '\0';
-    setLexerInputBuffer(fileData, fileDataLen);
-#else
-    if (inputFile == NULL) {
-        inputFile = stdin;
-    }
-    yyset_in(inputFile);
-#endif
-
-    if (debugLex) {
-        lexTest();
-    } else {
-        int ret = yyparse();
-        if (ret != 0) {
-            fprintf(stderr, "amend: Parse failed (%d)\n", ret);
-            exit(2);
-        } else {
-            if (debugAst) {
-                dumpCommandList(gCommands);
-            }
-printf("amend: Parse successful.\n");
-            ret = execCommandList((ExecContext *)1, gCommands);
-            if (ret != 0) {
-                fprintf(stderr, "amend: Execution failed (%d)\n", ret);
-                exit(3);
-            }
-printf("amend: Execution successful.\n");
-        }
-    }
-
-    return 0;
-}
diff --git a/amend/parser.h b/amend/parser.h
deleted file mode 100644
index aeb8657..0000000
--- a/amend/parser.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (C) 2007 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 AMEND_PARSER_H_
-#define AMEND_PARSER_H_
-
-#include "parser_y.h"
-
-int yyparse(void);
-
-#endif  // AMEND_PARSER_H_
diff --git a/amend/parser_y.y b/amend/parser_y.y
deleted file mode 100644
index b634016..0000000
--- a/amend/parser_y.y
+++ /dev/null
@@ -1,430 +0,0 @@
-/*
- * Copyright (C) 2007 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.
- */
-
-%{
-#undef NDEBUG
-    #include <stdlib.h>
-    #include <string.h>
-    #include <assert.h>
-    #include <stdio.h>
-    #include "ast.h"
-    #include "lexer.h"
-    #include "commands.h"
-
-    void yyerror(const char *msg);
-    int yylex(void);
-
-#define STRING_COMPARISON(out, a1, sop, a2) \
-    do { \
-        out = (AmBooleanValue *)malloc(sizeof(AmBooleanValue)); \
-        if (out == NULL) { \
-            YYABORT; \
-        } \
-        out->type = AM_BVAL_STRING_COMPARISON; \
-        out->u.stringComparison.op = sop; \
-        out->u.stringComparison.arg1 = a1; \
-        out->u.stringComparison.arg2 = a2; \
-    } while (false)
-
-#define BOOLEAN_EXPRESSION(out, a1, bop, a2) \
-    do { \
-        out = (AmBooleanValue *)malloc(sizeof(AmBooleanValue)); \
-        if (out == NULL) { \
-            YYABORT; \
-        } \
-        out->type = AM_BVAL_EXPRESSION; \
-        out->u.expression.op = bop; \
-        out->u.expression.arg1 = a1; \
-        out->u.expression.arg2 = a2; \
-    } while (false)
-
-AmCommandList *gCommands = NULL;
-%}
-
-%start  lines
-
-%union  {
-        char *literalString;
-        AmFunctionArgumentBuilder *functionArgumentBuilder;
-        AmFunctionArguments *functionArguments;
-        AmFunctionCall *functionCall;
-        AmStringValue *stringValue;
-        AmBooleanValue *booleanValue;
-        AmWordListBuilder *wordListBuilder;
-        AmCommandArguments *commandArguments;
-        AmCommand *command;
-        AmCommandList *commandList;
-    }
-
-%token  TOK_AND TOK_OR TOK_EQ TOK_NE TOK_GE TOK_LE TOK_EOF TOK_EOL TOK_ERROR
-%token  <literalString> TOK_STRING TOK_IDENTIFIER TOK_WORD
-
-%type   <commandList> lines
-%type   <command> command line
-%type   <functionArgumentBuilder> function_arguments
-%type   <functionArguments> function_arguments_or_empty
-%type   <functionCall> function_call
-%type   <literalString> function_name
-%type   <stringValue> string_value
-%type   <booleanValue> boolean_expression
-%type   <wordListBuilder> word_list
-%type   <commandArguments> arguments
-
-/* Operator precedence, weakest to strongest.
- * Same as C/Java precedence.
- */
-
-%left   TOK_OR
-%left   TOK_AND
-%left   TOK_EQ TOK_NE
-%left   '<' '>' TOK_LE TOK_GE
-%right   '!'
-
-%%
-
-lines :     /* empty */
-                {
-                    $$ = (AmCommandList *)malloc(sizeof(AmCommandList));
-                    if ($$ == NULL) {
-                        YYABORT;
-                    }
-gCommands = $$;
-                    $$->arraySize = 64;
-                    $$->commandCount = 0;
-                    $$->commands = (AmCommand **)malloc(
-                            sizeof(AmCommand *) * $$->arraySize);
-                    if ($$->commands == NULL) {
-                        YYABORT;
-                    }
-                }
-        |   lines line
-                {
-                    if ($2 != NULL) {
-                        if ($1->commandCount >= $1->arraySize) {
-                            AmCommand **newArray;
-                            newArray = (AmCommand **)realloc($$->commands,
-                                sizeof(AmCommand *) * $$->arraySize * 2);
-                            if (newArray == NULL) {
-                                YYABORT;
-                            }
-                            $$->commands = newArray;
-                            $$->arraySize *= 2;
-                        }
-                        $1->commands[$1->commandCount++] = $2;
-                    }
-                }
-        ;
-
-line :      line_ending
-                {
-                    $$ = NULL;  /* ignore blank lines */
-                }
-        |   command arguments line_ending
-                {
-                    $$ = $1;
-                    $$->args = $2;
-                    setLexerArgumentType(AM_UNKNOWN_ARGS);
-                }
-        ;
-
-command :   TOK_IDENTIFIER
-                {
-                    Command *cmd = findCommand($1);
-                    if (cmd == NULL) {
-                        fprintf(stderr, "Unknown command \"%s\"\n", $1);
-                        YYABORT;
-                    }
-                    $$ = (AmCommand *)malloc(sizeof(AmCommand));
-                    if ($$ == NULL) {
-                        YYABORT;
-                    }
-                    $$->line = getLexerLineNumber();
-                    $$->name = strdup($1);
-                    if ($$->name == NULL) {
-                        YYABORT;
-                    }
-                    $$->args = NULL;
-                    CommandArgumentType argType = getCommandArgumentType(cmd);
-                    if (argType == CMD_ARGS_BOOLEAN) {
-                        setLexerArgumentType(AM_BOOLEAN_ARGS);
-                    } else {
-                        setLexerArgumentType(AM_WORD_ARGS);
-                    }
-                    $$->cmd = cmd;
-                }
-        ;
-
-line_ending :
-            TOK_EOL
-        |   TOK_EOF
-        ;
-
-arguments : boolean_expression
-                {
-                    $$ = (AmCommandArguments *)malloc(
-                            sizeof(AmCommandArguments));
-                    if ($$ == NULL) {
-                        YYABORT;
-                    }
-                    $$->booleanArgs = true;
-                    $$->u.b = $1;
-                }
-        |   word_list
-                {
-                    /* Convert the builder list into an array.
-                     * Do it in reverse order; the words were pushed
-                     * onto the list in LIFO order.
-                     */
-                    AmWordList *w = (AmWordList *)malloc(sizeof(AmWordList));
-                    if (w == NULL) {
-                        YYABORT;
-                    }
-                    if ($1 != NULL) {
-                        AmWordListBuilder *words = $1;
-
-                        w->argc = words->wordCount;
-                        w->argv = (const char **)malloc(w->argc *
-                                        sizeof(char *));
-                        if (w->argv == NULL) {
-                            YYABORT;
-                        }
-                        int i;
-                        for (i = w->argc; words != NULL && i > 0; --i) {
-                            AmWordListBuilder *f = words;
-                            w->argv[i-1] = words->word;
-                            words = words->next;
-                            free(f);
-                        }
-                        assert(i == 0);
-                        assert(words == NULL);
-                    } else {
-                        w->argc = 0;
-                        w->argv = NULL;
-                    }
-                    $$ = (AmCommandArguments *)malloc(
-                            sizeof(AmCommandArguments));
-                    if ($$ == NULL) {
-                        YYABORT;
-                    }
-                    $$->booleanArgs = false;
-                    $$->u.w = w;
-                }
-        ;
-
-word_list : /* empty */
-                { $$ = NULL; }
-        |   word_list TOK_WORD
-                {
-                    if ($1 == NULL) {
-                        $$ = (AmWordListBuilder *)malloc(
-                                sizeof(AmWordListBuilder));
-                        if ($$ == NULL) {
-                            YYABORT;
-                        }
-                        $$->next = NULL;
-                        $$->wordCount = 1;
-                    } else {
-                        $$ = (AmWordListBuilder *)malloc(
-                                sizeof(AmWordListBuilder));
-                        if ($$ == NULL) {
-                            YYABORT;
-                        }
-                        $$->next = $1;
-                        $$->wordCount = $$->next->wordCount + 1;
-                    }
-                    $$->word = strdup($2);
-                    if ($$->word == NULL) {
-                        YYABORT;
-                    }
-                }
-        ;
-
-boolean_expression :
-            '!' boolean_expression
-                {
-                    $$ = (AmBooleanValue *)malloc(sizeof(AmBooleanValue));
-                    if ($$ == NULL) {
-                        YYABORT;
-                    }
-                    $$->type = AM_BVAL_EXPRESSION;
-                    $$->u.expression.op = AM_BOP_NOT;
-                    $$->u.expression.arg1 = $2;
-                    $$->u.expression.arg2 = NULL;
-                }
-    /* TODO: if both expressions are literals, evaluate now */
-        |   boolean_expression TOK_AND boolean_expression
-                { BOOLEAN_EXPRESSION($$, $1, AM_BOP_AND, $3); }
-        |   boolean_expression TOK_OR boolean_expression
-                { BOOLEAN_EXPRESSION($$, $1, AM_BOP_OR, $3); }
-        |   boolean_expression TOK_EQ boolean_expression
-                { BOOLEAN_EXPRESSION($$, $1, AM_BOP_EQ, $3); }
-        |   boolean_expression TOK_NE boolean_expression
-                { BOOLEAN_EXPRESSION($$, $1, AM_BOP_NE, $3); }
-        |   '(' boolean_expression ')'
-                { $$ = $2; }
-    /* TODO: if both strings are literals, evaluate now */
-        |   string_value '<' string_value
-                { STRING_COMPARISON($$, $1, AM_SOP_LT, $3); }
-        |   string_value '>' string_value
-                { STRING_COMPARISON($$, $1, AM_SOP_GT, $3); }
-        |   string_value TOK_EQ string_value
-                { STRING_COMPARISON($$, $1, AM_SOP_EQ, $3); }
-        |   string_value TOK_NE string_value
-                { STRING_COMPARISON($$, $1, AM_SOP_NE, $3); }
-        |   string_value TOK_LE string_value
-                { STRING_COMPARISON($$, $1, AM_SOP_LE, $3); }
-        |   string_value TOK_GE string_value
-                { STRING_COMPARISON($$, $1, AM_SOP_GE, $3); }
-        ;
-
-string_value :
-            TOK_IDENTIFIER
-                {
-                    $$ = (AmStringValue *)malloc(sizeof(AmStringValue));
-                    if ($$ == NULL) {
-                        YYABORT;
-                    }
-                    $$->type = AM_SVAL_LITERAL;
-                    $$->u.literal = strdup($1);
-                    if ($$->u.literal == NULL) {
-                        YYABORT;
-                    }
-                }
-        |   TOK_STRING
-                {
-                    $$ = (AmStringValue *)malloc(sizeof(AmStringValue));
-                    if ($$ == NULL) {
-                        YYABORT;
-                    }
-                    $$->type = AM_SVAL_LITERAL;
-                    $$->u.literal = strdup($1);
-                    if ($$->u.literal == NULL) {
-                        YYABORT;
-                    }
-                }
-        |   function_call
-                {
-                    $$ = (AmStringValue *)malloc(sizeof(AmStringValue));
-                    if ($$ == NULL) {
-                        YYABORT;
-                    }
-                    $$->type = AM_SVAL_FUNCTION;
-                    $$->u.function = $1;
-                }
-        ;
-
-        /* We can't just say
-         *  TOK_IDENTIFIER '(' function_arguments_or_empty ')'
-         * because parsing function_arguments_or_empty will clobber
-         * the underlying string that yylval.literalString points to.
-         */
-function_call :
-            function_name '(' function_arguments_or_empty ')'
-                {
-                    Function *fn = findFunction($1);
-                    if (fn == NULL) {
-                        fprintf(stderr, "Unknown function \"%s\"\n", $1);
-                        YYABORT;
-                    }
-                    $$ = (AmFunctionCall *)malloc(sizeof(AmFunctionCall));
-                    if ($$ == NULL) {
-                        YYABORT;
-                    }
-                    $$->name = $1;
-                    if ($$->name == NULL) {
-                        YYABORT;
-                    }
-                    $$->fn = fn;
-                    $$->args = $3;
-                }
-        ;
-
-function_name :
-            TOK_IDENTIFIER
-                {
-                    $$ = strdup($1);
-                }
-        ;
-
-function_arguments_or_empty :
-            /* empty */
-                {
-                    $$ = (AmFunctionArguments *)malloc(
-                            sizeof(AmFunctionArguments));
-                    if ($$ == NULL) {
-                        YYABORT;
-                    }
-                    $$->argc = 0;
-                    $$->argv = NULL;
-                }
-        |   function_arguments
-                {
-                    AmFunctionArgumentBuilder *args = $1;
-                    assert(args != NULL);
-
-                    /* Convert the builder list into an array.
-                     * Do it in reverse order; the args were pushed
-                     * onto the list in LIFO order.
-                     */
-                    $$ = (AmFunctionArguments *)malloc(
-                            sizeof(AmFunctionArguments));
-                    if ($$ == NULL) {
-                        YYABORT;
-                    }
-                    $$->argc = args->argCount;
-                    $$->argv = (AmStringValue *)malloc(
-                            $$->argc * sizeof(AmStringValue));
-                    if ($$->argv == NULL) {
-                        YYABORT;
-                    }
-                    int i;
-                    for (i = $$->argc; args != NULL && i > 0; --i) {
-                        AmFunctionArgumentBuilder *f = args;
-                        $$->argv[i-1] = *args->arg;
-                        args = args->next;
-                        free(f->arg);
-                        free(f);
-                    }
-                    assert(i == 0);
-                    assert(args == NULL);
-                }
-        ;
-
-function_arguments :
-            string_value
-                {
-                    $$ = (AmFunctionArgumentBuilder *)malloc(
-                            sizeof(AmFunctionArgumentBuilder));
-                    if ($$ == NULL) {
-                        YYABORT;
-                    }
-                    $$->next = NULL;
-                    $$->argCount = 1;
-                    $$->arg = $1;
-                }
-        |   function_arguments ',' string_value
-                {
-                    $$ = (AmFunctionArgumentBuilder *)malloc(
-                            sizeof(AmFunctionArgumentBuilder));
-                    if ($$ == NULL) {
-                        YYABORT;
-                    }
-                    $$->next = $1;
-                    $$->argCount = $$->next->argCount + 1;
-                    $$->arg = $3;
-                }
-        ;
-    /* xxx this whole tool needs to be hardened */
diff --git a/amend/permissions.c b/amend/permissions.c
deleted file mode 100644
index a642d0b..0000000
--- a/amend/permissions.c
+++ /dev/null
@@ -1,270 +0,0 @@
-/*
- * Copyright (C) 2007 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 <stdlib.h>
-#include <string.h>
-#include "permissions.h"
-
-int
-initPermissionRequestList(PermissionRequestList *list)
-{
-    if (list != NULL) {
-        list->requests = NULL;
-        list->numRequests = 0;
-        list->requestsAllocated = 0;
-        return 0;
-    }
-    return -1;
-}
-
-int
-addPermissionRequestToList(PermissionRequestList *list,
-        const char *path, bool recursive, unsigned int permissions)
-{
-    if (list == NULL || list->numRequests < 0 ||
-            list->requestsAllocated < list->numRequests || path == NULL)
-    {
-        return -1;
-    }
-
-    if (list->numRequests == list->requestsAllocated) {
-        int newSize;
-        PermissionRequest *newRequests;
-
-        newSize = list->requestsAllocated * 2;
-        if (newSize < 16) {
-            newSize = 16;
-        }
-        newRequests = (PermissionRequest *)realloc(list->requests,
-                newSize * sizeof(PermissionRequest));
-        if (newRequests == NULL) {
-            return -2;
-        }
-        list->requests = newRequests;
-        list->requestsAllocated = newSize;
-    }
-
-    PermissionRequest *req;
-    req = &list->requests[list->numRequests++];
-    req->path = strdup(path);
-    if (req->path == NULL) {
-        list->numRequests--;
-        return -3;
-    }
-    req->recursive = recursive;
-    req->requested = permissions;
-    req->allowed = 0;
-
-    return 0;
-}
-
-void
-freePermissionRequestListElements(PermissionRequestList *list)
-{
-    if (list != NULL && list->numRequests >= 0 &&
-            list->requestsAllocated >= list->numRequests)
-    {
-        int i;
-        for (i = 0; i < list->numRequests; i++) {
-            free((void *)list->requests[i].path);
-        }
-        free(list->requests);
-        initPermissionRequestList(list);
-    }
-}
-
-/*
- * Global permission table
- */
-
-static struct {
-    Permission *permissions;
-    int numPermissionEntries;
-    int allocatedPermissionEntries;
-    bool permissionStateInitialized;
-} gPermissionState = {
-#if 1
-    NULL, 0, 0, false
-#else
-    .permissions = NULL,
-    .numPermissionEntries = 0,
-    .allocatedPermissionEntries = 0,
-    .permissionStateInitialized = false
-#endif
-};
-
-int
-permissionInit()
-{
-    if (gPermissionState.permissionStateInitialized) {
-        return -1;
-    }
-    gPermissionState.permissions = NULL;
-    gPermissionState.numPermissionEntries = 0;
-    gPermissionState.allocatedPermissionEntries = 0;
-    gPermissionState.permissionStateInitialized = true;
-//xxx maybe add an "namespace root gets no permissions" fallback by default
-    return 0;
-}
-
-void
-permissionCleanup()
-{
-    if (gPermissionState.permissionStateInitialized) {
-        gPermissionState.permissionStateInitialized = false;
-        if (gPermissionState.permissions != NULL) {
-            int i;
-            for (i = 0; i < gPermissionState.numPermissionEntries; i++) {
-                free((void *)gPermissionState.permissions[i].path);
-            }
-            free(gPermissionState.permissions);
-        }
-    }
-}
-
-int
-getPermissionCount()
-{
-    if (gPermissionState.permissionStateInitialized) {
-        return gPermissionState.numPermissionEntries;
-    }
-    return -1;
-}
-
-const Permission *
-getPermissionAt(int index)
-{
-    if (!gPermissionState.permissionStateInitialized) {
-        return NULL;
-    }
-    if (index < 0 || index >= gPermissionState.numPermissionEntries) {
-        return NULL;
-    }
-    return &gPermissionState.permissions[index];
-}
-
-int
-getAllowedPermissions(const char *path, bool recursive,
-        unsigned int *outAllowed)
-{
-    if (!gPermissionState.permissionStateInitialized) {
-        return -2;
-    }
-    if (outAllowed == NULL) {
-        return -1;
-    }
-    *outAllowed = 0;
-    if (path == NULL) {
-        return -1;
-    }
-    //TODO: implement this for real.
-    recursive = false;
-    *outAllowed = PERMSET_ALL;
-    return 0;
-}
-
-int
-countPermissionConflicts(PermissionRequestList *requests, bool updateAllowed)
-{
-    if (!gPermissionState.permissionStateInitialized) {
-        return -2;
-    }
-    if (requests == NULL || requests->requests == NULL ||
-            requests->numRequests < 0 ||
-            requests->requestsAllocated < requests->numRequests)
-    {
-        return -1;
-    }
-    int conflicts = 0;
-    int i;
-    for (i = 0; i < requests->numRequests; i++) {
-        PermissionRequest *req;
-        unsigned int allowed;
-        int ret;
-
-        req = &requests->requests[i];
-        ret = getAllowedPermissions(req->path, req->recursive, &allowed);
-        if (ret < 0) {
-            return ret;
-        }
-        if ((req->requested & ~allowed) != 0) {
-            conflicts++;
-        }
-        if (updateAllowed) {
-            req->allowed = allowed;
-        }
-    }
-    return conflicts;
-}
-
-int
-registerPermissionSet(int count, Permission *set)
-{
-    if (!gPermissionState.permissionStateInitialized) {
-        return -2;
-    }
-    if (count < 0 || (count > 0 && set == NULL)) {
-        return -1;
-    }
-    if (count == 0) {
-        return 0;
-    }
-
-    if (gPermissionState.numPermissionEntries + count >=
-            gPermissionState.allocatedPermissionEntries)
-    {
-        Permission *newList;
-        int newSize;
-
-        newSize = (gPermissionState.allocatedPermissionEntries + count) * 2;
-        if (newSize < 16) {
-            newSize = 16;
-        }
-        newList = (Permission *)realloc(gPermissionState.permissions,
-                newSize * sizeof(Permission));
-        if (newList == NULL) {
-            return -3;
-        }
-        gPermissionState.permissions = newList;
-        gPermissionState.allocatedPermissionEntries = newSize;
-    }
-
-    Permission *p = &gPermissionState.permissions[
-                        gPermissionState.numPermissionEntries];
-    int i;
-    for (i = 0; i < count; i++) {
-        *p = set[i];
-        //TODO: cache the strlen of the path
-        //TODO: normalize; strip off trailing /
-        p->path = strdup(p->path);
-        if (p->path == NULL) {
-            /* If we can't add all of the entries, we don't
-             * add any of them.
-             */
-            Permission *pp = &gPermissionState.permissions[
-                                gPermissionState.numPermissionEntries];
-            while (pp != p) {
-                free((void *)pp->path);
-                pp++;
-            }
-            return -4;
-        }
-        p++;
-    }
-    gPermissionState.numPermissionEntries += count;
-
-    return 0;
-}
diff --git a/amend/permissions.h b/amend/permissions.h
deleted file mode 100644
index 5b1d14d..0000000
--- a/amend/permissions.h
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright (C) 2007 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 AMEND_PERMISSIONS_H_
-#define AMEND_PERMISSIONS_H_
-
-#include <stdbool.h>
-
-#define PERM_NONE   (0)
-#define PERM_STAT   (1<<0)
-#define PERM_READ   (1<<1)
-#define PERM_WRITE  (1<<2)  // including create, delete, mkdir, rmdir
-#define PERM_CHMOD  (1<<3)
-#define PERM_CHOWN  (1<<4)
-#define PERM_CHGRP  (1<<5)
-#define PERM_SETUID (1<<6)
-#define PERM_SETGID (1<<7)
-
-#define PERMSET_READ (PERM_STAT | PERM_READ)
-#define PERMSET_WRITE (PERMSET_READ | PERM_WRITE)
-
-#define PERMSET_ALL \
-    (PERM_STAT | PERM_READ | PERM_WRITE | PERM_CHMOD | \
-    PERM_CHOWN | PERM_CHGRP | PERM_SETUID | PERM_SETGID)
-
-typedef struct {
-    unsigned int requested;
-    unsigned int allowed;
-    const char *path;
-    bool recursive;
-} PermissionRequest;
-
-typedef struct {
-    PermissionRequest *requests;
-    int numRequests;
-    int requestsAllocated;
-} PermissionRequestList;
-
-/* Properly clear out a PermissionRequestList.
- *
- * @return 0 if list is non-NULL, negative otherwise.
- */
-int initPermissionRequestList(PermissionRequestList *list);
-
-/* Add a permission request to the list, allocating more space
- * if necessary.
- *
- * @return 0 on success or a negative value on failure.
- */
-int addPermissionRequestToList(PermissionRequestList *list,
-        const char *path, bool recursive, unsigned int permissions);
-
-/* Free anything allocated by addPermissionRequestToList().  The caller
- * is responsible for freeing the actual PermissionRequestList.
- */
-void freePermissionRequestListElements(PermissionRequestList *list);
-
-
-/*
- * Global permission table
- */
-
-typedef struct {
-    const char *path;
-    unsigned int allowed;
-} Permission;
-
-int permissionInit(void);
-void permissionCleanup(void);
-
-/* Returns the allowed permissions for the path in "outAllowed".
- * Returns 0 if successful, negative if a parameter or global state
- * is bad.
- */
-int getAllowedPermissions(const char *path, bool recursive,
-        unsigned int *outAllowed);
-
-/* More-recently-registered permissions override older permissions.
- */
-int registerPermissionSet(int count, Permission *set);
-
-/* Check to make sure that each request is allowed.
- *
- * @param requests The list of permission requests
- * @param updateAllowed If true, update the "allowed" field in each
- *                      element of the list
- * @return the number of requests that were denied, or negative if
- *         an error occurred.
- */
-int countPermissionConflicts(PermissionRequestList *requests,
-        bool updateAllowed);
-
-/* Inspection/testing/debugging functions
- */
-int getPermissionCount(void);
-const Permission *getPermissionAt(int index);
-
-#endif  // AMEND_PERMISSIONS_H_
diff --git a/amend/register.c b/amend/register.c
deleted file mode 100644
index e45a973..0000000
--- a/amend/register.c
+++ /dev/null
@@ -1,410 +0,0 @@
-/*
- * Copyright (C) 2007 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 <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#undef NDEBUG
-#include <assert.h>
-#include "commands.h"
-
-#include "register.h"
-
-#define UNUSED(p)   ((void)(p))
-
-#define CHECK_BOOL() \
-    do { \
-        assert(argv == NULL); \
-        if (argv != NULL) return -1; \
-        assert(argc == true || argc == false); \
-        if (argc != true && argc != false) return -1; \
-    } while (false)
-
-#define CHECK_WORDS() \
-    do { \
-        assert(argc >= 0); \
-        if (argc < 0) return -1; \
-        assert(argc == 0 || argv != NULL); \
-        if (argc != 0 && argv == NULL) return -1; \
-        if (permissions != NULL) { \
-            int CW_I_; \
-            for (CW_I_ = 0; CW_I_ < argc; CW_I_++) { \
-                assert(argv[CW_I_] != NULL); \
-                if (argv[CW_I_] == NULL) return -1; \
-            } \
-        } \
-    } while (false)
-
-#define CHECK_FN() \
-    do { \
-        CHECK_WORDS(); \
-        if (permissions != NULL) { \
-            assert(result == NULL); \
-            if (result != NULL) return -1; \
-        } else { \
-            assert(result != NULL); \
-            if (result == NULL) return -1; \
-        } \
-    } while (false)
-
-#define NO_PERMS(perms) \
-    do { \
-        PermissionRequestList *NP_PRL_ = (perms); \
-        if (NP_PRL_ != NULL) { \
-            int NP_RET_ = addPermissionRequestToList(NP_PRL_, \
-                    "", false, PERM_NONE); \
-            if (NP_RET_ < 0) { \
-                /* Returns from the calling function. \
-                 */ \
-                return NP_RET_; \
-            } \
-        } \
-    } while (false)
-
-/*
- * Command definitions
- */
-
-/* assert <boolexpr>
- */
-static int
-cmd_assert(const char *name, void *cookie, int argc, const char *argv[],
-        PermissionRequestList *permissions)
-{
-    UNUSED(name);
-    UNUSED(cookie);
-    CHECK_BOOL();
-    NO_PERMS(permissions);
-
-    /* If our argument is false, return non-zero (failure)
-     * If our argument is true, return zero (success)
-     */
-    if (argc) {
-        return 0;
-    } else {
-        return 1;
-    }
-}
-
-/* format <root>
- */
-static int
-cmd_format(const char *name, void *cookie, int argc, const char *argv[],
-        PermissionRequestList *permissions)
-{
-    UNUSED(name);
-    UNUSED(cookie);
-    CHECK_WORDS();
-//xxx
-    return -1;
-}
-
-/* copy_dir <srcdir> <dstdir>
- */
-static int
-cmd_copy_dir(const char *name, void *cookie, int argc, const char *argv[],
-        PermissionRequestList *permissions)
-{
-    UNUSED(name);
-    UNUSED(cookie);
-    CHECK_WORDS();
-//xxx
-    return -1;
-}
-
-/* delete <srcdir> <dstdir>
- */
-static int
-cmd_delete(const char *name, void *cookie, int argc, const char *argv[],
-        PermissionRequestList *permissions)
-{
-    UNUSED(name);
-    UNUSED(cookie);
-    CHECK_WORDS();
-//xxx
-    return -1;
-}
-
-/* mark <resource> dirty|clean
- */
-static int
-cmd_mark(const char *name, void *cookie, int argc, const char *argv[],
-        PermissionRequestList *permissions)
-{
-    UNUSED(name);
-    UNUSED(cookie);
-    CHECK_WORDS();
-//xxx when marking, save the top-level hash at the mark point
-//    so we can retry on failure.  Otherwise the hashes won't match,
-//    or someone could intentionally dirty the FS to force a downgrade
-//xxx
-    return -1;
-}
-
-/* done
- */
-static int
-cmd_done(const char *name, void *cookie, int argc, const char *argv[],
-        PermissionRequestList *permissions)
-{
-    UNUSED(name);
-    UNUSED(cookie);
-    CHECK_WORDS();
-//xxx
-    return -1;
-}
-
-int
-registerUpdateCommands()
-{
-    int ret;
-
-    ret = registerCommand("assert", CMD_ARGS_BOOLEAN, cmd_assert, NULL);
-    if (ret < 0) return ret;
-
-    ret = registerCommand("copy_dir", CMD_ARGS_WORDS, cmd_copy_dir, NULL);
-    if (ret < 0) return ret;
-
-    ret = registerCommand("delete", CMD_ARGS_WORDS, cmd_delete, NULL);
-    if (ret < 0) return ret;
-
-    ret = registerCommand("format", CMD_ARGS_WORDS, cmd_format, NULL);
-    if (ret < 0) return ret;
-
-    ret = registerCommand("mark", CMD_ARGS_WORDS, cmd_mark, NULL);
-    if (ret < 0) return ret;
-
-    ret = registerCommand("done", CMD_ARGS_WORDS, cmd_done, NULL);
-    if (ret < 0) return ret;
-
-//xxx some way to fix permissions
-//xxx could have "installperms" commands that build the fs_config list
-//xxx along with a "commitperms", and any copy_dir etc. needs to see
-//    a commitperms before it will work
-
-    return 0;
-}
-
-
-/*
- * Function definitions
- */
-
-/* update_forced()
- *
- * Returns "true" if some system setting has determined that
- * the update should happen no matter what.
- */
-static int
-fn_update_forced(const char *name, void *cookie, int argc, const char *argv[],
-        char **result, size_t *resultLen,
-        PermissionRequestList *permissions)
-{
-    UNUSED(name);
-    UNUSED(cookie);
-    CHECK_FN();
-    NO_PERMS(permissions);
-
-    if (argc != 0) {
-        fprintf(stderr, "%s: wrong number of arguments (%d)\n",
-                name, argc);
-        return 1;
-    }
-
-    //xxx check some global or property
-    bool force = true;
-    if (force) {
-        *result = strdup("true");
-    } else {
-        *result = strdup("");
-    }
-    if (resultLen != NULL) {
-        *resultLen = strlen(*result);
-    }
-
-    return 0;
-}
-
-/* get_mark(<resource>)
- *
- * Returns the current mark associated with the provided resource.
- */
-static int
-fn_get_mark(const char *name, void *cookie, int argc, const char *argv[],
-        char **result, size_t *resultLen,
-        PermissionRequestList *permissions)
-{
-    UNUSED(name);
-    UNUSED(cookie);
-    CHECK_FN();
-    NO_PERMS(permissions);
-
-    if (argc != 1) {
-        fprintf(stderr, "%s: wrong number of arguments (%d)\n",
-                name, argc);
-        return 1;
-    }
-
-    //xxx look up the value
-    *result = strdup("");
-    if (resultLen != NULL) {
-        *resultLen = strlen(*result);
-    }
-
-    return 0;
-}
-
-/* hash_dir(<path-to-directory>)
- */
-static int
-fn_hash_dir(const char *name, void *cookie, int argc, const char *argv[],
-        char **result, size_t *resultLen,
-        PermissionRequestList *permissions)
-{
-    int ret = -1;
-
-    UNUSED(name);
-    UNUSED(cookie);
-    CHECK_FN();
-
-    const char *dir;
-    if (argc != 1) {
-        fprintf(stderr, "%s: wrong number of arguments (%d)\n",
-                name, argc);
-        return 1;
-    } else {
-        dir = argv[0];
-    }
-
-    if (permissions != NULL) {
-        if (dir == NULL) {
-            /* The argument is the result of another function.
-             * Assume the worst case, where the function returns
-             * the root.
-             */
-            dir = "/";
-        }
-        ret = addPermissionRequestToList(permissions, dir, true, PERM_READ);
-    } else {
-//xxx build and return the string
-        *result = strdup("hashvalue");
-        if (resultLen != NULL) {
-            *resultLen = strlen(*result);
-        }
-        ret = 0;
-    }
-
-    return ret;
-}
-
-/* matches(<str>, <str1> [, <strN>...])
- * If <str> matches (strcmp) any of <str1>...<strN>, returns <str>,
- * otherwise returns "".
- *
- * E.g., assert matches(hash_dir("/path"), "hash1", "hash2")
- */
-static int
-fn_matches(const char *name, void *cookie, int argc, const char *argv[],
-        char **result, size_t *resultLen,
-        PermissionRequestList *permissions)
-{
-    UNUSED(name);
-    UNUSED(cookie);
-    CHECK_FN();
-    NO_PERMS(permissions);
-
-    if (argc < 2) {
-        fprintf(stderr, "%s: not enough arguments (%d < 2)\n",
-                name, argc);
-        return 1;
-    }
-
-    int i;
-    for (i = 1; i < argc; i++) {
-        if (strcmp(argv[0], argv[i]) == 0) {
-            *result = strdup(argv[0]);
-            if (resultLen != NULL) {
-                *resultLen = strlen(*result);
-            }
-            return 0;
-        }
-    }
-
-    *result = strdup("");
-    if (resultLen != NULL) {
-        *resultLen = 1;
-    }
-    return 0;
-}
-
-/* concat(<str>, <str1> [, <strN>...])
- * Returns the concatenation of all strings.
- */
-static int
-fn_concat(const char *name, void *cookie, int argc, const char *argv[],
-        char **result, size_t *resultLen,
-        PermissionRequestList *permissions)
-{
-    UNUSED(name);
-    UNUSED(cookie);
-    CHECK_FN();
-    NO_PERMS(permissions);
-
-    size_t totalLen = 0;
-    int i;
-    for (i = 0; i < argc; i++) {
-        totalLen += strlen(argv[i]);
-    }
-
-    char *s = (char *)malloc(totalLen + 1);
-    if (s == NULL) {
-        return -1;
-    }
-    s[totalLen] = '\0';
-    for (i = 0; i < argc; i++) {
-        //TODO: keep track of the end to avoid walking the string each time
-        strcat(s, argv[i]);
-    }
-    *result = s;
-    if (resultLen != NULL) {
-        *resultLen = strlen(s);
-    }
-
-    return 0;
-}
-
-int
-registerUpdateFunctions()
-{
-    int ret;
-
-    ret = registerFunction("update_forced", fn_update_forced, NULL);
-    if (ret < 0) return ret;
-
-    ret = registerFunction("get_mark", fn_get_mark, NULL);
-    if (ret < 0) return ret;
-
-    ret = registerFunction("hash_dir", fn_hash_dir, NULL);
-    if (ret < 0) return ret;
-
-    ret = registerFunction("matches", fn_matches, NULL);
-    if (ret < 0) return ret;
-
-    ret = registerFunction("concat", fn_concat, NULL);
-    if (ret < 0) return ret;
-
-    return 0;
-}
diff --git a/amend/register.h b/amend/register.h
deleted file mode 100644
index 1d9eacb..0000000
--- a/amend/register.h
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (C) 2007 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 AMEND_REGISTER_H_
-#define AMEND_REGISTER_H_
-
-int registerUpdateCommands(void);
-int registerUpdateFunctions(void);
-
-#endif  // AMEND_REGISTER_H_
diff --git a/amend/symtab.c b/amend/symtab.c
deleted file mode 100644
index 835d2fc..0000000
--- a/amend/symtab.c
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright (C) 2007 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 <stdlib.h>
-#include <string.h>
-#include "symtab.h"
-
-#define DEFAULT_TABLE_SIZE 16
-
-typedef struct {
-    char *symbol;
-    const void *cookie;
-    unsigned int flags;
-} SymbolTableEntry;
-
-struct SymbolTable {
-    SymbolTableEntry *table;
-    int numEntries;
-    int maxSize;
-};
-
-SymbolTable *
-createSymbolTable()
-{
-    SymbolTable *tab;
-
-    tab = (SymbolTable *)malloc(sizeof(SymbolTable));
-    if (tab != NULL) {
-        tab->numEntries = 0;
-        tab->maxSize = DEFAULT_TABLE_SIZE;
-        tab->table = (SymbolTableEntry *)malloc(
-                            tab->maxSize * sizeof(SymbolTableEntry));
-        if (tab->table == NULL) {
-            free(tab);
-            tab = NULL;
-        }
-    }
-    return tab;
-}
-
-void
-deleteSymbolTable(SymbolTable *tab)
-{
-    if (tab != NULL) {
-        while (tab->numEntries > 0) {
-            free(tab->table[--tab->numEntries].symbol);
-        }
-        free(tab->table);
-    }
-}
-
-void *
-findInSymbolTable(SymbolTable *tab, const char *symbol, unsigned int flags)
-{
-    int i;
-
-    if (tab == NULL || symbol == NULL) {
-        return NULL;
-    }
-
-    // TODO: Sort the table and binary search
-    for (i = 0; i < tab->numEntries; i++) {
-        if (strcmp(tab->table[i].symbol, symbol) == 0 &&
-                tab->table[i].flags == flags)
-        {
-            return (void *)tab->table[i].cookie;
-        }
-    }
-
-    return NULL;
-}
-
-int
-addToSymbolTable(SymbolTable *tab, const char *symbol, unsigned int flags,
-        const void *cookie)
-{
-    if (tab == NULL || symbol == NULL || cookie == NULL) {
-        return -1;
-    }
-
-    /* Make sure that this symbol isn't already in the table.
-     */
-    if (findInSymbolTable(tab, symbol, flags) != NULL) {
-        return -2;
-    }
-
-    /* Make sure there's enough space for the new entry.
-     */
-    if (tab->numEntries == tab->maxSize) {
-        SymbolTableEntry *newTable;
-        int newSize;
-
-        newSize = tab->numEntries * 2;
-        if (newSize < DEFAULT_TABLE_SIZE) {
-            newSize = DEFAULT_TABLE_SIZE;
-        }
-        newTable = (SymbolTableEntry *)realloc(tab->table,
-                            newSize * sizeof(SymbolTableEntry));
-        if (newTable == NULL) {
-            return -1;
-        }
-        tab->maxSize = newSize;
-        tab->table = newTable;
-    }
-
-    /* Insert the new entry.
-     */
-    symbol = strdup(symbol);
-    if (symbol == NULL) {
-        return -1;
-    }
-    // TODO: Sort the table
-    tab->table[tab->numEntries].symbol = (char *)symbol;
-    tab->table[tab->numEntries].cookie = cookie;
-    tab->table[tab->numEntries].flags = flags;
-    tab->numEntries++;
-
-    return 0;
-}
diff --git a/amend/symtab.h b/amend/symtab.h
deleted file mode 100644
index f83c65b..0000000
--- a/amend/symtab.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2007 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 AMEND_SYMTAB_H_
-#define AMEND_SYMTAB_H_
-
-typedef struct SymbolTable SymbolTable;
-
-SymbolTable *createSymbolTable(void);
-
-void deleteSymbolTable(SymbolTable *tab);
-
-/* symbol and cookie must be non-NULL.
- */
-int addToSymbolTable(SymbolTable *tab, const char *symbol, unsigned int flags,
-        const void *cookie);
-
-void *findInSymbolTable(SymbolTable *tab, const char *symbol,
-        unsigned int flags);
-
-#endif  // AMEND_SYMTAB_H_
diff --git a/amend/test_commands.c b/amend/test_commands.c
deleted file mode 100644
index be938ac..0000000
--- a/amend/test_commands.c
+++ /dev/null
@@ -1,538 +0,0 @@
-/*
- * Copyright (C) 2007 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 <stdlib.h>
-#include <string.h>
-#include <stdio.h>
-#undef NDEBUG
-#include <assert.h>
-#include "commands.h"
-
-static struct {
-    bool called;
-    const char *name;
-    void *cookie;
-    int argc;
-    const char **argv;
-    PermissionRequestList *permissions;
-    int returnValue;
-    char *functionResult;
-} gTestCommandState;
-
-static int
-testCommand(const char *name, void *cookie, int argc, const char *argv[],
-        PermissionRequestList *permissions)
-{
-    gTestCommandState.called = true;
-    gTestCommandState.name = name;
-    gTestCommandState.cookie = cookie;
-    gTestCommandState.argc = argc;
-    gTestCommandState.argv = argv;
-    gTestCommandState.permissions = permissions;
-    return gTestCommandState.returnValue;
-}
-
-static int
-testFunction(const char *name, void *cookie, int argc, const char *argv[],
-        char **result, size_t *resultLen, PermissionRequestList *permissions)
-{
-    gTestCommandState.called = true;
-    gTestCommandState.name = name;
-    gTestCommandState.cookie = cookie;
-    gTestCommandState.argc = argc;
-    gTestCommandState.argv = argv;
-    gTestCommandState.permissions = permissions;
-    if (result != NULL) {
-        *result = gTestCommandState.functionResult;
-        if (resultLen != NULL) {
-            *resultLen = strlen(*result);
-        }
-    }
-    return gTestCommandState.returnValue;
-}
-
-static int
-test_commands()
-{
-    Command *cmd;
-    int ret;
-    CommandArgumentType argType;
-
-    ret = commandInit();
-    assert(ret == 0);
-
-    /* Make sure we can't initialize twice.
-     */
-    ret = commandInit();
-    assert(ret < 0);
-
-    /* Try calling with some bad values.
-     */
-    ret = registerCommand(NULL, CMD_ARGS_UNKNOWN, NULL, NULL);
-    assert(ret < 0);
-
-    ret = registerCommand("hello", CMD_ARGS_UNKNOWN, NULL, NULL);
-    assert(ret < 0);
-
-    ret = registerCommand("hello", CMD_ARGS_WORDS, NULL, NULL);
-    assert(ret < 0);
-
-    cmd = findCommand(NULL);
-    assert(cmd == NULL);
-
-    argType = getCommandArgumentType(NULL);
-    assert((int)argType < 0);
-
-    ret = callCommand(NULL, -1, NULL);
-    assert(ret < 0);
-
-    ret = callBooleanCommand(NULL, false);
-    assert(ret < 0);
-
-    /* Register some commands.
-     */
-    ret = registerCommand("one", CMD_ARGS_WORDS, testCommand,
-            &gTestCommandState);
-    assert(ret == 0);
-
-    ret = registerCommand("two", CMD_ARGS_WORDS, testCommand,
-            &gTestCommandState);
-    assert(ret == 0);
-
-    ret = registerCommand("bool", CMD_ARGS_BOOLEAN, testCommand,
-            &gTestCommandState);
-    assert(ret == 0);
-
-    /* Make sure that all of those commands exist and that their
-     * argument types are correct.
-     */
-    cmd = findCommand("one");
-    assert(cmd != NULL);
-    argType = getCommandArgumentType(cmd);
-    assert(argType == CMD_ARGS_WORDS);
-
-    cmd = findCommand("two");
-    assert(cmd != NULL);
-    argType = getCommandArgumentType(cmd);
-    assert(argType == CMD_ARGS_WORDS);
-
-    cmd = findCommand("bool");
-    assert(cmd != NULL);
-    argType = getCommandArgumentType(cmd);
-    assert(argType == CMD_ARGS_BOOLEAN);
-
-    /* Make sure that no similar commands exist.
-     */
-    cmd = findCommand("on");
-    assert(cmd == NULL);
-
-    cmd = findCommand("onee");
-    assert(cmd == NULL);
-
-    /* Make sure that a double insertion fails.
-     */
-    ret = registerCommand("one", CMD_ARGS_WORDS, testCommand,
-            &gTestCommandState);
-    assert(ret < 0);
-
-    /* Make sure that bad args fail.
-     */
-    cmd = findCommand("one");
-    assert(cmd != NULL);
-
-    ret = callCommand(cmd, -1, NULL);   // argc must be non-negative
-    assert(ret < 0);
-
-    ret = callCommand(cmd, 1, NULL);    // argv can't be NULL if argc > 0
-    assert(ret < 0);
-
-    /* Make sure that you can't make a boolean call on a regular command.
-     */
-    cmd = findCommand("one");
-    assert(cmd != NULL);
-
-    ret = callBooleanCommand(cmd, false);
-    assert(ret < 0);
-
-    /* Make sure that you can't make a regular call on a boolean command.
-     */
-    cmd = findCommand("bool");
-    assert(cmd != NULL);
-
-    ret = callCommand(cmd, 0, NULL);
-    assert(ret < 0);
-
-    /* Set up some arguments.
-     */
-    int argc = 4;
-    const char *argv[4] = { "ONE", "TWO", "THREE", "FOUR" };
-
-    /* Make a call and make sure that it occurred.
-     */
-    cmd = findCommand("one");
-    assert(cmd != NULL);
-    memset(&gTestCommandState, 0, sizeof(gTestCommandState));
-    gTestCommandState.called = false;
-    gTestCommandState.returnValue = 25;
-    gTestCommandState.permissions = (PermissionRequestList *)1;
-    ret = callCommand(cmd, argc, argv);
-//xxx also try calling with a null argv element (should fail)
-    assert(ret == 25);
-    assert(gTestCommandState.called);
-    assert(strcmp(gTestCommandState.name, "one") == 0);
-    assert(gTestCommandState.cookie == &gTestCommandState);
-    assert(gTestCommandState.argc == argc);
-    assert(gTestCommandState.argv == argv);
-    assert(gTestCommandState.permissions == NULL);
-
-    /* Make a boolean call and make sure that it occurred.
-     */
-    cmd = findCommand("bool");
-    assert(cmd != NULL);
-
-    memset(&gTestCommandState, 0, sizeof(gTestCommandState));
-    gTestCommandState.called = false;
-    gTestCommandState.returnValue = 12;
-    gTestCommandState.permissions = (PermissionRequestList *)1;
-    ret = callBooleanCommand(cmd, false);
-    assert(ret == 12);
-    assert(gTestCommandState.called);
-    assert(strcmp(gTestCommandState.name, "bool") == 0);
-    assert(gTestCommandState.cookie == &gTestCommandState);
-    assert(gTestCommandState.argc == 0);
-    assert(gTestCommandState.argv == NULL);
-    assert(gTestCommandState.permissions == NULL);
-
-    memset(&gTestCommandState, 0, sizeof(gTestCommandState));
-    gTestCommandState.called = false;
-    gTestCommandState.returnValue = 13;
-    gTestCommandState.permissions = (PermissionRequestList *)1;
-    ret = callBooleanCommand(cmd, true);
-    assert(ret == 13);
-    assert(gTestCommandState.called);
-    assert(strcmp(gTestCommandState.name, "bool") == 0);
-    assert(gTestCommandState.cookie == &gTestCommandState);
-    assert(gTestCommandState.argc == 1);
-    assert(gTestCommandState.argv == NULL);
-    assert(gTestCommandState.permissions == NULL);
-
-    /* Try looking up permissions.
-     */
-    PermissionRequestList permissions;
-    cmd = findCommand("one");
-    assert(cmd != NULL);
-    memset(&gTestCommandState, 0, sizeof(gTestCommandState));
-    gTestCommandState.called = false;
-    gTestCommandState.returnValue = 27;
-    gTestCommandState.permissions = (PermissionRequestList *)1;
-    argv[1] = NULL; // null out an arg, which should be ok
-    ret = getCommandPermissions(cmd, argc, argv, &permissions);
-    assert(ret == 27);
-    assert(gTestCommandState.called);
-    assert(strcmp(gTestCommandState.name, "one") == 0);
-    assert(gTestCommandState.cookie == &gTestCommandState);
-    assert(gTestCommandState.argc == argc);
-    assert(gTestCommandState.argv == argv);
-    assert(gTestCommandState.permissions == &permissions);
-
-    /* Boolean command permissions
-     */
-    cmd = findCommand("bool");
-    assert(cmd != NULL);
-    memset(&gTestCommandState, 0, sizeof(gTestCommandState));
-    gTestCommandState.called = false;
-    gTestCommandState.returnValue = 55;
-    gTestCommandState.permissions = (PermissionRequestList *)1;
-    // argv[1] is still NULL
-    ret = getBooleanCommandPermissions(cmd, true, &permissions);
-    assert(ret == 55);
-    assert(gTestCommandState.called);
-    assert(strcmp(gTestCommandState.name, "bool") == 0);
-    assert(gTestCommandState.cookie == &gTestCommandState);
-    assert(gTestCommandState.argc == 1);
-    assert(gTestCommandState.argv == NULL);
-    assert(gTestCommandState.permissions == &permissions);
-
-
-    /* Smoke test commandCleanup().
-     */
-    commandCleanup();
-
-    return 0;
-}
-
-static int
-test_functions()
-{
-    Function *fn;
-    int ret;
-
-    ret = commandInit();
-    assert(ret == 0);
-
-    /* Try calling with some bad values.
-     */
-    ret = registerFunction(NULL, NULL, NULL);
-    assert(ret < 0);
-
-    ret = registerFunction("hello", NULL, NULL);
-    assert(ret < 0);
-
-    fn = findFunction(NULL);
-    assert(fn == NULL);
-
-    ret = callFunction(NULL, -1, NULL, NULL, NULL);
-    assert(ret < 0);
-
-    /* Register some functions.
-     */
-    ret = registerFunction("one", testFunction, &gTestCommandState);
-    assert(ret == 0);
-
-    ret = registerFunction("two", testFunction, &gTestCommandState);
-    assert(ret == 0);
-
-    ret = registerFunction("three", testFunction, &gTestCommandState);
-    assert(ret == 0);
-
-    /* Make sure that all of those functions exist.
-     * argument types are correct.
-     */
-    fn = findFunction("one");
-    assert(fn != NULL);
-
-    fn = findFunction("two");
-    assert(fn != NULL);
-
-    fn = findFunction("three");
-    assert(fn != NULL);
-
-    /* Make sure that no similar functions exist.
-     */
-    fn = findFunction("on");
-    assert(fn == NULL);
-
-    fn = findFunction("onee");
-    assert(fn == NULL);
-
-    /* Make sure that a double insertion fails.
-     */
-    ret = registerFunction("one", testFunction, &gTestCommandState);
-    assert(ret < 0);
-
-    /* Make sure that bad args fail.
-     */
-    fn = findFunction("one");
-    assert(fn != NULL);
-
-    // argc must be non-negative
-    ret = callFunction(fn, -1, NULL, (char **)1, NULL);
-    assert(ret < 0);
-
-    // argv can't be NULL if argc > 0
-    ret = callFunction(fn, 1, NULL, (char **)1, NULL);
-    assert(ret < 0);
-
-    // result can't be NULL
-    ret = callFunction(fn, 0, NULL, NULL, NULL);
-    assert(ret < 0);
-
-    /* Set up some arguments.
-     */
-    int argc = 4;
-    const char *argv[4] = { "ONE", "TWO", "THREE", "FOUR" };
-
-    /* Make a call and make sure that it occurred.
-     */
-    char *functionResult;
-    size_t functionResultLen;
-    fn = findFunction("one");
-    assert(fn != NULL);
-    memset(&gTestCommandState, 0, sizeof(gTestCommandState));
-    gTestCommandState.called = false;
-    gTestCommandState.returnValue = 25;
-    gTestCommandState.functionResult = "1234";
-    gTestCommandState.permissions = (PermissionRequestList *)1;
-    functionResult = NULL;
-    functionResultLen = 55;
-    ret = callFunction(fn, argc, argv,
-            &functionResult, &functionResultLen);
-//xxx also try calling with a null resultLen arg (should succeed)
-//xxx also try calling with a null argv element (should fail)
-    assert(ret == 25);
-    assert(gTestCommandState.called);
-    assert(strcmp(gTestCommandState.name, "one") == 0);
-    assert(gTestCommandState.cookie == &gTestCommandState);
-    assert(gTestCommandState.argc == argc);
-    assert(gTestCommandState.argv == argv);
-    assert(gTestCommandState.permissions == NULL);
-    assert(strcmp(functionResult, "1234") == 0);
-    assert(functionResultLen == strlen(functionResult));
-
-    /* Try looking up permissions.
-     */
-    PermissionRequestList permissions;
-    fn = findFunction("one");
-    assert(fn != NULL);
-    memset(&gTestCommandState, 0, sizeof(gTestCommandState));
-    gTestCommandState.called = false;
-    gTestCommandState.returnValue = 27;
-    gTestCommandState.permissions = (PermissionRequestList *)1;
-    argv[1] = NULL; // null out an arg, which should be ok
-    ret = getFunctionPermissions(fn, argc, argv, &permissions);
-    assert(ret == 27);
-    assert(gTestCommandState.called);
-    assert(strcmp(gTestCommandState.name, "one") == 0);
-    assert(gTestCommandState.cookie == &gTestCommandState);
-    assert(gTestCommandState.argc == argc);
-    assert(gTestCommandState.argv == argv);
-    assert(gTestCommandState.permissions == &permissions);
-
-    /* Smoke test commandCleanup().
-     */
-    commandCleanup();
-
-    return 0;
-}
-
-static int
-test_interaction()
-{
-    Command *cmd;
-    Function *fn;
-    int ret;
-
-    ret = commandInit();
-    assert(ret == 0);
-
-    /* Register some commands.
-     */
-    ret = registerCommand("one", CMD_ARGS_WORDS, testCommand, (void *)0xc1);
-    assert(ret == 0);
-
-    ret = registerCommand("two", CMD_ARGS_WORDS, testCommand, (void *)0xc2);
-    assert(ret == 0);
-
-    /* Register some functions, one of which shares a name with a command.
-     */
-    ret = registerFunction("one", testFunction, (void *)0xf1);
-    assert(ret == 0);
-
-    ret = registerFunction("three", testFunction, (void *)0xf3);
-    assert(ret == 0);
-
-    /* Look up each of the commands, and make sure no command exists
-     * with the name used only by our function.
-     */
-    cmd = findCommand("one");
-    assert(cmd != NULL);
-
-    cmd = findCommand("two");
-    assert(cmd != NULL);
-
-    cmd = findCommand("three");
-    assert(cmd == NULL);
-
-    /* Look up each of the functions, and make sure no function exists
-     * with the name used only by our command.
-     */
-    fn = findFunction("one");
-    assert(fn != NULL);
-
-    fn = findFunction("two");
-    assert(fn == NULL);
-
-    fn = findFunction("three");
-    assert(fn != NULL);
-
-    /* Set up some arguments.
-     */
-    int argc = 4;
-    const char *argv[4] = { "ONE", "TWO", "THREE", "FOUR" };
-
-    /* Call the overlapping command and make sure that the cookie is correct.
-     */
-    cmd = findCommand("one");
-    assert(cmd != NULL);
-    memset(&gTestCommandState, 0, sizeof(gTestCommandState));
-    gTestCommandState.called = false;
-    gTestCommandState.returnValue = 123;
-    gTestCommandState.permissions = (PermissionRequestList *)1;
-    ret = callCommand(cmd, argc, argv);
-    assert(ret == 123);
-    assert(gTestCommandState.called);
-    assert(strcmp(gTestCommandState.name, "one") == 0);
-    assert((int)gTestCommandState.cookie == 0xc1);
-    assert(gTestCommandState.argc == argc);
-    assert(gTestCommandState.argv == argv);
-    assert(gTestCommandState.permissions == NULL);
-
-    /* Call the overlapping function and make sure that the cookie is correct.
-     */
-    char *functionResult;
-    size_t functionResultLen;
-    fn = findFunction("one");
-    assert(fn != NULL);
-    memset(&gTestCommandState, 0, sizeof(gTestCommandState));
-    gTestCommandState.called = false;
-    gTestCommandState.returnValue = 125;
-    gTestCommandState.functionResult = "5678";
-    gTestCommandState.permissions = (PermissionRequestList *)2;
-    functionResult = NULL;
-    functionResultLen = 66;
-    ret = callFunction(fn, argc, argv, &functionResult, &functionResultLen);
-    assert(ret == 125);
-    assert(gTestCommandState.called);
-    assert(strcmp(gTestCommandState.name, "one") == 0);
-    assert((int)gTestCommandState.cookie == 0xf1);
-    assert(gTestCommandState.argc == argc);
-    assert(gTestCommandState.argv == argv);
-    assert(gTestCommandState.permissions == NULL);
-    assert(strcmp(functionResult, "5678") == 0);
-    assert(functionResultLen == strlen(functionResult));
-
-    /* Clean up.
-     */
-    commandCleanup();
-
-    return 0;
-}
-
-int
-test_cmd_fn()
-{
-    int ret;
-
-    ret = test_commands();
-    if (ret != 0) {
-        fprintf(stderr, "test_commands() failed: %d\n", ret);
-        return ret;
-    }
-
-    ret = test_functions();
-    if (ret != 0) {
-        fprintf(stderr, "test_functions() failed: %d\n", ret);
-        return ret;
-    }
-
-    ret = test_interaction();
-    if (ret != 0) {
-        fprintf(stderr, "test_interaction() failed: %d\n", ret);
-        return ret;
-    }
-
-    return 0;
-}
diff --git a/amend/test_permissions.c b/amend/test_permissions.c
deleted file mode 100644
index c389456..0000000
--- a/amend/test_permissions.c
+++ /dev/null
@@ -1,347 +0,0 @@
-/*
- * Copyright (C) 2007 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 <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#undef NDEBUG
-#include <assert.h>
-#include "permissions.h"
-
-static int
-test_permission_list()
-{
-    PermissionRequestList list;
-    int ret;
-    int numRequests;
-
-    /* Bad parameter
-     */
-    ret = initPermissionRequestList(NULL);
-    assert(ret < 0);
-
-    /* Good parameter
-     */
-    ret = initPermissionRequestList(&list);
-    assert(ret == 0);
-
-    /* Bad parameters
-     */
-    ret = addPermissionRequestToList(NULL, NULL, false, 0);
-    assert(ret < 0);
-
-    ret = addPermissionRequestToList(&list, NULL, false, 0);
-    assert(ret < 0);
-
-    /* Good parameters
-     */
-    numRequests = 0;
-
-    ret = addPermissionRequestToList(&list, "one", false, 1);
-    assert(ret == 0);
-    numRequests++;
-
-    ret = addPermissionRequestToList(&list, "two", false, 2);
-    assert(ret == 0);
-    numRequests++;
-
-    ret = addPermissionRequestToList(&list, "three", false, 3);
-    assert(ret == 0);
-    numRequests++;
-
-    ret = addPermissionRequestToList(&list, "recursive", true, 55);
-    assert(ret == 0);
-    numRequests++;
-
-    /* Validate the list
-     */
-    assert(list.requests != NULL);
-    assert(list.numRequests == numRequests);
-    assert(list.numRequests <= list.requestsAllocated);
-    bool sawOne = false;
-    bool sawTwo = false;
-    bool sawThree = false;
-    bool sawRecursive = false;
-    int i;
-    for (i = 0; i < list.numRequests; i++) {
-        PermissionRequest *req = &list.requests[i];
-        assert(req->allowed == 0);
-
-        /* Order isn't guaranteed, so we have to switch every time.
-         */
-        if (strcmp(req->path, "one") == 0) {
-            assert(!sawOne);
-            assert(req->requested == 1);
-            assert(!req->recursive);
-            sawOne = true;
-        } else if (strcmp(req->path, "two") == 0) {
-            assert(!sawTwo);
-            assert(req->requested == 2);
-            assert(!req->recursive);
-            sawTwo = true;
-        } else if (strcmp(req->path, "three") == 0) {
-            assert(!sawThree);
-            assert(req->requested == 3);
-            assert(!req->recursive);
-            sawThree = true;
-        } else if (strcmp(req->path, "recursive") == 0) {
-            assert(!sawRecursive);
-            assert(req->requested == 55);
-            assert(req->recursive);
-            sawRecursive = true;
-        } else {
-            assert(false);
-        }
-    }
-    assert(sawOne);
-    assert(sawTwo);
-    assert(sawThree);
-    assert(sawRecursive);
-
-    /* Smoke test the teardown
-     */
-    freePermissionRequestListElements(&list);
-
-    return 0;
-}
-
-static int
-test_permission_table()
-{
-    int ret;
-
-    /* Test the global permissions table.
-     * Try calling functions without initializing first.
-     */
-    ret = registerPermissionSet(0, NULL);
-    assert(ret < 0);
-
-    ret = countPermissionConflicts((PermissionRequestList *)16, false);
-    assert(ret < 0);
-
-    ret = getPermissionCount();
-    assert(ret < 0);
-
-    const Permission *p;
-    p = getPermissionAt(0);
-    assert(p == NULL);
-
-    /* Initialize.
-     */
-    ret = permissionInit();
-    assert(ret == 0);
-
-    /* Make sure we can't initialize twice.
-     */
-    ret = permissionInit();
-    assert(ret < 0);
-
-    /* Test the inspection functions.
-     */
-    ret = getPermissionCount();
-    assert(ret == 0);
-
-    p = getPermissionAt(-1);
-    assert(p == NULL);
-
-    p = getPermissionAt(0);
-    assert(p == NULL);
-
-    p = getPermissionAt(1);
-    assert(p == NULL);
-
-    /* Test registerPermissionSet().
-     * Try some bad parameter values.
-     */
-    ret = registerPermissionSet(-1, NULL);
-    assert(ret < 0);
-
-    ret = registerPermissionSet(1, NULL);
-    assert(ret < 0);
-
-    /* Register some permissions.
-     */
-    Permission p1;
-    p1.path = "one";
-    p1.allowed = 1;
-    ret = registerPermissionSet(1, &p1);
-    assert(ret == 0);
-    ret = getPermissionCount();
-    assert(ret == 1);
-
-    Permission p2[2];
-    p2[0].path = "two";
-    p2[0].allowed = 2;
-    p2[1].path = "three";
-    p2[1].allowed = 3;
-    ret = registerPermissionSet(2, p2);
-    assert(ret == 0);
-    ret = getPermissionCount();
-    assert(ret == 3);
-
-    ret = registerPermissionSet(0, NULL);
-    assert(ret == 0);
-    ret = getPermissionCount();
-    assert(ret == 3);
-
-    p1.path = "four";
-    p1.allowed = 4;
-    ret = registerPermissionSet(1, &p1);
-    assert(ret == 0);
-
-    /* Make sure the table looks correct.
-     * Order is important;  more-recent additions
-     * should appear at higher indices.
-     */
-    ret = getPermissionCount();
-    assert(ret == 4);
-
-    int i;
-    for (i = 0; i < ret; i++) {
-        const Permission *p;
-        p = getPermissionAt(i);
-        assert(p != NULL);
-        assert(p->allowed == (unsigned int)(i + 1));
-        switch (i) {
-        case 0:
-            assert(strcmp(p->path, "one") == 0);
-            break;
-        case 1:
-            assert(strcmp(p->path, "two") == 0);
-            break;
-        case 2:
-            assert(strcmp(p->path, "three") == 0);
-            break;
-        case 3:
-            assert(strcmp(p->path, "four") == 0);
-            break;
-        default:
-            assert(!"internal error");
-            break;
-        }
-    }
-    p = getPermissionAt(ret);
-    assert(p == NULL);
-
-    /* Smoke test the teardown
-     */
-    permissionCleanup();
-
-    return 0;
-}
-
-static int
-test_allowed_permissions()
-{
-    int ret;
-    int numPerms;
-
-    /* Make sure these fail before initialization.
-     */
-    ret = countPermissionConflicts((PermissionRequestList *)1, false);
-    assert(ret < 0);
-
-    ret = getAllowedPermissions((const char *)1, false, (unsigned int *)1);
-    assert(ret < 0);
-
-    /* Initialize.
-     */
-    ret = permissionInit();
-    assert(ret == 0);
-
-    /* Make sure countPermissionConflicts() fails with bad parameters.
-     */
-    ret = countPermissionConflicts(NULL, false);
-    assert(ret < 0);
-
-    /* Register a set of permissions.
-     */
-    Permission perms[] = {
-        { "/", PERM_NONE },
-        { "/stat", PERM_STAT },
-        { "/read", PERMSET_READ },
-        { "/write", PERMSET_WRITE },
-        { "/.stat", PERM_STAT },
-        { "/.stat/.read", PERMSET_READ },
-        { "/.stat/.read/.write", PERMSET_WRITE },
-        { "/.stat/.write", PERMSET_WRITE },
-    };
-    numPerms = sizeof(perms) / sizeof(perms[0]);
-    ret = registerPermissionSet(numPerms, perms);
-    assert(ret == 0);
-
-    /* Build a permission request list.
-     */
-    PermissionRequestList list;
-    ret = initPermissionRequestList(&list);
-    assert(ret == 0);
-
-    ret = addPermissionRequestToList(&list, "/stat", false, PERM_STAT);
-    assert(ret == 0);
-
-    ret = addPermissionRequestToList(&list, "/read", false, PERM_READ);
-    assert(ret == 0);
-
-    ret = addPermissionRequestToList(&list, "/write", false, PERM_WRITE);
-    assert(ret == 0);
-
-    //TODO: cover more cases once the permission stuff has been implemented
-
-    /* All of the requests in the list should be allowed.
-     */
-    ret = countPermissionConflicts(&list, false);
-    assert(ret == 0);
-
-    /* Add a request that will be denied.
-     */
-    ret = addPermissionRequestToList(&list, "/stat", false, 1<<31 | PERM_STAT);
-    assert(ret == 0);
-
-    ret = countPermissionConflicts(&list, false);
-    assert(ret == 1);
-
-    //TODO: more tests
-
-    permissionCleanup();
-
-    return 0;
-}
-
-int
-test_permissions()
-{
-    int ret;
-
-    ret = test_permission_list();
-    if (ret != 0) {
-        fprintf(stderr, "test_permission_list() failed: %d\n", ret);
-        return ret;
-    }
-
-    ret = test_permission_table();
-    if (ret != 0) {
-        fprintf(stderr, "test_permission_table() failed: %d\n", ret);
-        return ret;
-    }
-
-    ret = test_allowed_permissions();
-    if (ret != 0) {
-        fprintf(stderr, "test_permission_table() failed: %d\n", ret);
-        return ret;
-    }
-
-    return 0;
-}
diff --git a/amend/test_symtab.c b/amend/test_symtab.c
deleted file mode 100644
index 017d18c..0000000
--- a/amend/test_symtab.c
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * Copyright (C) 2007 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 <stdlib.h>
-#undef NDEBUG
-#include <assert.h>
-#include "symtab.h"
-
-int
-test_symtab()
-{
-    SymbolTable *tab;
-    void *cookie;
-    int ret;
-
-    /* Test creation */
-    tab = createSymbolTable();
-    assert(tab != NULL);
-
-    /* Smoke-test deletion */
-    deleteSymbolTable(tab);
-
-
-    tab = createSymbolTable();
-    assert(tab != NULL);
-
-
-    /* table parameter must be non-NULL. */
-    ret = addToSymbolTable(NULL, NULL, 0, NULL);
-    assert(ret < 0);
-
-    /* symbol parameter must be non-NULL. */
-    ret = addToSymbolTable(tab, NULL, 0, NULL);
-    assert(ret < 0);
-    
-    /* cookie parameter must be non-NULL. */
-    ret = addToSymbolTable(tab, "null", 0, NULL);
-    assert(ret < 0);
-
-
-    /* table parameter must be non-NULL. */
-    cookie = findInSymbolTable(NULL, NULL, 0);
-    assert(cookie == NULL);
-
-    /* symbol parameter must be non-NULL. */
-    cookie = findInSymbolTable(tab, NULL, 0);
-    assert(cookie == NULL);
-
-
-    /* Try some actual inserts.
-     */
-    ret = addToSymbolTable(tab, "one", 0, (void *)1);
-    assert(ret == 0);
-
-    ret = addToSymbolTable(tab, "two", 0, (void *)2);
-    assert(ret == 0);
-
-    ret = addToSymbolTable(tab, "three", 0, (void *)3);
-    assert(ret == 0);
-
-    /* Try some lookups.
-     */
-    cookie = findInSymbolTable(tab, "one", 0);
-    assert((int)cookie == 1);
-
-    cookie = findInSymbolTable(tab, "two", 0);
-    assert((int)cookie == 2);
-
-    cookie = findInSymbolTable(tab, "three", 0);
-    assert((int)cookie == 3);
-
-    /* Try to insert something that's already there.
-     */
-    ret = addToSymbolTable(tab, "one", 0, (void *)1111);
-    assert(ret < 0);
-
-    /* Make sure that the failed duplicate insert didn't
-     * clobber the original cookie value.
-     */
-    cookie = findInSymbolTable(tab, "one", 0);
-    assert((int)cookie == 1);
-
-    /* Try looking up something that isn't there.
-     */
-    cookie = findInSymbolTable(tab, "FOUR", 0);
-    assert(cookie == NULL);
-
-    /* Try looking up something that's similar to an existing entry.
-     */
-    cookie = findInSymbolTable(tab, "on", 0);
-    assert(cookie == NULL);
-
-    cookie = findInSymbolTable(tab, "onee", 0);
-    assert(cookie == NULL);
-
-    /* Test flags.
-     * Try inserting something with a different flag.
-     */
-    ret = addToSymbolTable(tab, "ten", 333, (void *)10);
-    assert(ret == 0);
-
-    /* Make sure it's there.
-     */
-    cookie = findInSymbolTable(tab, "ten", 333);
-    assert((int)cookie == 10);
-
-    /* Make sure it's not there when looked up with a different flag.
-     */
-    cookie = findInSymbolTable(tab, "ten", 0);
-    assert(cookie == NULL);
-
-    /* Try inserting something that has the same name as something
-     * with a different flag.
-     */
-    ret = addToSymbolTable(tab, "one", 333, (void *)11);
-    assert(ret == 0);
-
-    /* Make sure the new entry exists.
-     */
-    cookie = findInSymbolTable(tab, "one", 333);
-    assert((int)cookie == 11);
-
-    /* Make sure the old entry still has the right value.
-     */
-    cookie = findInSymbolTable(tab, "one", 0);
-    assert((int)cookie == 1);
-
-    /* Try deleting again, now that there's stuff in the table.
-     */
-    deleteSymbolTable(tab);
-
-    return 0;
-}
diff --git a/amend/tests/001-nop/expected.txt b/amend/tests/001-nop/expected.txt
deleted file mode 100644
index d4a85ce..0000000
--- a/amend/tests/001-nop/expected.txt
+++ /dev/null
@@ -1 +0,0 @@
-I am a jelly donut.
diff --git a/amend/tests/001-nop/info.txt b/amend/tests/001-nop/info.txt
deleted file mode 100644
index 9942f10..0000000
--- a/amend/tests/001-nop/info.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-This is a sample no-op test, which does at least serve to verify that the
-test harness is working.
diff --git a/amend/tests/001-nop/run b/amend/tests/001-nop/run
deleted file mode 100644
index 51637c1..0000000
--- a/amend/tests/001-nop/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2007 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.
-
-echo 'I am a jelly donut.'
diff --git a/amend/tests/002-lex-empty/SKIP b/amend/tests/002-lex-empty/SKIP
deleted file mode 100644
index e69de29..0000000
--- a/amend/tests/002-lex-empty/SKIP
+++ /dev/null
diff --git a/amend/tests/002-lex-empty/expected.txt b/amend/tests/002-lex-empty/expected.txt
deleted file mode 100644
index 822a54c..0000000
--- a/amend/tests/002-lex-empty/expected.txt
+++ /dev/null
@@ -1 +0,0 @@
- EOF
diff --git a/amend/tests/002-lex-empty/info.txt b/amend/tests/002-lex-empty/info.txt
deleted file mode 100644
index 090083f..0000000
--- a/amend/tests/002-lex-empty/info.txt
+++ /dev/null
@@ -1 +0,0 @@
-Test to make sure that an empty file is accepted properly.
diff --git a/amend/tests/002-lex-empty/input b/amend/tests/002-lex-empty/input
deleted file mode 100644
index e69de29..0000000
--- a/amend/tests/002-lex-empty/input
+++ /dev/null
diff --git a/amend/tests/002-lex-empty/run b/amend/tests/002-lex-empty/run
deleted file mode 100644
index 35c4a4f..0000000
--- a/amend/tests/002-lex-empty/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2007 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.
-
-amend --debug-lex input
diff --git a/amend/tests/003-lex-command/expected.txt b/amend/tests/003-lex-command/expected.txt
deleted file mode 100644
index e40db0c..0000000
--- a/amend/tests/003-lex-command/expected.txt
+++ /dev/null
@@ -1,13 +0,0 @@
- IDENTIFIER<this_identifier_is_not_assert> EOL
- IDENTIFIER<NEITHER_IS_THIS_123> EOL
- IDENTIFIER<but_the_next_one_is> EOL
- IDENTIFIER<assert> EOL
- IDENTIFIER<next_one_is_not_an_identifier> EOL
-line 6: unexpected character at '1'
- EOF
-line 1: unexpected character at '"'
- EOF
-line 1: unexpected character at '='
- EOF
-line 1: unexpected character at '9'
- EOF
diff --git a/amend/tests/003-lex-command/info.txt b/amend/tests/003-lex-command/info.txt
deleted file mode 100644
index 9296648..0000000
--- a/amend/tests/003-lex-command/info.txt
+++ /dev/null
@@ -1 +0,0 @@
-Test to make sure that simple command names are tokenized properly.
diff --git a/amend/tests/003-lex-command/input b/amend/tests/003-lex-command/input
deleted file mode 100644
index b9ef231..0000000
--- a/amend/tests/003-lex-command/input
+++ /dev/null
@@ -1,6 +0,0 @@
-this_identifier_is_not_assert
-NEITHER_IS_THIS_123
-but_the_next_one_is
-assert
-next_one_is_not_an_identifier
-12not_an_identifier
diff --git a/amend/tests/003-lex-command/input2 b/amend/tests/003-lex-command/input2
deleted file mode 100644
index eb5daf7..0000000
--- a/amend/tests/003-lex-command/input2
+++ /dev/null
@@ -1 +0,0 @@
-"quoted"
diff --git a/amend/tests/003-lex-command/input3 b/amend/tests/003-lex-command/input3
deleted file mode 100644
index f1c8738..0000000
--- a/amend/tests/003-lex-command/input3
+++ /dev/null
@@ -1 +0,0 @@
-==
diff --git a/amend/tests/003-lex-command/input4 b/amend/tests/003-lex-command/input4
deleted file mode 100644
index 3ad5abd..0000000
--- a/amend/tests/003-lex-command/input4
+++ /dev/null
@@ -1 +0,0 @@
-99
diff --git a/amend/tests/003-lex-command/run b/amend/tests/003-lex-command/run
deleted file mode 100644
index 2e21fab..0000000
--- a/amend/tests/003-lex-command/run
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2007 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.
-
-amend --debug-lex input
-amend --debug-lex input2
-amend --debug-lex input3
-amend --debug-lex input4
diff --git a/amend/tests/004-lex-comment/expected.txt b/amend/tests/004-lex-comment/expected.txt
deleted file mode 100644
index a728a5e..0000000
--- a/amend/tests/004-lex-comment/expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
- IDENTIFIER<comment_on_this_line> EOL
- IDENTIFIER<none_on_this_one> EOL
- EOL
- EOL
- EOF
diff --git a/amend/tests/004-lex-comment/info.txt b/amend/tests/004-lex-comment/info.txt
deleted file mode 100644
index 0691248..0000000
--- a/amend/tests/004-lex-comment/info.txt
+++ /dev/null
@@ -1 +0,0 @@
-Test to make sure that comments are stripped out.
diff --git a/amend/tests/004-lex-comment/input b/amend/tests/004-lex-comment/input
deleted file mode 100644
index 6736c95..0000000
--- a/amend/tests/004-lex-comment/input
+++ /dev/null
@@ -1,4 +0,0 @@
-comment_on_this_line # this is a "comment" (with / a bunch) # \\ of stuff \
-none_on_this_one
-# beginning of line
-                         # preceded by whitespace
diff --git a/amend/tests/004-lex-comment/run b/amend/tests/004-lex-comment/run
deleted file mode 100644
index 35c4a4f..0000000
--- a/amend/tests/004-lex-comment/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2007 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.
-
-amend --debug-lex input
diff --git a/amend/tests/005-lex-quoted-string/expected.txt b/amend/tests/005-lex-quoted-string/expected.txt
deleted file mode 100644
index 9bb5ac4..0000000
--- a/amend/tests/005-lex-quoted-string/expected.txt
+++ /dev/null
@@ -1,13 +0,0 @@
- IDENTIFIER<test> WORD<string> EOL
- IDENTIFIER<test> WORD<string with spaces> EOL
- IDENTIFIER<test> WORD<string with "escaped" quotes> EOL
- IDENTIFIER<test> WORD<string with \escaped\ backslashes> EOL
- IDENTIFIER<test> WORD<string with # a comment character> EOL
- EOF
- EOL
- IDENTIFIER<test1>line 2: unterminated string at '
-'
- ??? <0>
- EOL
- IDENTIFIER<test1>line 2: illegal escape at '\n'
- ??? <0>
diff --git a/amend/tests/005-lex-quoted-string/info.txt b/amend/tests/005-lex-quoted-string/info.txt
deleted file mode 100644
index be458bd..0000000
--- a/amend/tests/005-lex-quoted-string/info.txt
+++ /dev/null
@@ -1 +0,0 @@
-Test to make sure that quoted strings are tokenized properly.
diff --git a/amend/tests/005-lex-quoted-string/input b/amend/tests/005-lex-quoted-string/input
deleted file mode 100644
index 2b34bbc..0000000
--- a/amend/tests/005-lex-quoted-string/input
+++ /dev/null
@@ -1,5 +0,0 @@
-test "string"
-test "string with spaces"
-test "string with \"escaped\" quotes"
-test "string with \\escaped\\ backslashes"
-test "string with # a comment character"
diff --git a/amend/tests/005-lex-quoted-string/input2 b/amend/tests/005-lex-quoted-string/input2
deleted file mode 100644
index 09e6689..0000000
--- a/amend/tests/005-lex-quoted-string/input2
+++ /dev/null
@@ -1,2 +0,0 @@
-# This should fail
-test1 "unterminated string
diff --git a/amend/tests/005-lex-quoted-string/input3 b/amend/tests/005-lex-quoted-string/input3
deleted file mode 100644
index 02f3f85..0000000
--- a/amend/tests/005-lex-quoted-string/input3
+++ /dev/null
@@ -1,2 +0,0 @@
-# This should fail
-test1 "string with illegal escape \n in the middle"
diff --git a/amend/tests/005-lex-quoted-string/run b/amend/tests/005-lex-quoted-string/run
deleted file mode 100644
index 7b1292a..0000000
--- a/amend/tests/005-lex-quoted-string/run
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2007 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.
-
-amend --debug-lex input
-amend --debug-lex input2
-amend --debug-lex input3
diff --git a/amend/tests/006-lex-words/SKIP b/amend/tests/006-lex-words/SKIP
deleted file mode 100644
index e69de29..0000000
--- a/amend/tests/006-lex-words/SKIP
+++ /dev/null
diff --git a/amend/tests/006-lex-words/expected.txt b/amend/tests/006-lex-words/expected.txt
deleted file mode 100644
index a78a0b1..0000000
--- a/amend/tests/006-lex-words/expected.txt
+++ /dev/null
@@ -1,6 +0,0 @@
- IDENTIFIER<test> WORD<this> WORD<has> WORD<a> WORD<bunch> WORD<of> WORD<BARE> WORD<ALPHA> WORD<WORDS> EOL
- IDENTIFIER<test> WORD<12> WORD<this> WORD<has(some> WORD<)> WORD<ALPHANUMER1C> WORD<and> WORD<\\> WORD<whatever> WORD<characters> EOL
- IDENTIFIER<test> WORD<this> WORD<has> WORD<mixed> WORD<bare> WORD<and quoted> WORD<words> EOL
- IDENTIFIER<test> WORD<what> WORD<about> WORD<quotesin the middle?> EOL
- IDENTIFIER<test> WORD<"""shouldn't> WORD<be> WORD<a> WORD<quoted> WORD<string> EOL
- EOF
diff --git a/amend/tests/006-lex-words/info.txt b/amend/tests/006-lex-words/info.txt
deleted file mode 100644
index dd37016..0000000
--- a/amend/tests/006-lex-words/info.txt
+++ /dev/null
@@ -1 +0,0 @@
-Test to make sure that argument words are tokenized properly.
diff --git a/amend/tests/006-lex-words/input b/amend/tests/006-lex-words/input
deleted file mode 100644
index a4de638..0000000
--- a/amend/tests/006-lex-words/input
+++ /dev/null
@@ -1,5 +0,0 @@
-test this has a bunch of BARE ALPHA WORDS
-test 12 this has(some ) ALPHANUMER1C and \\ whatever characters
-test this has mixed bare "and quoted" words
-test what about quotes"in the middle?"
-test \"\"\"shouldn't be a quoted string
diff --git a/amend/tests/006-lex-words/input2 b/amend/tests/006-lex-words/input2
deleted file mode 100644
index 09e6689..0000000
--- a/amend/tests/006-lex-words/input2
+++ /dev/null
@@ -1,2 +0,0 @@
-# This should fail
-test1 "unterminated string
diff --git a/amend/tests/006-lex-words/input3 b/amend/tests/006-lex-words/input3
deleted file mode 100644
index 02f3f85..0000000
--- a/amend/tests/006-lex-words/input3
+++ /dev/null
@@ -1,2 +0,0 @@
-# This should fail
-test1 "string with illegal escape \n in the middle"
diff --git a/amend/tests/006-lex-words/run b/amend/tests/006-lex-words/run
deleted file mode 100644
index 35c4a4f..0000000
--- a/amend/tests/006-lex-words/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2007 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.
-
-amend --debug-lex input
diff --git a/amend/tests/007-lex-real-script/expected.txt b/amend/tests/007-lex-real-script/expected.txt
deleted file mode 100644
index 012f62c..0000000
--- a/amend/tests/007-lex-real-script/expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
- IDENTIFIER<assert> IDENTIFIER<hash_dir> ( STRING<SYS:> ) == STRING<112345oldhashvalue1234123> EOL
- IDENTIFIER<mark> WORD<SYS:> WORD<dirty> EOL
- IDENTIFIER<copy_dir> WORD<PKG:android-files> WORD<SYS:> EOL
- IDENTIFIER<assert> IDENTIFIER<hash_dir> ( STRING<SYS:> ) == STRING<667890newhashvalue6678909> EOL
- IDENTIFIER<mark> WORD<SYS:> WORD<clean> EOL
- IDENTIFIER<done> EOL
- IDENTIFIER<assert> IDENTIFIER<hash_dir> ( STRING<SYS:> , STRING<blah> ) == STRING<112345oldhashvalue1234123> EOL
- IDENTIFIER<assert> STRING<true> == STRING<false> EOL
- IDENTIFIER<assert> IDENTIFIER<one> ( STRING<abc> , IDENTIFIER<two> ( STRING<def> ) ) == STRING<five> EOL
- IDENTIFIER<assert> IDENTIFIER<hash_dir> ( STRING<SYS:> ) == STRING<667890newhashvalue6678909> || IDENTIFIER<hash_dir> ( STRING<SYS:> ) == STRING<667890newhashvalue6678909> EOL
- EOF
diff --git a/amend/tests/007-lex-real-script/info.txt b/amend/tests/007-lex-real-script/info.txt
deleted file mode 100644
index 5e321f5..0000000
--- a/amend/tests/007-lex-real-script/info.txt
+++ /dev/null
@@ -1 +0,0 @@
-An input script similar to one that will actually be used in practice.
diff --git a/amend/tests/007-lex-real-script/input b/amend/tests/007-lex-real-script/input
deleted file mode 100644
index f3f1fd9..0000000
--- a/amend/tests/007-lex-real-script/input
+++ /dev/null
@@ -1,10 +0,0 @@
-assert hash_dir("SYS:") == "112345oldhashvalue1234123"
-mark SYS: dirty
-copy_dir "PKG:android-files" SYS:
-assert hash_dir("SYS:") == "667890newhashvalue6678909"
-mark SYS: clean
-done
-assert hash_dir("SYS:", "blah") == "112345oldhashvalue1234123"
-assert "true" == "false"
-assert one("abc", two("def")) == "five"
-assert hash_dir("SYS:") == "667890newhashvalue6678909" || hash_dir("SYS:") == "667890newhashvalue6678909"
diff --git a/amend/tests/007-lex-real-script/run b/amend/tests/007-lex-real-script/run
deleted file mode 100644
index 35c4a4f..0000000
--- a/amend/tests/007-lex-real-script/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2007 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.
-
-amend --debug-lex input
diff --git a/amend/tests/008-parse-real-script/expected.txt b/amend/tests/008-parse-real-script/expected.txt
deleted file mode 100644
index dabf6d4..0000000
--- a/amend/tests/008-parse-real-script/expected.txt
+++ /dev/null
@@ -1,74 +0,0 @@
-command "assert" {
-    STRING EQ {
-        FUNCTION hash_dir (
-            "SYS:"
-        )
-        "112345oldhashvalue1234123"
-    }
-}
-command "mark" {
-    "SYS:"
-    "dirty"
-}
-command "copy_dir" {
-    "PKG:android-files"
-    "SYS:"
-}
-command "assert" {
-    STRING EQ {
-        FUNCTION hash_dir (
-            "SYS:"
-        )
-        "667890newhashvalue6678909"
-    }
-}
-command "mark" {
-    "SYS:"
-    "clean"
-}
-command "done" {
-}
-command "assert" {
-    STRING EQ {
-        FUNCTION hash_dir (
-            "SYS:"
-            "blah"
-        )
-        "112345oldhashvalue1234123"
-    }
-}
-command "assert" {
-    STRING EQ {
-        "true"
-        "false"
-    }
-}
-command "assert" {
-    STRING NE {
-        FUNCTION matches (
-            FUNCTION hash_dir (
-                "SYS:"
-            )
-            "667890newhashvalue6678909"
-            "999999newhashvalue6678909"
-        )
-        ""
-    }
-}
-command "assert" {
-    BOOLEAN OR {
-        STRING EQ {
-            FUNCTION hash_dir (
-                "SYS:"
-            )
-            "667890newhashvalue6678909"
-        }
-        STRING EQ {
-            FUNCTION hash_dir (
-                "SYS:"
-            )
-            "999999newhashvalue6678909"
-        }
-    }
-}
-amend: Parse successful.
diff --git a/amend/tests/008-parse-real-script/info.txt b/amend/tests/008-parse-real-script/info.txt
deleted file mode 100644
index 5e321f5..0000000
--- a/amend/tests/008-parse-real-script/info.txt
+++ /dev/null
@@ -1 +0,0 @@
-An input script similar to one that will actually be used in practice.
diff --git a/amend/tests/008-parse-real-script/input b/amend/tests/008-parse-real-script/input
deleted file mode 100644
index b073306..0000000
--- a/amend/tests/008-parse-real-script/input
+++ /dev/null
@@ -1,10 +0,0 @@
-assert hash_dir("SYS:") == "112345oldhashvalue1234123"
-mark SYS: dirty
-copy_dir "PKG:android-files" SYS:
-assert hash_dir("SYS:") == "667890newhashvalue6678909"
-mark SYS: clean
-done
-assert hash_dir("SYS:", "blah") == "112345oldhashvalue1234123"
-assert "true" == "false"
-assert matches(hash_dir("SYS:"), "667890newhashvalue6678909", "999999newhashvalue6678909") != ""
-assert hash_dir("SYS:") == "667890newhashvalue6678909" || hash_dir("SYS:") == "999999newhashvalue6678909"
diff --git a/amend/tests/008-parse-real-script/run b/amend/tests/008-parse-real-script/run
deleted file mode 100644
index 9544e1b..0000000
--- a/amend/tests/008-parse-real-script/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2007 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.
-
-amend --debug-ast input
diff --git a/amend/tests/XXX-long-token/SKIP b/amend/tests/XXX-long-token/SKIP
deleted file mode 100644
index e69de29..0000000
--- a/amend/tests/XXX-long-token/SKIP
+++ /dev/null
diff --git a/amend/tests/XXX-stack-overflow/SKIP b/amend/tests/XXX-stack-overflow/SKIP
deleted file mode 100644
index e69de29..0000000
--- a/amend/tests/XXX-stack-overflow/SKIP
+++ /dev/null
diff --git a/amend/tests/one-test b/amend/tests/one-test
deleted file mode 100755
index 9cebd3f..0000000
--- a/amend/tests/one-test
+++ /dev/null
@@ -1,150 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2007 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.
-
-# Set up prog to be the path of this script, including following symlinks,
-# and set up progdir to be the fully-qualified pathname of its directory.
-prog="$0"
-while [ -h "${prog}" ]; do
-    newProg=`/bin/ls -ld "${prog}"`
-    newProg=`expr "${newProg}" : ".* -> \(.*\)$"`
-    if expr "x${newProg}" : 'x/' >/dev/null; then
-        prog="${newProg}"
-    else
-        progdir=`dirname "${prog}"`
-        prog="${progdir}/${newProg}"
-    fi
-done
-oldwd=`pwd`
-progdir=`dirname "${prog}"`
-cd "${progdir}"
-progdir=`pwd`
-prog="${progdir}"/`basename "${prog}"`
-
-info="info.txt"
-run="run"
-expected="expected.txt"
-output="out.txt"
-skip="SKIP"
-
-dev_mode="no"
-if [ "x$1" = "x--dev" ]; then
-    dev_mode="yes"
-    shift
-fi
-
-update_mode="no"
-if [ "x$1" = "x--update" ]; then
-    update_mode="yes"
-    shift
-fi
-
-usage="no"
-if [ "x$1" = "x--help" ]; then
-    usage="yes"
-else
-    if [ "x$1" = "x" ]; then
-        testdir=`basename "$oldwd"`
-    else
-        testdir="$1"
-    fi
-
-    if [ '!' -d "$testdir" ]; then
-        td2=`echo ${testdir}-*`
-        if [ '!' -d "$td2" ]; then
-            echo "${testdir}: no such test directory" 1>&2
-            usage="yes"
-        fi
-        testdir="$td2"
-    fi
-fi
-
-if [ "$usage" = "yes" ]; then
-    prog=`basename $prog`
-    (
-        echo "usage:"
-        echo "  $prog --help             Print this message."
-        echo "  $prog testname           Run test normally."
-        echo "  $prog --dev testname     Development mode (dump to stdout)."
-        echo "  $prog --update testname  Update mode (replace expected.txt)."
-        echo "  Omitting the test name uses the current directory as the test."
-    ) 1>&2
-    exit 1
-fi
-
-td_info="$testdir"/"$info"
-td_run="$testdir"/"$run"
-td_expected="$testdir"/"$expected"
-td_skip="$testdir"/"$skip"
-
-if [ -r "$td_skip" ]; then
-    exit 2
-fi
-
-tmpdir=/tmp/test-$$
-
-if [ '!' '(' -r "$td_info" -a -r "$td_run" -a -r "$td_expected" ')' ]; then
-    echo "${testdir}: missing files" 1>&2
-    exit 1
-fi
-
-# copy the test to a temp dir and run it
-
-echo "${testdir}: running..." 1>&2
-
-rm -rf "$tmpdir"
-cp -Rp "$testdir" "$tmpdir"
-cd "$tmpdir"
-chmod 755 "$run"
-
-#PATH="${progdir}/../build/bin:${PATH}"
-
-good="no"
-if [ "$dev_mode" = "yes" ]; then
-    "./$run" 2>&1
-    echo "exit status: $?" 1>&2
-    good="yes"
-elif [ "$update_mode" = "yes" ]; then
-    "./$run" >"${progdir}/$td_expected" 2>&1
-    good="yes"
-else
-    "./$run" >"$output" 2>&1
-    cmp -s "$expected" "$output"
-    if [ "$?" = "0" ]; then
-        # output == expected
-        good="yes"
-        echo "$testdir"': succeeded!' 1>&2
-    fi
-fi
-
-if [ "$good" = "yes" ]; then
-    cd "$oldwd"
-    rm -rf "$tmpdir"
-    exit 0
-fi
-
-(
-    echo "${testdir}: FAILED!"
-    echo ' '
-    echo '#################### info'
-    cat "$info" | sed 's/^/# /g'
-    echo '#################### diffs'
-    diff -u "$expected" "$output"
-    echo '####################'
-    echo ' '
-    echo "files left in $tmpdir"
-) 1>&2
-
-exit 1
diff --git a/amend/tests/run-all-tests b/amend/tests/run-all-tests
deleted file mode 100755
index c696bbd..0000000
--- a/amend/tests/run-all-tests
+++ /dev/null
@@ -1,69 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2007 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.
-
-# Set up prog to be the path of this script, including following symlinks,
-# and set up progdir to be the fully-qualified pathname of its directory.
-prog="$0"
-while [ -h "${prog}" ]; do
-    newProg=`/bin/ls -ld "${prog}"`
-    newProg=`expr "${newProg}" : ".* -> \(.*\)$"`
-    if expr "x${newProg}" : 'x/' >/dev/null; then
-        prog="${newProg}"
-    else
-        progdir=`dirname "${prog}"`
-        prog="${progdir}/${newProg}"
-    fi
-done
-oldwd=`pwd`
-progdir=`dirname "${prog}"`
-cd "${progdir}"
-progdir=`pwd`
-prog="${progdir}"/`basename "${prog}"`
-
-passed=0
-skipped=0
-skipNames=""
-failed=0
-failNames=""
-
-for i in *; do
-    if [ -d "$i" -a -r "$i" ]; then
-        ./one-test "$i"
-        status=$?
-        if [ "$status" = "0" ]; then
-            ((passed += 1))
-        elif [ "$status" = "2" ]; then
-            ((skipped += 1))
-            skipNames="$skipNames $i"
-        else
-            ((failed += 1))
-            failNames="$failNames $i"
-        fi
-    fi
-done
-
-echo "passed:  $passed test(s)"
-echo "skipped: $skipped test(s)"
-
-for i in $skipNames; do
-    echo "skipped: $i"
-done
-
-echo "failed:  $failed test(s)"
-    
-for i in $failNames; do
-    echo "failed: $i"
-done
diff --git a/applypatch/Android.mk b/applypatch/Android.mk
index bb024f6..e91e4bf 100644
--- a/applypatch/Android.mk
+++ b/applypatch/Android.mk
@@ -14,6 +14,7 @@
 
 ifneq ($(TARGET_SIMULATOR),true)
 
+ifeq ($(TARGET_ARCH),arm)
 LOCAL_PATH := $(call my-dir)
 include $(CLEAR_VARS)
 
@@ -58,4 +59,5 @@
 
 include $(BUILD_HOST_EXECUTABLE)
 
+endif   # TARGET_ARCH == arm
 endif  # !TARGET_SIMULATOR
diff --git a/applypatch/applypatch.c b/applypatch/applypatch.c
index a0493e0..fd8153a 100644
--- a/applypatch/applypatch.c
+++ b/applypatch/applypatch.c
@@ -30,10 +30,10 @@
 #include "mtdutils/mtdutils.h"
 #include "edify/expr.h"
 
-int SaveFileContents(const char* filename, FileContents file);
-int LoadMTDContents(const char* filename, FileContents* file);
+static int SaveFileContents(const char* filename, FileContents file);
+static int LoadPartitionContents(const char* filename, FileContents* file);
 int ParseSha1(const char* str, uint8_t* digest);
-ssize_t FileSink(unsigned char* data, ssize_t len, void* token);
+static ssize_t FileSink(unsigned char* data, ssize_t len, void* token);
 
 static int mtd_partitions_scanned = 0;
 
@@ -42,10 +42,11 @@
 int LoadFileContents(const char* filename, FileContents* file) {
     file->data = NULL;
 
-    // A special 'filename' beginning with "MTD:" means to load the
-    // contents of an MTD partition.
-    if (strncmp(filename, "MTD:", 4) == 0) {
-        return LoadMTDContents(filename, file);
+    // A special 'filename' beginning with "MTD:" or "EMMC:" means to
+    // load the contents of a partition.
+    if (strncmp(filename, "MTD:", 4) == 0 ||
+        strncmp(filename, "EMMC:", 5) == 0) {
+        return LoadPartitionContents(filename, file);
     }
 
     if (stat(filename, &file->st) != 0) {
@@ -98,27 +99,35 @@
     free(file);
 }
 
-// Load the contents of an MTD partition into the provided
+// Load the contents of an MTD or EMMC partition into the provided
 // FileContents.  filename should be a string of the form
-// "MTD:<partition_name>:<size_1>:<sha1_1>:<size_2>:<sha1_2>:...".
-// The smallest size_n bytes for which that prefix of the mtd contents
-// has the corresponding sha1 hash will be loaded.  It is acceptable
-// for a size value to be repeated with different sha1s.  Will return
-// 0 on success.
+// "MTD:<partition_name>:<size_1>:<sha1_1>:<size_2>:<sha1_2>:..."  (or
+// "EMMC:<partition_device>:...").  The smallest size_n bytes for
+// which that prefix of the partition contents has the corresponding
+// sha1 hash will be loaded.  It is acceptable for a size value to be
+// repeated with different sha1s.  Will return 0 on success.
 //
 // This complexity is needed because if an OTA installation is
 // interrupted, the partition might contain either the source or the
 // target data, which might be of different lengths.  We need to know
-// the length in order to read from MTD (there is no "end-of-file"
-// marker), so the caller must specify the possible lengths and the
-// hash of the data, and we'll do the load expecting to find one of
-// those hashes.
-int LoadMTDContents(const char* filename, FileContents* file) {
-#ifdef BOARD_USES_MTDUTILS
+// the length in order to read from a partition (there is no
+// "end-of-file" marker), so the caller must specify the possible
+// lengths and the hash of the data, and we'll do the load expecting
+// to find one of those hashes.
+enum PartitionType { MTD, EMMC };
+
+static int LoadPartitionContents(const char* filename, FileContents* file) {
     char* copy = strdup(filename);
     const char* magic = strtok(copy, ":");
-    if (strcmp(magic, "MTD") != 0) {
-        printf("LoadMTDContents called with bad filename (%s)\n",
+
+    enum PartitionType type;
+
+    if (strcmp(magic, "MTD") == 0) {
+        type = MTD;
+    } else if (strcmp(magic, "EMMC") == 0) {
+        type = EMMC;
+    } else {
+        printf("LoadPartitionContents called with bad filename (%s)\n",
                filename);
         return -1;
     }
@@ -132,7 +141,7 @@
         }
     }
     if (colons < 3 || colons%2 == 0) {
-        printf("LoadMTDContents called with bad filename (%s)\n",
+        printf("LoadPartitionContents called with bad filename (%s)\n",
                filename);
     }
 
@@ -145,7 +154,7 @@
         const char* size_str = strtok(NULL, ":");
         size[i] = strtol(size_str, NULL, 10);
         if (size[i] == 0) {
-            printf("LoadMTDContents called with bad size (%s)\n", filename);
+            printf("LoadPartitionContents called with bad size (%s)\n", filename);
             return -1;
         }
         sha1sum[i] = strtok(NULL, ":");
@@ -157,23 +166,38 @@
     size_array = size;
     qsort(index, pairs, sizeof(int), compare_size_indices);
 
-    if (!mtd_partitions_scanned) {
-        mtd_scan_partitions();
-        mtd_partitions_scanned = 1;
-    }
+    MtdReadContext* ctx = NULL;
+    FILE* dev = NULL;
 
-    const MtdPartition* mtd = mtd_find_partition_by_name(partition);
-    if (mtd == NULL) {
-        printf("mtd partition \"%s\" not found (loading %s)\n",
-               partition, filename);
-        return -1;
-    }
+    switch (type) {
+        case MTD:
+            if (!mtd_partitions_scanned) {
+                mtd_scan_partitions();
+                mtd_partitions_scanned = 1;
+            }
 
-    MtdReadContext* ctx = mtd_read_partition(mtd);
-    if (ctx == NULL) {
-        printf("failed to initialize read of mtd partition \"%s\"\n",
-               partition);
-        return -1;
+            const MtdPartition* mtd = mtd_find_partition_by_name(partition);
+            if (mtd == NULL) {
+                printf("mtd partition \"%s\" not found (loading %s)\n",
+                       partition, filename);
+                return -1;
+            }
+
+            ctx = mtd_read_partition(mtd);
+            if (ctx == NULL) {
+                printf("failed to initialize read of mtd partition \"%s\"\n",
+                       partition);
+                return -1;
+            }
+            break;
+
+        case EMMC:
+            dev = fopen(partition, "rb");
+            if (dev == NULL) {
+                printf("failed to open emmc partition \"%s\": %s\n",
+                       partition, strerror(errno));
+                return -1;
+            }
     }
 
     SHA_CTX sha_ctx;
@@ -192,7 +216,15 @@
         size_t next = size[index[i]] - file->size;
         size_t read = 0;
         if (next > 0) {
-            read = mtd_read_data(ctx, p, next);
+            switch (type) {
+                case MTD:
+                    read = mtd_read_data(ctx, p, next);
+                    break;
+
+                case EMMC:
+                    read = fread(p, 1, next, dev);
+                    break;
+            }
             if (next != read) {
                 printf("short read (%d bytes of %d) for partition \"%s\"\n",
                        read, next, partition);
@@ -221,7 +253,7 @@
         if (memcmp(sha_so_far, parsed_sha, SHA_DIGEST_SIZE) == 0) {
             // we have a match.  stop reading the partition; we'll return
             // the data we've read so far.
-            printf("mtd read matched size %d sha %s\n",
+            printf("partition read matched size %d sha %s\n",
                    size[index[i]], sha1sum[index[i]]);
             break;
         }
@@ -229,12 +261,21 @@
         p += read;
     }
 
-    mtd_read_close(ctx);
+    switch (type) {
+        case MTD:
+            mtd_read_close(ctx);
+            break;
+
+        case EMMC:
+            fclose(dev);
+            break;
+    }
+
 
     if (i == pairs) {
         // Ran off the end of the list of (size,sha1) pairs without
         // finding a match.
-        printf("contents of MTD partition \"%s\" didn't match %s\n",
+        printf("contents of partition \"%s\" didn't match %s\n",
                partition, filename);
         free(file->data);
         file->data = NULL;
@@ -257,16 +298,12 @@
     free(sha1sum);
 
     return 0;
-#else
-    printf("mtd utils not supported.\n");
-    return -1;
-#endif
 }
 
 
 // Save the contents of the given FileContents object under the given
 // filename.  Return 0 on success.
-int SaveFileContents(const char* filename, FileContents file) {
+static int SaveFileContents(const char* filename, FileContents file) {
     int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC);
     if (fd < 0) {
         printf("failed to open \"%s\" for write: %s\n",
@@ -297,68 +334,88 @@
     return 0;
 }
 
-// Write a memory buffer to target_mtd partition, a string of the form
-// "MTD:<partition>[:...]".  Return 0 on success.
-int WriteToMTDPartition(unsigned char* data, size_t len,
-                        const char* target_mtd) {
-#ifdef BOARD_USES_MTDUTILS
-    char* partition = strchr(target_mtd, ':');
+// Write a memory buffer to 'target' partition, a string of the form
+// "MTD:<partition>[:...]" or "EMMC:<partition_device>:".  Return 0 on
+// success.
+int WriteToPartition(unsigned char* data, size_t len,
+                        const char* target) {
+    char* copy = strdup(target);
+    const char* magic = strtok(copy, ":");
+
+    enum PartitionType type;
+    if (strcmp(magic, "MTD") == 0) {
+        type = MTD;
+    } else if (strcmp(magic, "EMMC") == 0) {
+        type = EMMC;
+    } else {
+        printf("WriteToPartition called with bad target (%s)\n", target);
+        return -1;
+    }
+    const char* partition = strtok(NULL, ":");
+
     if (partition == NULL) {
-        printf("bad MTD target name \"%s\"\n", target_mtd);
-        return -1;
-    }
-    ++partition;
-    // Trim off anything after a colon, eg "MTD:boot:blah:blah:blah...".
-    // We want just the partition name "boot".
-    partition = strdup(partition);
-    char* end = strchr(partition, ':');
-    if (end != NULL)
-        *end = '\0';
-
-    if (!mtd_partitions_scanned) {
-        mtd_scan_partitions();
-        mtd_partitions_scanned = 1;
-    }
-
-    const MtdPartition* mtd = mtd_find_partition_by_name(partition);
-    if (mtd == NULL) {
-        printf("mtd partition \"%s\" not found for writing\n", partition);
+        printf("bad partition target name \"%s\"\n", target);
         return -1;
     }
 
-    MtdWriteContext* ctx = mtd_write_partition(mtd);
-    if (ctx == NULL) {
-        printf("failed to init mtd partition \"%s\" for writing\n",
-               partition);
-        return -1;
+    switch (type) {
+        case MTD:
+            if (!mtd_partitions_scanned) {
+                mtd_scan_partitions();
+                mtd_partitions_scanned = 1;
+            }
+
+            const MtdPartition* mtd = mtd_find_partition_by_name(partition);
+            if (mtd == NULL) {
+                printf("mtd partition \"%s\" not found for writing\n",
+                       partition);
+                return -1;
+            }
+
+            MtdWriteContext* ctx = mtd_write_partition(mtd);
+            if (ctx == NULL) {
+                printf("failed to init mtd partition \"%s\" for writing\n",
+                       partition);
+                return -1;
+            }
+
+            size_t written = mtd_write_data(ctx, (char*)data, len);
+            if (written != len) {
+                printf("only wrote %d of %d bytes to MTD %s\n",
+                       written, len, partition);
+                mtd_write_close(ctx);
+                return -1;
+            }
+
+            if (mtd_erase_blocks(ctx, -1) < 0) {
+                printf("error finishing mtd write of %s\n", partition);
+                mtd_write_close(ctx);
+                return -1;
+            }
+
+            if (mtd_write_close(ctx)) {
+                printf("error closing mtd write of %s\n", partition);
+                return -1;
+            }
+            break;
+
+        case EMMC:
+            ;
+            FILE* f = fopen(partition, "wb");
+            if (fwrite(data, 1, len, f) != len) {
+                printf("short write writing to %s (%s)\n",
+                       partition, strerror(errno));
+                return -1;
+            }
+            if (fclose(f) != 0) {
+                printf("error closing %s (%s)\n", partition, strerror(errno));
+                return -1;
+            }
+            break;
     }
 
-    size_t written = mtd_write_data(ctx, (char*)data, len);
-    if (written != len) {
-        printf("only wrote %d of %d bytes to MTD %s\n",
-               written, len, partition);
-        mtd_write_close(ctx);
-        return -1;
-    }
-
-    if (mtd_erase_blocks(ctx, -1) < 0) {
-        printf("error finishing mtd write of %s\n", partition);
-        mtd_write_close(ctx);
-        return -1;
-    }
-
-    if (mtd_write_close(ctx)) {
-        printf("error closing mtd write of %s\n", partition);
-        return -1;
-    }
-
-    free(partition);
+    free(copy);
     return 0;
-#else
-    printf("mtd utils not supported.\n");
-    return -1;
-#endif
-
 }
 
 
@@ -417,7 +474,7 @@
     file.data = NULL;
 
     // It's okay to specify no sha1s; the check will pass if the
-    // LoadFileContents is successful.  (Useful for reading MTD
+    // LoadFileContents is successful.  (Useful for reading
     // partitions, where the filename encodes the sha1s; no need to
     // check them twice.)
     if (LoadFileContents(filename, &file) != 0 ||
@@ -529,8 +586,8 @@
 // - otherwise, or if any error is encountered, exits with non-zero
 //   status.
 //
-// <source_filename> may refer to an MTD partition to read the source
-// data.  See the comments for the LoadMTDContents() function above
+// <source_filename> may refer to a partition to read the source data.
+// See the comments for the LoadPartition Contents() function above
 // for the format of such a filename.
 
 int applypatch(const char* source_filename,
@@ -599,7 +656,7 @@
 
         int to_use = FindMatchingPatch(copy_file.sha1,
                                        patch_sha1_str, num_patches);
-        if (to_use > 0) {
+        if (to_use >= 0) {
             copy_patch_value = patch_data[to_use];
         }
 
@@ -634,14 +691,16 @@
         // Is there enough room in the target filesystem to hold the patched
         // file?
 
-        if (strncmp(target_filename, "MTD:", 4) == 0) {
-            // If the target is an MTD partition, we're actually going to
-            // write the output to /tmp and then copy it to the partition.
-            // statfs() always returns 0 blocks free for /tmp, so instead
-            // we'll just assume that /tmp has enough space to hold the file.
+        if (strncmp(target_filename, "MTD:", 4) == 0 ||
+            strncmp(target_filename, "EMMC:", 5) == 0) {
+            // If the target is a partition, we're actually going to
+            // write the output to /tmp and then copy it to the
+            // partition.  statfs() always returns 0 blocks free for
+            // /tmp, so instead we'll just assume that /tmp has enough
+            // space to hold the file.
 
-            // We still write the original source to cache, in case the MTD
-            // write is interrupted.
+            // We still write the original source to cache, in case
+            // the partition write is interrupted.
             if (MakeFreeSpaceOnCache(source_file.size) < 0) {
                 printf("not enough free space on /cache\n");
                 return 1;
@@ -656,7 +715,8 @@
             int enough_space = 0;
             if (retry > 0) {
                 size_t free_space = FreeSpaceForFile(target_fs);
-                int enough_space =
+                enough_space =
+                    (free_space > (256 << 10)) &&          // 256k (two-block) minimum
                     (free_space > (target_size * 3 / 2));  // 50% margin of error
                 printf("target %ld bytes; free space %ld bytes; retry %d; enough %d\n",
                        (long)target_size, (long)free_space, retry, enough_space);
@@ -671,11 +731,13 @@
                 // copy the source file to cache, then delete it from the original
                 // location.
 
-                if (strncmp(source_filename, "MTD:", 4) == 0) {
+                if (strncmp(source_filename, "MTD:", 4) == 0 ||
+                    strncmp(source_filename, "EMMC:", 5) == 0) {
                     // It's impossible to free space on the target filesystem by
-                    // deleting the source if the source is an MTD partition.  If
+                    // deleting the source if the source is a partition.  If
                     // we're ever in a state where we need to do this, fail.
-                    printf("not enough free space for target but source is MTD\n");
+                    printf("not enough free space for target but source "
+                           "is partition\n");
                     return 1;
                 }
 
@@ -714,7 +776,8 @@
         void* token = NULL;
         output = -1;
         outname = NULL;
-        if (strncmp(target_filename, "MTD:", 4) == 0) {
+        if (strncmp(target_filename, "MTD:", 4) == 0 ||
+            strncmp(target_filename, "EMMC:", 5) == 0) {
             // We store the decoded output in memory.
             msi.buffer = malloc(target_size);
             if (msi.buffer == NULL) {
@@ -790,8 +853,8 @@
     }
 
     if (output < 0) {
-        // Copy the temp file to the MTD partition.
-        if (WriteToMTDPartition(msi.buffer, msi.pos, target_filename) != 0) {
+        // Copy the temp file to the partition.
+        if (WriteToPartition(msi.buffer, msi.pos, target_filename) != 0) {
             printf("write of patched data to %s failed\n", target_filename);
             return 1;
         }
diff --git a/bmlutils/Android.mk b/bmlutils/Android.mk
index 6b3a537..e95cb5f 100644
--- a/bmlutils/Android.mk
+++ b/bmlutils/Android.mk
@@ -4,4 +4,5 @@
 LOCAL_CFLAGS += -DBOARD_BOOT_DEVICE=\"$(BOARD_BOOT_DEVICE)\"
 LOCAL_SRC_FILES := bmlutils.c
 LOCAL_MODULE := libbmlutils
+LOCAL_MODULE_TAGS := eng
 include $(BUILD_STATIC_LIBRARY)
diff --git a/bootloader.c b/bootloader.c
index bc79bee..b690c55 100644
--- a/bootloader.c
+++ b/bootloader.c
@@ -23,96 +23,103 @@
 #include <stdio.h>
 #include <string.h>
 
-static const char *CACHE_NAME = "CACHE:";
-static const char *MISC_NAME = "MISC:";
+static int get_bootloader_message_mtd(struct bootloader_message *out, const Volume* v);
+static int set_bootloader_message_mtd(const struct bootloader_message *in, const Volume* v);
+static int get_bootloader_message_block(struct bootloader_message *out, const Volume* v);
+static int set_bootloader_message_block(const struct bootloader_message *in, const Volume* v);
+
+int get_bootloader_message(struct bootloader_message *out) {
+    Volume* v = volume_for_path("/misc");
+    if (strcmp(v->fs_type, "mtd") == 0) {
+        return get_bootloader_message_mtd(out, v);
+    } else if (strcmp(v->fs_type, "emmc") == 0) {
+        return get_bootloader_message_block(out, v);
+    }
+    LOGE("unknown misc partition fs_type \"%s\"\n", v->fs_type);
+    return -1;
+}
+
+int set_bootloader_message(const struct bootloader_message *in) {
+    Volume* v = volume_for_path("/misc");
+    if (strcmp(v->fs_type, "mtd") == 0) {
+        return set_bootloader_message_mtd(in, v);
+    } else if (strcmp(v->fs_type, "emmc") == 0) {
+        return set_bootloader_message_block(in, v);
+    }
+    LOGE("unknown misc partition fs_type \"%s\"\n", v->fs_type);
+    return -1;
+}
+
+// ------------------------------
+// for misc partitions on MTD
+// ------------------------------
+
 static const int MISC_PAGES = 3;         // number of pages to save
 static const int MISC_COMMAND_PAGE = 1;  // bootloader command is this page
 
-#ifdef LOG_VERBOSE
-static void dump_data(const char *data, int len) {
-    int pos;
-    for (pos = 0; pos < len; ) {
-        printf("%05x: %02x", pos, data[pos]);
-        for (++pos; pos < len && (pos % 24) != 0; ++pos) {
-            printf(" %02x", data[pos]);
-        }
-        printf("\n");
-    }
-}
-#endif
-
-int get_bootloader_message(struct bootloader_message *out) {
+static int get_bootloader_message_mtd(struct bootloader_message *out,
+                                      const Volume* v) {
     size_t write_size;
-    const MtdPartition *part = get_root_mtd_partition(MISC_NAME);
+    mtd_scan_partitions();
+    const MtdPartition *part = mtd_find_partition_by_name(v->device);
     if (part == NULL || mtd_partition_info(part, NULL, NULL, &write_size)) {
-        LOGE("Can't find %s\n", MISC_NAME);
+        LOGE("Can't find %s\n", v->device);
         return -1;
     }
 
     MtdReadContext *read = mtd_read_partition(part);
     if (read == NULL) {
-        LOGE("Can't open %s\n(%s)\n", MISC_NAME, strerror(errno));
+        LOGE("Can't open %s\n(%s)\n", v->device, strerror(errno));
         return -1;
     }
 
     const ssize_t size = write_size * MISC_PAGES;
     char data[size];
     ssize_t r = mtd_read_data(read, data, size);
-    if (r != size) LOGE("Can't read %s\n(%s)\n", MISC_NAME, strerror(errno));
+    if (r != size) LOGE("Can't read %s\n(%s)\n", v->device, strerror(errno));
     mtd_read_close(read);
     if (r != size) return -1;
 
-#ifdef LOG_VERBOSE
-    printf("\n--- get_bootloader_message ---\n");
-    dump_data(data, size);
-    printf("\n");
-#endif
-
     memcpy(out, &data[write_size * MISC_COMMAND_PAGE], sizeof(*out));
     return 0;
 }
-
-int set_bootloader_message(const struct bootloader_message *in) {
+static int set_bootloader_message_mtd(const struct bootloader_message *in,
+                                      const Volume* v) {
     size_t write_size;
-    const MtdPartition *part = get_root_mtd_partition(MISC_NAME);
+    mtd_scan_partitions();
+    const MtdPartition *part = mtd_find_partition_by_name(v->device);
     if (part == NULL || mtd_partition_info(part, NULL, NULL, &write_size)) {
-        LOGE("Can't find %s\n", MISC_NAME);
+        LOGE("Can't find %s\n", v->device);
         return -1;
     }
 
     MtdReadContext *read = mtd_read_partition(part);
     if (read == NULL) {
-        LOGE("Can't open %s\n(%s)\n", MISC_NAME, strerror(errno));
+        LOGE("Can't open %s\n(%s)\n", v->device, strerror(errno));
         return -1;
     }
 
     ssize_t size = write_size * MISC_PAGES;
     char data[size];
     ssize_t r = mtd_read_data(read, data, size);
-    if (r != size) LOGE("Can't read %s\n(%s)\n", MISC_NAME, strerror(errno));
+    if (r != size) LOGE("Can't read %s\n(%s)\n", v->device, strerror(errno));
     mtd_read_close(read);
     if (r != size) return -1;
 
     memcpy(&data[write_size * MISC_COMMAND_PAGE], in, sizeof(*in));
 
-#ifdef LOG_VERBOSE
-    printf("\n--- set_bootloader_message ---\n");
-    dump_data(data, size);
-    printf("\n");
-#endif
-
     MtdWriteContext *write = mtd_write_partition(part);
     if (write == NULL) {
-        LOGE("Can't open %s\n(%s)\n", MISC_NAME, strerror(errno));
+        LOGE("Can't open %s\n(%s)\n", v->device, strerror(errno));
         return -1;
     }
     if (mtd_write_data(write, data, size) != size) {
-        LOGE("Can't write %s\n(%s)\n", MISC_NAME, strerror(errno));
+        LOGE("Can't write %s\n(%s)\n", v->device, strerror(errno));
         mtd_write_close(write);
         return -1;
     }
     if (mtd_write_close(write)) {
-        LOGE("Can't finish %s\n(%s)\n", MISC_NAME, strerror(errno));
+        LOGE("Can't finish %s\n(%s)\n", v->device, strerror(errno));
         return -1;
     }
 
@@ -120,152 +127,47 @@
     return 0;
 }
 
-/* Update Image
- *
- * - will be stored in the "cache" partition
- * - bad blocks will be ignored, like boot.img and recovery.img
- * - the first block will be the image header (described below)
- * - the size is in BYTES, inclusive of the header
- * - offsets are in BYTES from the start of the update header
- * - two raw bitmaps will be included, the "busy" and "fail" bitmaps
- * - for dream, the bitmaps will be 320x480x16bpp RGB565
- */
 
-#define UPDATE_MAGIC       "MSM-RADIO-UPDATE"
-#define UPDATE_MAGIC_SIZE  16
-#define UPDATE_VERSION     0x00010000
+// ------------------------------------
+// for misc partitions on block devices
+// ------------------------------------
 
-struct update_header {
-    unsigned char MAGIC[UPDATE_MAGIC_SIZE];
-
-    unsigned version;
-    unsigned size;
-
-    unsigned image_offset;
-    unsigned image_length;
-
-    unsigned bitmap_width;
-    unsigned bitmap_height;
-    unsigned bitmap_bpp;
-
-    unsigned busy_bitmap_offset;
-    unsigned busy_bitmap_length;
-
-    unsigned fail_bitmap_offset;
-    unsigned fail_bitmap_length;
-};
-
-int write_update_for_bootloader(
-        const char *update, int update_length,
-        int bitmap_width, int bitmap_height, int bitmap_bpp,
-        const char *busy_bitmap, const char *fail_bitmap) {
-    if (ensure_root_path_unmounted(CACHE_NAME)) {
-        LOGE("Can't unmount %s\n", CACHE_NAME);
+static int get_bootloader_message_block(struct bootloader_message *out,
+                                        const Volume* v) {
+    FILE* f = fopen(v->device, "rb");
+    if (f == NULL) {
+        LOGE("Can't open %s\n(%s)\n", v->device, strerror(errno));
         return -1;
     }
-
-    const MtdPartition *part = get_root_mtd_partition(CACHE_NAME);
-    if (part == NULL) {
-        LOGE("Can't find %s\n", CACHE_NAME);
+    struct bootloader_message temp;
+    int count = fread(&temp, sizeof(temp), 1, f);
+    if (count != 1) {
+        LOGE("Failed reading %s\n(%s)\n", v->device, strerror(errno));
         return -1;
     }
-
-    MtdWriteContext *write = mtd_write_partition(part);
-    if (write == NULL) {
-        LOGE("Can't open %s\n(%s)\n", CACHE_NAME, strerror(errno));
+    if (fclose(f) != 0) {
+        LOGE("Failed closing %s\n(%s)\n", v->device, strerror(errno));
         return -1;
     }
+    memcpy(out, &temp, sizeof(temp));
+    return 0;
+}
 
-    /* Write an invalid (zero) header first, to disable any previous
-     * update and any other structured contents (like a filesystem),
-     * and as a placeholder for the amount of space required.
-     */
-
-    struct update_header header;
-    memset(&header, 0, sizeof(header));
-    const ssize_t header_size = sizeof(header);
-    if (mtd_write_data(write, (char*) &header, header_size) != header_size) {
-        LOGE("Can't write header to %s\n(%s)\n", CACHE_NAME, strerror(errno));
-        mtd_write_close(write);
+static int set_bootloader_message_block(const struct bootloader_message *in,
+                                        const Volume* v) {
+    FILE* f = fopen(v->device, "wb");
+    if (f == NULL) {
+        LOGE("Can't open %s\n(%s)\n", v->device, strerror(errno));
         return -1;
     }
-
-    /* Write each section individually block-aligned, so we can write
-     * each block independently without complicated buffering.
-     */
-
-    memcpy(&header.MAGIC, UPDATE_MAGIC, UPDATE_MAGIC_SIZE);
-    header.version = UPDATE_VERSION;
-    header.size = header_size;
-
-    off_t image_start_pos = mtd_erase_blocks(write, 0);
-    header.image_length = update_length;
-    if ((int) header.image_offset == -1 ||
-        mtd_write_data(write, update, update_length) != update_length) {
-        LOGE("Can't write update to %s\n(%s)\n", CACHE_NAME, strerror(errno));
-        mtd_write_close(write);
+    int count = fwrite(in, sizeof(*in), 1, f);
+    if (count != 1) {
+        LOGE("Failed writing %s\n(%s)\n", v->device, strerror(errno));
         return -1;
     }
-    off_t busy_start_pos = mtd_erase_blocks(write, 0);
-    header.image_offset = mtd_find_write_start(write, image_start_pos);
-
-    header.bitmap_width = bitmap_width;
-    header.bitmap_height = bitmap_height;
-    header.bitmap_bpp = bitmap_bpp;
-
-    int bitmap_length = (bitmap_bpp + 7) / 8 * bitmap_width * bitmap_height;
-
-    header.busy_bitmap_length = busy_bitmap != NULL ? bitmap_length : 0;
-    if ((int) header.busy_bitmap_offset == -1 ||
-        mtd_write_data(write, busy_bitmap, bitmap_length) != bitmap_length) {
-        LOGE("Can't write bitmap to %s\n(%s)\n", CACHE_NAME, strerror(errno));
-        mtd_write_close(write);
+    if (fclose(f) != 0) {
+        LOGE("Failed closing %s\n(%s)\n", v->device, strerror(errno));
         return -1;
     }
-    off_t fail_start_pos = mtd_erase_blocks(write, 0);
-    header.busy_bitmap_offset = mtd_find_write_start(write, busy_start_pos);
-
-    header.fail_bitmap_length = fail_bitmap != NULL ? bitmap_length : 0;
-    if ((int) header.fail_bitmap_offset == -1 ||
-        mtd_write_data(write, fail_bitmap, bitmap_length) != bitmap_length) {
-        LOGE("Can't write bitmap to %s\n(%s)\n", CACHE_NAME, strerror(errno));
-        mtd_write_close(write);
-        return -1;
-    }
-    mtd_erase_blocks(write, 0);
-    header.fail_bitmap_offset = mtd_find_write_start(write, fail_start_pos);
-
-    /* Write the header last, after all the blocks it refers to, so that
-     * when the magic number is installed everything is valid.
-     */
-
-    if (mtd_write_close(write)) {
-        LOGE("Can't finish writing %s\n(%s)\n", CACHE_NAME, strerror(errno));
-        return -1;
-    }
-
-    write = mtd_write_partition(part);
-    if (write == NULL) {
-        LOGE("Can't reopen %s\n(%s)\n", CACHE_NAME, strerror(errno));
-        return -1;
-    }
-
-    if (mtd_write_data(write, (char*) &header, header_size) != header_size) {
-        LOGE("Can't rewrite header to %s\n(%s)\n", CACHE_NAME, strerror(errno));
-        mtd_write_close(write);
-        return -1;
-    }
-
-    if (mtd_erase_blocks(write, 0) != image_start_pos) {
-        LOGE("Misalignment rewriting %s\n(%s)\n", CACHE_NAME, strerror(errno));
-        mtd_write_close(write);
-        return -1;
-    }
-
-    if (mtd_write_close(write)) {
-        LOGE("Can't finish header of %s\n(%s)\n", CACHE_NAME, strerror(errno));
-        return -1;
-    }
-
     return 0;
 }
diff --git a/commands.c b/commands.c
deleted file mode 100644
index d233877..0000000
--- a/commands.c
+++ /dev/null
@@ -1,1299 +0,0 @@
-/*
- * Copyright (C) 2007 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.
- */
-
-#undef NDEBUG
-
-#include <assert.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <limits.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <unistd.h>
-#include <unistd.h>
-
-#include "amend/commands.h"
-#include "commands.h"
-#include "common.h"
-#include "cutils/misc.h"
-#include "cutils/properties.h"
-#include "firmware.h"
-#include "minzip/DirUtil.h"
-#include "minzip/Zip.h"
-#include "roots.h"
-
-#include "extendedcommands.h"
-#include "flashutils/flashutils.h"
-
-static int gDidShowProgress = 0;
-
-#define UNUSED(p)   ((void)(p))
-
-#define CHECK_BOOL() \
-    do { \
-        assert(argv == NULL); \
-        if (argv != NULL) return -1; \
-        assert(argc == true || argc == false); \
-        if (argc != true && argc != false) return -1; \
-    } while (false)
-
-#define CHECK_WORDS() \
-    do { \
-        assert(argc >= 0); \
-        if (argc < 0) return -1; \
-        assert(argc == 0 || argv != NULL); \
-        if (argc != 0 && argv == NULL) return -1; \
-        if (permissions != NULL) { \
-            int CW_I_; \
-            for (CW_I_ = 0; CW_I_ < argc; CW_I_++) { \
-                assert(argv[CW_I_] != NULL); \
-                if (argv[CW_I_] == NULL) return -1; \
-            } \
-        } \
-    } while (false)
-
-#define CHECK_FN() \
-    do { \
-        CHECK_WORDS(); \
-        if (permissions != NULL) { \
-            assert(result == NULL); \
-            if (result != NULL) return -1; \
-        } else { \
-            assert(result != NULL); \
-            if (result == NULL) return -1; \
-        } \
-    } while (false)
-
-#define NO_PERMS(perms) \
-    do { \
-        PermissionRequestList *NP_PRL_ = (perms); \
-        if (NP_PRL_ != NULL) { \
-            int NP_RET_ = addPermissionRequestToList(NP_PRL_, \
-                    "", false, PERM_NONE); \
-            if (NP_RET_ < 0) { \
-                /* Returns from the calling function. \
-                 */ \
-                return NP_RET_; \
-            } \
-        } \
-    } while (false)
-
-/*
- * Command definitions
- */
-
-/* assert <boolexpr>
- */
-static int
-cmd_assert(const char *name, void *cookie, int argc, const char *argv[],
-        PermissionRequestList *permissions)
-{
-    UNUSED(name);
-    UNUSED(cookie);
-    CHECK_BOOL();
-    NO_PERMS(permissions);
-
-    if (!script_assert_enabled) {
-      return 0;
-    }
-
-    /* If our argument is false, return non-zero (failure)
-     * If our argument is true, return zero (success)
-     */
-    if (argc) {
-        return 0;
-    } else {
-        return 1;
-    }
-}
-
-/* format <root>
- */
-static int
-cmd_format(const char *name, void *cookie, int argc, const char *argv[],
-        PermissionRequestList *permissions)
-{
-    UNUSED(name);
-    UNUSED(cookie);
-    CHECK_WORDS();
-
-    if (argc != 1) {
-        LOGE("Command %s requires exactly one argument\n", name);
-        return 1;
-    }
-    const char *root = argv[0];
-    ui_print("Formatting %s...\n", root);
-
-    int ret = format_root_device(root);
-    if (ret != 0) {
-        LOGE("Can't format %s\n", root);
-        return 1;
-    }
-#ifdef BOARD_HAS_DATADATA
-    if (0 == strcmp(root, "DATA:")) {
-        ret = format_root_device("DATADATA:");
-        if (ret != 0) {
-            LOGE("Can't format %s\n", root);
-            return 1;
-        }
-    }
-#endif
-    return 0;
-}
-
-/* delete <file1> [<fileN> ...]
- * delete_recursive <file-or-dir1> [<file-or-dirN> ...]
- *
- * Like "rm -f", will try to delete every named file/dir, even if
- * earlier ones fail.  Recursive deletes that fail halfway through
- * give up early.
- */
-static int
-cmd_delete(const char *name, void *cookie, int argc, const char *argv[],
-        PermissionRequestList *permissions)
-{
-    UNUSED(cookie);
-    CHECK_WORDS();
-    int nerr = 0;
-    bool recurse;
-
-    if (argc < 1) {
-        LOGE("Command %s requires at least one argument\n", name);
-        return 1;
-    }
-
-    recurse = (strcmp(name, "delete_recursive") == 0);
-    ui_print("Deleting files...\n");
-//xxx permissions
-
-    int i;
-    for (i = 0; i < argc; i++) {
-        const char *root_path = argv[i];
-        char pathbuf[PATH_MAX];
-        const char *path;
-
-        /* This guarantees that all paths use "SYSTEM:"-style roots;
-         * plain paths won't make it through translate_root_path().
-         */
-        path = translate_root_path(root_path, pathbuf, sizeof(pathbuf));
-        if (path != NULL) {
-            int ret = ensure_root_path_mounted(root_path);
-            if (ret < 0) {
-                LOGW("Can't mount volume to delete \"%s\"\n", root_path);
-                nerr++;
-                continue;
-            }
-            if (recurse) {
-                ret = dirUnlinkHierarchy(path);
-            } else {
-                ret = unlink(path);
-            }
-            if (ret != 0 && errno != ENOENT) {
-                LOGW("Can't delete %s\n(%s)\n", path, strerror(errno));
-                nerr++;
-            }
-        } else {
-            nerr++;
-        }
-    }
-//TODO: add a way to fail if a delete didn't work
-
-    return 0;
-}
-
-typedef struct {
-    int num_done;
-    int num_total;
-} ExtractContext;
-
-static void extract_count_cb(const char *fn, void *cookie)
-{
-   ++((ExtractContext*) cookie)->num_total;
-}
-
-static void extract_cb(const char *fn, void *cookie)
-{
-    // minzip writes the filename to the log, so we don't need to
-    ExtractContext *ctx = (ExtractContext*) cookie;
-    ui_set_progress((float) ++ctx->num_done / ctx->num_total);
-}
-
-/* copy_dir <src-dir> <dst-dir> [<timestamp>]
- *
- * The contents of <src-dir> will become the contents of <dst-dir>.
- * The original contents of <dst-dir> are preserved unless something
- * in <src-dir> overwrote them.
- *
- * e.g., for "copy_dir PKG:system SYSTEM:", the file "PKG:system/a"
- * would be copied to "SYSTEM:a".
- *
- * The specified timestamp (in decimal seconds since 1970) will be used,
- * or a fixed default timestamp will be supplied otherwise.
- */
-static int
-cmd_copy_dir(const char *name, void *cookie, int argc, const char *argv[],
-        PermissionRequestList *permissions)
-{
-    UNUSED(name);
-    UNUSED(cookie);
-    CHECK_WORDS();
-//xxx permissions
-
-    // To create a consistent system image, never use the clock for timestamps.
-    struct utimbuf timestamp = { 1217592000, 1217592000 };  // 8/1/2008 default
-    if (argc == 3) {
-        char *end;
-        time_t value = strtoul(argv[2], &end, 0);
-        if (value == 0 || end[0] != '\0') {
-            LOGE("Command %s: invalid timestamp \"%s\"\n", name, argv[2]);
-            return 1;
-        } else if (value < timestamp.modtime) {
-            LOGE("Command %s: timestamp \"%s\" too early\n", name, argv[2]);
-            return 1;
-        }
-        timestamp.modtime = timestamp.actime = value;
-    } else if (argc != 2) {
-        LOGE("Command %s requires exactly two arguments\n", name);
-        return 1;
-    }
-
-    // Use 40% of the progress bar (80% post-verification) by default
-    ui_print("Copying files...\n");
-    if (!gDidShowProgress) ui_show_progress(DEFAULT_FILES_PROGRESS_FRACTION, 0);
-
-    /* Mount the destination volume if it isn't already.
-     */
-    const char *dst_root_path = argv[1];
-    int ret = ensure_root_path_mounted(dst_root_path);
-    if (ret < 0) {
-        LOGE("Can't mount %s\n", dst_root_path);
-        return 1;
-    }
-
-    /* Get the real target path.
-     */
-    char dstpathbuf[PATH_MAX];
-    const char *dst_path;
-    dst_path = translate_root_path(dst_root_path,
-            dstpathbuf, sizeof(dstpathbuf));
-    if (dst_path == NULL) {
-        LOGE("Command %s: bad destination path \"%s\"\n", name, dst_root_path);
-        return 1;
-    }
-
-    /* Try to copy the directory.  The source may be inside a package.
-     */
-    const char *src_root_path = argv[0];
-    char srcpathbuf[PATH_MAX];
-    const char *src_path;
-    if (is_package_root_path(src_root_path)) {
-        const ZipArchive *package;
-        src_path = translate_package_root_path(src_root_path,
-                srcpathbuf, sizeof(srcpathbuf), &package);
-        if (src_path == NULL) {
-            LOGE("Command %s: bad source path \"%s\"\n", name, src_root_path);
-            return 1;
-        }
-
-        /* Extract the files.  Set MZ_EXTRACT_FILES_ONLY, because only files
-         * are validated by the signature.  Do a dry run first to count how
-         * many there are (and find some errors early).
-         */
-        ExtractContext ctx;
-        ctx.num_done = 0;
-        ctx.num_total = 0;
-
-        if (!mzExtractRecursive(package, src_path, dst_path,
-                    MZ_EXTRACT_FILES_ONLY | MZ_EXTRACT_DRY_RUN,
-                    &timestamp, extract_count_cb, (void *) &ctx) ||
-            !mzExtractRecursive(package, src_path, dst_path,
-                    MZ_EXTRACT_FILES_ONLY,
-                    &timestamp, extract_cb, (void *) &ctx)) {
-            LOGW("Command %s: couldn't extract \"%s\" to \"%s\"\n",
-                    name, src_root_path, dst_root_path);
-            return 1;
-        }
-    } else {
-        LOGE("Command %s: non-package source path \"%s\" not yet supported\n",
-                name, src_root_path);
-//xxx mount the src volume
-//xxx
-        return 255;
-    }
-
-    return 0;
-}
-
-/* run_program <program-file> [<args> ...]
- *
- * Run an external program included in the update package.
- */
-static int
-cmd_run_program(const char *name, void *cookie, int argc, const char *argv[],
-        PermissionRequestList *permissions)
-{
-    UNUSED(cookie);
-    CHECK_WORDS();
-
-    if (argc < 1) {
-        LOGE("Command %s requires at least one argument\n", name);
-        return 1;
-    }
-
-    static const char *binary = "/tmp/run_program_binary";
-    char path[PATH_MAX];
-    if (!is_package_root_path(argv[0])) {
-        // Copy the program file to temporary storage.
-        binary = argv[0];
-        strcpy(path, binary);
-    }
-    else
-    {
-        const ZipArchive *package;
-        if (!translate_package_root_path(argv[0], path, sizeof(path), &package)) {
-            LOGE("Command %s: bad source path \"%s\"\n", name, argv[0]);
-            return 1;
-        }
-
-        const ZipEntry *entry = mzFindZipEntry(package, path);
-        if (entry == NULL) {
-            LOGE("Can't find %s\n", path);
-            return 1;
-        }
-
-        unlink(binary);  // just to be sure
-        int fd = creat(binary, 0755);
-        if (fd < 0) {
-            LOGE("Can't make %s\n", binary);
-            return 1;
-        }
-        bool ok = mzExtractZipEntryToFile(package, entry, fd);
-        close(fd);
-
-        if (!ok) {
-            LOGE("Can't copy %s\n", path);
-            return 1;
-        }
-    }
-
-    // Create a copy of argv to NULL-terminate it, as execv requires
-    char **args = (char **) malloc(sizeof(char*) * (argc + 1));
-    memcpy(args, argv, sizeof(char*) * argc);
-    args[argc] = NULL;
-
-    pid_t pid = fork();
-    if (pid == 0) {
-        execv(binary, args);
-        fprintf(stderr, "E:Can't run %s\n(%s)\n", binary, strerror(errno));
-        _exit(-1);
-    }
-
-    int status;
-    waitpid(pid, &status, 0);
-    if (WIFEXITED(status) && WEXITSTATUS(status) == 0 || !script_assert_enabled) {
-        return 0;
-    } else {
-        LOGE("Error in %s\n(Status %d)\n", path, status);
-        return 1;
-    }
-}
-
-/* set_perm <uid> <gid> <mode> <path> [... <pathN>]
- * set_perm_recursive <uid> <gid> <dir-mode> <file-mode> <path> [... <pathN>]
- *
- * Like "chmod", "chown" and "chgrp" all in one, set ownership and permissions
- * of single files or entire directory trees.  Any error causes failure.
- * User, group, and modes must all be integer values (hex or octal OK).
- */
-static int
-cmd_set_perm(const char *name, void *cookie, int argc, const char *argv[],
-        PermissionRequestList *permissions)
-{
-    UNUSED(cookie);
-    CHECK_WORDS();
-    bool recurse = !strcmp(name, "set_perm_recursive");
-
-    int min_args = 4 + (recurse ? 1 : 0);
-    if (argc < min_args) {
-        LOGE("Command %s requires at least %d args\n", name, min_args);
-        return 1;
-    }
-
-    // All the arguments except the path(s) are numeric.
-    int i, n[min_args - 1];
-    for (i = 0; i < min_args - 1; ++i) {
-        char *end;
-        n[i] = strtoul(argv[i], &end, 0);
-        if (end[0] != '\0' || argv[i][0] == '\0') {
-            LOGE("Command %s: invalid argument \"%s\"\n", name, argv[i]);
-            return 1;
-        }
-    }
-
-    for (i = min_args - 1; i < min_args; ++i) {
-        char path[PATH_MAX];
-        if (translate_root_path(argv[i], path, sizeof(path)) == NULL) {
-            LOGE("Command %s: bad path \"%s\"\n", name, argv[i]);
-            return 1;
-        }
-
-        if (ensure_root_path_mounted(argv[i])) {
-            LOGE("Can't mount %s\n", argv[i]);
-            return 1;
-        }
-
-        if (recurse
-                ? dirSetHierarchyPermissions(path, n[0], n[1], n[2], n[3])
-                : (chown(path, n[0], n[1]) || chmod(path, n[2]))) {
-           LOGE("Can't chown/mod %s\n(%s)\n", path, strerror(errno));
-           return 1;
-        }
-    }
-
-    return 0;
-}
-
-/* show_progress <fraction> <duration>
- *
- * Use <fraction> of the on-screen progress meter for the next operation,
- * automatically advancing the meter over <duration> seconds (or more rapidly
- * if the actual rate of progress can be determined).
- */
-static int
-cmd_show_progress(const char *name, void *cookie, int argc, const char *argv[],
-        PermissionRequestList *permissions)
-{
-    UNUSED(cookie);
-    CHECK_WORDS();
-
-    if (argc != 2) {
-        LOGE("Command %s requires exactly two arguments\n", name);
-        return 1;
-    }
-
-    char *end;
-    double fraction = strtod(argv[0], &end);
-    if (end[0] != '\0' || argv[0][0] == '\0' || fraction < 0 || fraction > 1) {
-        LOGE("Command %s: invalid fraction \"%s\"\n", name, argv[0]);
-        return 1;
-    }
-
-    int duration = strtoul(argv[1], &end, 0);
-    if (end[0] != '\0' || argv[0][0] == '\0') {
-        LOGE("Command %s: invalid duration \"%s\"\n", name, argv[1]);
-        return 1;
-    }
-
-    // Half of the progress bar is taken by verification,
-    // so everything that happens during installation is scaled.
-    ui_show_progress(fraction * (1 - VERIFICATION_PROGRESS_FRACTION), duration);
-    gDidShowProgress = 1;
-    return 0;
-}
-
-/* symlink <link-target> <link-path>
- *
- * Create a symlink, like "ln -s".  The link path must not exist already.
- * Note that <link-path> is in root:path format, but <link-target> is
- * for the target filesystem (and may be relative).
- */
-static int
-cmd_symlink(const char *name, void *cookie, int argc, const char *argv[],
-        PermissionRequestList *permissions)
-{
-    UNUSED(cookie);
-    CHECK_WORDS();
-
-    if (argc != 2) {
-        LOGE("Command %s requires exactly two arguments\n", name);
-        return 1;
-    }
-
-    char path[PATH_MAX];
-    if (translate_root_path(argv[1], path, sizeof(path)) == NULL) {
-        LOGE("Command %s: bad path \"%s\"\n", name, argv[1]);
-        return 1;
-    }
-
-    if (ensure_root_path_mounted(argv[1])) {
-        LOGE("Can't mount %s\n", argv[1]);
-        return 1;
-    }
-
-    if (symlink(argv[0], path)) {
-        LOGE("Can't symlink %s\n", path);
-        return 1;
-    }
-
-    return 0;
-}
-
-struct FirmwareContext {
-    size_t total_bytes, done_bytes;
-    char *data;
-};
-
-static bool firmware_fn(const unsigned char *data, int data_len, void *cookie)
-{
-    struct FirmwareContext *context = (struct FirmwareContext*) cookie;
-    if (context->done_bytes + data_len > context->total_bytes) {
-        LOGE("Data overrun in firmware\n");
-        return false;  // Should not happen, but let's be safe.
-    }
-
-    memcpy(context->data + context->done_bytes, data, data_len);
-    context->done_bytes += data_len;
-    ui_set_progress(context->done_bytes * 1.0 / context->total_bytes);
-    return true;
-}
-
-/* write_radio_image <src-image>
- * write_hboot_image <src-image>
- * Doesn't actually take effect until the rest of installation finishes.
- */
-static int
-cmd_write_firmware_image(const char *name, void *cookie,
-        int argc, const char *argv[], PermissionRequestList *permissions)
-{
-    UNUSED(cookie);
-    CHECK_WORDS();
-
-    if (argc != 1) {
-        LOGE("Command %s requires exactly one argument\n", name);
-        return 1;
-    }
-
-    const char *type;
-    if (!strcmp(name, "write_radio_image")) {
-        type = "radio";
-    } else if (!strcmp(name, "write_hboot_image")) {
-        type = "hboot";
-    } else {
-        LOGE("Unknown firmware update command %s\n", name);
-        return 1;
-    }
-
-    if (!is_package_root_path(argv[0])) {
-        LOGE("Command %s: non-package image file \"%s\" not supported\n",
-                name, argv[0]);
-        return 1;
-    }
-
-    ui_print("Extracting %s image...\n", type);
-    char path[PATH_MAX];
-    const ZipArchive *package;
-    if (!translate_package_root_path(argv[0], path, sizeof(path), &package)) {
-        LOGE("Command %s: bad source path \"%s\"\n", name, argv[0]);
-        return 1;
-    }
-
-    const ZipEntry *entry = mzFindZipEntry(package, path);
-    if (entry == NULL) {
-        LOGE("Can't find %s\n", path);
-        return 1;
-    }
-
-    // Load the update image into RAM.
-    struct FirmwareContext context;
-    context.total_bytes = mzGetZipEntryUncompLen(entry);
-    context.done_bytes = 0;
-    context.data = malloc(context.total_bytes);
-    if (context.data == NULL) {
-        LOGE("Can't allocate %d bytes for %s\n", context.total_bytes, argv[0]);
-        return 1;
-    }
-
-    if (!mzProcessZipEntryContents(package, entry, firmware_fn, &context) ||
-        context.done_bytes != context.total_bytes) {
-        LOGE("Can't read %s\n", argv[0]);
-        free(context.data);
-        return 1;
-    }
-
-#ifndef BOARD_HAS_NO_MISC_PARTITION
-    if (remember_firmware_update(type, context.data, context.total_bytes)) {
-        LOGE("Can't store %s image\n", type);
-        free(context.data);
-        return 1;
-    }
-#endif
-
-    return 0;
-}
-
-static bool write_raw_image_process_fn(
-        const unsigned char *data,
-        int data_len, void *ctx)
-{
-    int r = mtd_write_data((MtdWriteContext*)ctx, (const char *)data, data_len);
-    if (r == data_len) return true;
-    LOGE("%s\n", strerror(errno));
-    return false;
-}
-
-/* write_raw_image <src-image> <dest-root>
- */
-static int
-cmd_write_raw_image(const char *name, void *cookie,
-        int argc, const char *argv[], PermissionRequestList *permissions)
-{
-    if (device_flash_type() != MTD) {
-        LOGE("Board does not support mtd utils.");
-        return -1;
-    }
-    
-    UNUSED(cookie);
-    CHECK_WORDS();
-//xxx permissions
-
-    if (argc != 2) {
-        LOGE("Command %s requires exactly two arguments\n", name);
-        return 1;
-    }
-
-    // Use 10% of the progress bar (20% post-verification) by default
-    const char *src_root_path = argv[0];
-    const char *dst_root_path = argv[1];
-    ui_print("Writing %s...\n", dst_root_path);
-    if (!gDidShowProgress) ui_show_progress(DEFAULT_IMAGE_PROGRESS_FRACTION, 0);
-
-    /* Find the source image, which is probably in a package.
-     */
-    if (!is_package_root_path(src_root_path)) {
-        LOGE("Command %s: non-package source path \"%s\" not yet supported\n",
-                name, src_root_path);
-        return 255;
-    }
-
-    /* Get the package.
-     */
-    char srcpathbuf[PATH_MAX];
-    const char *src_path;
-    const ZipArchive *package;
-    src_path = translate_package_root_path(src_root_path,
-            srcpathbuf, sizeof(srcpathbuf), &package);
-    if (src_path == NULL) {
-        LOGE("Command %s: bad source path \"%s\"\n", name, src_root_path);
-        return 1;
-    }
-
-    /* Get the entry.
-     */
-    const ZipEntry *entry = mzFindZipEntry(package, src_path);
-    if (entry == NULL) {
-        LOGE("Missing file %s\n", src_path);
-        return 1;
-    }
-
-    /* Unmount the destination root if it isn't already.
-     */
-    int ret = ensure_root_path_unmounted(dst_root_path);
-    if (ret < 0) {
-        LOGE("Can't unmount %s\n", dst_root_path);
-        return 1;
-    }
-
-    /* Open the partition for writing.
-     */
-    const MtdPartition *partition = get_root_mtd_partition(dst_root_path);
-    if (partition == NULL) {
-        LOGE("Can't find %s\n", dst_root_path);
-        return 1;
-    }
-    MtdWriteContext *context = mtd_write_partition(partition);
-    if (context == NULL) {
-        LOGE("Can't open %s\n", dst_root_path);
-        return 1;
-    }
-
-    /* Extract and write the image.
-     */
-    bool ok = mzProcessZipEntryContents(package, entry,
-            write_raw_image_process_fn, context);
-    if (!ok) {
-        LOGE("Error writing %s\n", dst_root_path);
-        mtd_write_close(context);
-        return 1;
-    }
-
-    if (mtd_erase_blocks(context, -1) == (off_t) -1) {
-        LOGE("Error finishing %s\n", dst_root_path);
-        mtd_write_close(context);
-        return -1;
-    }
-
-    if (mtd_write_close(context)) {
-        LOGE("Error closing %s\n", dst_root_path);
-        return -1;
-    }
-    return 0;
-}
-
-/* mark <resource> dirty|clean
- */
-static int
-cmd_mark(const char *name, void *cookie, int argc, const char *argv[],
-        PermissionRequestList *permissions)
-{
-    UNUSED(name);
-    UNUSED(cookie);
-    CHECK_WORDS();
-//xxx when marking, save the top-level hash at the mark point
-//    so we can retry on failure.  Otherwise the hashes won't match,
-//    or someone could intentionally dirty the FS to force a downgrade
-//xxx
-    return -1;
-}
-
-/* done
- */
-static int
-cmd_done(const char *name, void *cookie, int argc, const char *argv[],
-        PermissionRequestList *permissions)
-{
-    UNUSED(name);
-    UNUSED(cookie);
-    CHECK_WORDS();
-//xxx
-    return -1;
-}
-
-
-static int
-cmd_backup_rom(const char *name, void *cookie, int argc, const char *argv[],
-        PermissionRequestList *permissions)
-{
-    UNUSED(cookie);
-    CHECK_WORDS();
-
-    char* backup_name = NULL;
-    char backup_path[PATH_MAX];
-    switch(argc)
-    {
-        case 0:
-            {
-                char backup_path[PATH_MAX];
-                nandroid_generate_timestamp_path(backup_path);
-                backup_name = backup_path;
-            }
-            break;
-        case 1:
-            backup_name = argv[0];
-            break;
-        default:
-            LOGE("Command %s requires zero or one argument\n", name);
-            return 1;
-    }
-
-    return nandroid_backup(backup_name);
-}
-
-static int
-cmd_restore_rom(const char *name, void *cookie, int argc, const char *argv[],
-        PermissionRequestList *permissions)
-{
-    UNUSED(cookie);
-    CHECK_WORDS();
-
-    int restoreboot = 1;
-    int restoresystem = 1;
-    int restoredata = 1;
-    int restorecache = 1;
-    int restoresdext = 1;
-    int i;
-    for (i = 0; i < argc; i++)
-    {
-        if (strcmp(argv[i], "noboot") == 0)
-            restoreboot = 0;
-        else if (strcmp(argv[i], "nosystem") == 0)
-            restoresystem = 0;
-        else if (strcmp(argv[i], "nodata") == 0)
-            restoredata = 0;
-        else if (strcmp(argv[i], "nocache") == 0)
-            restorecache = 0;
-        else if (strcmp(argv[i], "nosd-ext") == 0)
-            restorecache = 0;
-    }
-
-    return nandroid_restore(argv[0], restoreboot, restoresystem, restoredata, restorecache, restoresdext);
-}
-
-static int
-cmd_sleep(const char *name, void *cookie, int argc, const char *argv[],
-            PermissionRequestList *permissions)
-{
-    UNUSED(cookie);
-    CHECK_WORDS();
-
-    if (argc != 1) {
-        LOGE("Command %s requires exactly one argument\n", name);
-        return 1;
-    }
-    
-    int seconds = atoi(argv[0]);
-    sleep(seconds);
-
-    return 0;
-}
-
-static int
-cmd_print(const char *name, void *cookie, int argc, const char *argv[],
-            PermissionRequestList *permissions)
-{
-    UNUSED(cookie);
-    CHECK_WORDS();
-    
-    char message[1024];
-    message[0] = NULL;
-    int i;
-    for (i = 0; i < argc; i++)
-    {
-        strcat(message, argv[i]);
-        strcat(message, " ");
-    }
-    strcat(message, "\n");
-    
-    ui_print(message);
-    return 0;
-}
-
-static int
-cmd_install_zip(const char *name, void *cookie, int argc, const char *argv[],
-            PermissionRequestList *permissions)
-{
-    UNUSED(cookie);
-    CHECK_WORDS();
-    
-    if (argc != 1) {
-        LOGE("Command %s requires exactly one argument\n", name);
-        return 1;
-    }
-
-    return install_zip(argv[0]);
-}
-
-/*
- * Function definitions
- */
-
-/* compatible_with(<version>)
- *
- * Returns "true" if this version of the script parser and command
- * set supports the named version.
- */
-static int
-fn_compatible_with(const char *name, void *cookie, int argc, const char *argv[],
-        char **result, size_t *resultLen,
-        PermissionRequestList *permissions)
-{
-    UNUSED(name);
-    UNUSED(cookie);
-    CHECK_FN();
-    NO_PERMS(permissions);
-
-    if (argc != 1) {
-        fprintf(stderr, "%s: wrong number of arguments (%d)\n",
-                name, argc);
-        return 1;
-    }
-
-    if (!strcmp(argv[0], "0.1") || !strcmp(argv[0], "0.2")) {
-        *result = strdup("true");
-    } else {
-        *result = strdup("");
-    }
-    if (resultLen != NULL) {
-        *resultLen = strlen(*result);
-    }
-    return 0;
-}
-
-/* update_forced()
- *
- * Returns "true" if some system setting has determined that
- * the update should happen no matter what.
- */
-static int
-fn_update_forced(const char *name, void *cookie, int argc, const char *argv[],
-        char **result, size_t *resultLen,
-        PermissionRequestList *permissions)
-{
-    UNUSED(name);
-    UNUSED(cookie);
-    CHECK_FN();
-    NO_PERMS(permissions);
-
-    if (argc != 0) {
-        fprintf(stderr, "%s: wrong number of arguments (%d)\n",
-                name, argc);
-        return 1;
-    }
-
-    //xxx check some global or property
-    bool force = true;
-    if (force) {
-        *result = strdup("true");
-    } else {
-        *result = strdup("");
-    }
-    if (resultLen != NULL) {
-        *resultLen = strlen(*result);
-    }
-
-    return 0;
-}
-
-/* get_mark(<resource>)
- *
- * Returns the current mark associated with the provided resource.
- */
-static int
-fn_get_mark(const char *name, void *cookie, int argc, const char *argv[],
-        char **result, size_t *resultLen,
-        PermissionRequestList *permissions)
-{
-    UNUSED(name);
-    UNUSED(cookie);
-    CHECK_FN();
-    NO_PERMS(permissions);
-
-    if (argc != 1) {
-        fprintf(stderr, "%s: wrong number of arguments (%d)\n",
-                name, argc);
-        return 1;
-    }
-
-    //xxx look up the value
-    *result = strdup("");
-    if (resultLen != NULL) {
-        *resultLen = strlen(*result);
-    }
-
-    return 0;
-}
-
-/* hash_dir(<path-to-directory>)
- */
-static int
-fn_hash_dir(const char *name, void *cookie, int argc, const char *argv[],
-        char **result, size_t *resultLen,
-        PermissionRequestList *permissions)
-{
-    int ret = -1;
-
-    UNUSED(name);
-    UNUSED(cookie);
-    CHECK_FN();
-
-    const char *dir;
-    if (argc != 1) {
-        fprintf(stderr, "%s: wrong number of arguments (%d)\n",
-                name, argc);
-        return 1;
-    } else {
-        dir = argv[0];
-    }
-
-    if (permissions != NULL) {
-        if (dir == NULL) {
-            /* The argument is the result of another function.
-             * Assume the worst case, where the function returns
-             * the root.
-             */
-            dir = "/";
-        }
-        ret = addPermissionRequestToList(permissions, dir, true, PERM_READ);
-    } else {
-//xxx build and return the string
-        *result = strdup("hashvalue");
-        if (resultLen != NULL) {
-            *resultLen = strlen(*result);
-        }
-        ret = 0;
-    }
-
-    return ret;
-}
-
-/* matches(<str>, <str1> [, <strN>...])
- * If <str> matches (strcmp) any of <str1>...<strN>, returns <str>,
- * otherwise returns "".
- *
- * E.g., assert matches(hash_dir("/path"), "hash1", "hash2")
- */
-static int
-fn_matches(const char *name, void *cookie, int argc, const char *argv[],
-        char **result, size_t *resultLen,
-        PermissionRequestList *permissions)
-{
-    UNUSED(name);
-    UNUSED(cookie);
-    CHECK_FN();
-    NO_PERMS(permissions);
-
-    if (argc < 2) {
-        fprintf(stderr, "%s: not enough arguments (%d < 2)\n",
-                name, argc);
-        return 1;
-    }
-
-    int i;
-    for (i = 1; i < argc; i++) {
-        if (strcmp(argv[0], argv[i]) == 0) {
-            *result = strdup(argv[0]);
-            if (resultLen != NULL) {
-                *resultLen = strlen(*result);
-            }
-            return 0;
-        }
-    }
-
-    *result = strdup("");
-    if (resultLen != NULL) {
-        *resultLen = 1;
-    }
-    return 0;
-}
-
-/* concat(<str>, <str1> [, <strN>...])
- * Returns the concatenation of all strings.
- */
-static int
-fn_concat(const char *name, void *cookie, int argc, const char *argv[],
-        char **result, size_t *resultLen,
-        PermissionRequestList *permissions)
-{
-    UNUSED(name);
-    UNUSED(cookie);
-    CHECK_FN();
-    NO_PERMS(permissions);
-
-    size_t totalLen = 0;
-    int i;
-    for (i = 0; i < argc; i++) {
-        totalLen += strlen(argv[i]);
-    }
-
-    char *s = (char *)malloc(totalLen + 1);
-    if (s == NULL) {
-        return -1;
-    }
-    s[totalLen] = '\0';
-    for (i = 0; i < argc; i++) {
-        //TODO: keep track of the end to avoid walking the string each time
-        strcat(s, argv[i]);
-    }
-    *result = s;
-    if (resultLen != NULL) {
-        *resultLen = strlen(s);
-    }
-
-    return 0;
-}
-
-/* getprop(<property>)
- * Returns the named Android system property value, or "" if not set.
- */
-static int
-fn_getprop(const char *name, void *cookie, int argc, const char *argv[],
-        char **result, size_t *resultLen,
-        PermissionRequestList *permissions)
-{
-    UNUSED(cookie);
-    CHECK_FN();
-    NO_PERMS(permissions);
-
-    if (argc != 1) {
-        LOGE("Command %s requires exactly one argument\n", name);
-        return 1;
-    }
-
-    char value[PROPERTY_VALUE_MAX];
-    property_get(argv[0], value, "");
-
-    *result = strdup(value);
-    if (resultLen != NULL) {
-        *resultLen = strlen(*result);
-    }
-
-    return 0;
-}
-
-/* file_contains(<filename>, <substring>)
- * Returns "true" if the file exists and contains the specified substring.
- */
-static int
-fn_file_contains(const char *name, void *cookie, int argc, const char *argv[],
-        char **result, size_t *resultLen,
-        PermissionRequestList *permissions)
-{
-    UNUSED(cookie);
-    CHECK_FN();
-    NO_PERMS(permissions);
-
-    if (argc != 2) {
-        LOGE("Command %s requires exactly two arguments\n", name);
-        return 1;
-    }
-
-    char pathbuf[PATH_MAX];
-    const char *root_path = argv[0];
-    const char *path = translate_root_path(root_path, pathbuf, sizeof(pathbuf));
-    if (path == NULL) {
-        LOGE("Command %s: bad path \"%s\"\n", name, root_path);
-        return 1;
-    }
-
-    if (ensure_root_path_mounted(root_path)) {
-        LOGE("Can't mount %s\n", root_path);
-        return 1;
-    }
-
-    const char *needle = argv[1];
-    char *haystack = (char*) load_file(path, NULL);
-    if (haystack == NULL) {
-        LOGI("%s: Can't read \"%s\" (%s)\n", name, path, strerror(errno));
-        *result = "";  /* File not found is not an error. */
-    } else if (strstr(haystack, needle) == NULL) {
-        LOGI("%s: Can't find \"%s\" in \"%s\"\n", name, needle, path);
-        *result = strdup("");
-        free(haystack);
-    } else {
-        *result = strdup("true");
-        free(haystack);
-    }
-
-    if (resultLen != NULL) {
-        *resultLen = strlen(*result);
-    }
-    return 0;
-}
-
-int
-register_update_commands(RecoveryCommandContext *ctx)
-{
-    int ret;
-
-    ret = commandInit();
-    if (ret < 0) return ret;
-
-    /*
-     * Commands
-     */
-
-    ret = registerCommand("assert", CMD_ARGS_BOOLEAN, cmd_assert, (void *)ctx);
-    if (ret < 0) return ret;
-
-    ret = registerCommand("delete", CMD_ARGS_WORDS, cmd_delete, (void *)ctx);
-    if (ret < 0) return ret;
-
-    ret = registerCommand("delete_recursive", CMD_ARGS_WORDS, cmd_delete,
-            (void *)ctx);
-    if (ret < 0) return ret;
-
-    ret = registerCommand("copy_dir", CMD_ARGS_WORDS,
-            cmd_copy_dir, (void *)ctx);
-    if (ret < 0) return ret;
-
-    ret = registerCommand("run_program", CMD_ARGS_WORDS,
-            cmd_run_program, (void *)ctx);
-    if (ret < 0) return ret;
-
-    ret = registerCommand("set_perm", CMD_ARGS_WORDS,
-            cmd_set_perm, (void *)ctx);
-    if (ret < 0) return ret;
-
-    ret = registerCommand("set_perm_recursive", CMD_ARGS_WORDS,
-            cmd_set_perm, (void *)ctx);
-    if (ret < 0) return ret;
-
-    ret = registerCommand("show_progress", CMD_ARGS_WORDS,
-            cmd_show_progress, (void *)ctx);
-    if (ret < 0) return ret;
-
-    ret = registerCommand("symlink", CMD_ARGS_WORDS, cmd_symlink, (void *)ctx);
-    if (ret < 0) return ret;
-
-    ret = registerCommand("format", CMD_ARGS_WORDS, cmd_format, (void *)ctx);
-    if (ret < 0) return ret;
-
-    ret = registerCommand("write_radio_image", CMD_ARGS_WORDS,
-            cmd_write_firmware_image, (void *)ctx);
-    if (ret < 0) return ret;
-
-    ret = registerCommand("write_hboot_image", CMD_ARGS_WORDS,
-            cmd_write_firmware_image, (void *)ctx);
-    if (ret < 0) return ret;
-
-    ret = registerCommand("write_raw_image", CMD_ARGS_WORDS,
-            cmd_write_raw_image, (void *)ctx);
-    if (ret < 0) return ret;
-
-    ret = registerCommand("mark", CMD_ARGS_WORDS, cmd_mark, (void *)ctx);
-    if (ret < 0) return ret;
-
-    ret = registerCommand("done", CMD_ARGS_WORDS, cmd_done, (void *)ctx);
-    if (ret < 0) return ret;
-
-    ret = registerCommand("backup_rom", CMD_ARGS_WORDS, cmd_backup_rom, (void *)ctx);
-    if (ret < 0) return ret;
-
-    ret = registerCommand("restore_rom", CMD_ARGS_WORDS, cmd_restore_rom, (void *)ctx);
-    if (ret < 0) return ret;
-
-    ret = registerCommand("sleep", CMD_ARGS_WORDS, cmd_sleep, (void *)ctx);
-    if (ret < 0) return ret;
-
-    ret = registerCommand("print", CMD_ARGS_WORDS, cmd_print, (void *)ctx);
-    if (ret < 0) return ret;
-
-    ret = registerCommand("install_zip", CMD_ARGS_WORDS, cmd_install_zip, (void *)ctx);
-    if (ret < 0) return ret;
-
-    /*
-     * Functions
-     */
-
-    ret = registerFunction("compatible_with", fn_compatible_with, (void *)ctx);
-    if (ret < 0) return ret;
-
-    ret = registerFunction("update_forced", fn_update_forced, (void *)ctx);
-    if (ret < 0) return ret;
-
-    ret = registerFunction("get_mark", fn_get_mark, (void *)ctx);
-    if (ret < 0) return ret;
-
-    ret = registerFunction("hash_dir", fn_hash_dir, (void *)ctx);
-    if (ret < 0) return ret;
-
-    ret = registerFunction("matches", fn_matches, (void *)ctx);
-    if (ret < 0) return ret;
-
-    ret = registerFunction("concat", fn_concat, (void *)ctx);
-    if (ret < 0) return ret;
-
-    ret = registerFunction("getprop", fn_getprop, (void *)ctx);
-    if (ret < 0) return ret;
-
-    ret = registerFunction("file_contains", fn_file_contains, (void *)ctx);
-    if (ret < 0) return ret;
-
-    return 0;
-}
diff --git a/commands.h b/commands.h
deleted file mode 100644
index e9acea2..0000000
--- a/commands.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2007 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 RECOVERY_COMMANDS_H_
-#define RECOVERY_COMMANDS_H_
-
-#include "minzip/Zip.h"
-
-typedef struct {
-    ZipArchive *package;
-} RecoveryCommandContext;
-
-int register_update_commands(RecoveryCommandContext *ctx);
-
-#endif  // RECOVERY_COMMANDS_H_
diff --git a/common.h b/common.h
index 6c60d07..df26597 100644
--- a/common.h
+++ b/common.h
@@ -26,12 +26,13 @@
 int ui_wait_key();            // waits for a key/button press, returns the code
 int ui_key_pressed(int key);  // returns >0 if the code is currently pressed
 int ui_text_visible();        // returns >0 if text log is currently visible
+void ui_show_text(int visible);
 void ui_clear_key_queue();
 
 // Write a message to the on-screen log shown with Alt-L (also to stderr).
 // The screen is small, and users may need to report these messages to support,
 // so keep the output short and not too cryptic.
-void ui_print(const char *fmt, ...);
+void ui_print(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
 
 void ui_reset_text_col();
 void ui_set_show_text(int value);
@@ -39,7 +40,7 @@
 // Display some header text followed by a menu of items, which appears
 // at the top of the screen (in place of any scrolling ui_print()
 // output, if necessary).
-int ui_start_menu(char** headers, char** items);
+int ui_start_menu(char** headers, char** items, int initial_selection);
 // Set the menu highlight to the given index, and return it (capped to
 // the range [0..numitems).
 int ui_menu_select(int sel);
@@ -85,12 +86,12 @@
 void ui_reset_progress();
 
 #define LOGE(...) ui_print("E:" __VA_ARGS__)
-#define LOGW(...) fprintf(stderr, "W:" __VA_ARGS__)
-#define LOGI(...) fprintf(stderr, "I:" __VA_ARGS__)
+#define LOGW(...) fprintf(stdout, "W:" __VA_ARGS__)
+#define LOGI(...) fprintf(stdout, "I:" __VA_ARGS__)
 
 #if 0
-#define LOGV(...) fprintf(stderr, "V:" __VA_ARGS__)
-#define LOGD(...) fprintf(stderr, "D:" __VA_ARGS__)
+#define LOGV(...) fprintf(stdout, "V:" __VA_ARGS__)
+#define LOGD(...) fprintf(stdout, "D:" __VA_ARGS__)
 #else
 #define LOGV(...) do {} while (0)
 #define LOGD(...) do {} while (0)
@@ -99,4 +100,17 @@
 #define STRINGIFY(x) #x
 #define EXPAND(x) STRINGIFY(x)
 
+typedef struct {
+    const char* mount_point;  // eg. "/cache".  must live in the root directory.
+
+    const char* fs_type;      // "yaffs2" or "ext4" or "vfat"
+
+    const char* device;       // MTD partition name if fs_type == "yaffs"
+                              // block device if fs_type == "ext4" or "vfat"
+
+    const char* device2;      // alternative device to try if fs_type
+                              // == "ext4" or "vfat" and mounting
+                              // 'device' fails
+} Volume;
+
 #endif  // RECOVERY_COMMON_H
diff --git a/default_recovery_ui.c b/default_recovery_ui.c
index e93e95f..9c192a2 100644
--- a/default_recovery_ui.c
+++ b/default_recovery_ui.c
@@ -23,7 +23,7 @@
 char* MENU_HEADERS[] = { NULL };
 
 char* MENU_ITEMS[] = { "reboot system now",
-                       "apply sdcard:update.zip",
+                       "apply update from sdcard",
                        "wipe data/factory reset",
                        "wipe cache partition",
                        "install zip from sdcard",
diff --git a/encryptedfs_provisioning.c b/encryptedfs_provisioning.c
new file mode 100644
index 0000000..678c09f
--- /dev/null
+++ b/encryptedfs_provisioning.c
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) 2009 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 <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "encryptedfs_provisioning.h"
+#include "cutils/misc.h"
+#include "cutils/properties.h"
+#include "common.h"
+#include "mtdutils/mtdutils.h"
+#include "mounts.h"
+#include "roots.h"
+
+const char* encrypted_fs_enabled_property      = "persist.security.secfs.enabled";
+const char* encrypted_fs_property_dir          = "/data/property/";
+const char* encrypted_fs_system_dir            = "/data/system/";
+const char* encrypted_fs_key_file_name         = "/data/fs_key.dat";
+const char* encrypted_fs_salt_file_name        = "/data/hash_salt.dat";
+const char* encrypted_fs_hash_file_src_name    = "/data/system/password.key";
+const char* encrypted_fs_hash_file_dst_name    = "/data/hash.dat";
+const char* encrypted_fs_entropy_file_src_name = "/data/system/entropy.dat";
+const char* encrypted_fs_entropy_file_dst_name = "/data/ported_entropy.dat";
+
+void get_property_file_name(char *buffer, const char *property_name) {
+    sprintf(buffer, "%s%s", encrypted_fs_property_dir, property_name);
+}
+
+int get_binary_file_contents(char *buffer, int buf_size, const char *file_name, int *out_size) {
+    FILE *in_file;
+    int read_bytes;
+
+    in_file = fopen(file_name, "r");
+    if (in_file == NULL) {
+        LOGE("Secure FS: error accessing key file.");
+        return ENCRYPTED_FS_ERROR;
+    }
+
+    read_bytes = fread(buffer, 1, buf_size, in_file);
+    if (out_size == NULL) {
+        if (read_bytes != buf_size) {
+            // Error or unexpected data
+            fclose(in_file);
+            LOGE("Secure FS: error reading conmplete key.");
+            return ENCRYPTED_FS_ERROR;
+        }
+    } else {
+        *out_size = read_bytes;
+    }
+    fclose(in_file);
+    return ENCRYPTED_FS_OK;
+}
+
+int set_binary_file_contents(char *buffer, int buf_size, const char *file_name) {
+    FILE *out_file;
+    int write_bytes;
+
+    out_file = fopen(file_name, "w");
+    if (out_file == NULL) {
+        LOGE("Secure FS: error setting up key file.");
+        return ENCRYPTED_FS_ERROR;
+    }
+
+    write_bytes = fwrite(buffer, 1, buf_size, out_file);
+    if (write_bytes != buf_size) {
+        // Error or unexpected data
+        fclose(out_file);
+        LOGE("Secure FS: error reading conmplete key.");
+        return ENCRYPTED_FS_ERROR;
+    }
+
+    fclose(out_file);
+    return ENCRYPTED_FS_OK;
+}
+
+int get_text_file_contents(char *buffer, int buf_size, char *file_name) {
+    FILE *in_file;
+    char *read_data;
+
+    in_file = fopen(file_name, "r");
+    if (in_file == NULL) {
+        LOGE("Secure FS: error accessing properties.");
+        return ENCRYPTED_FS_ERROR;
+    }
+
+    read_data = fgets(buffer, buf_size, in_file);
+    if (read_data == NULL) {
+        // Error or unexpected data
+        fclose(in_file);
+        LOGE("Secure FS: error accessing properties.");
+        return ENCRYPTED_FS_ERROR;
+    }
+
+    fclose(in_file);
+    return ENCRYPTED_FS_OK;
+}
+
+int set_text_file_contents(char *buffer, char *file_name) {
+    FILE *out_file;
+    int result;
+
+    out_file = fopen(file_name, "w");
+    if (out_file == NULL) {
+        LOGE("Secure FS: error setting up properties.");
+        return ENCRYPTED_FS_ERROR;
+    }
+
+    result = fputs(buffer, out_file);
+    if (result != 0) {
+        // Error or unexpected data
+        fclose(out_file);
+        LOGE("Secure FS: error setting up properties.");
+        return ENCRYPTED_FS_ERROR;
+    }
+
+    fflush(out_file);
+    fclose(out_file);
+    return ENCRYPTED_FS_OK;
+}
+
+int read_encrypted_fs_boolean_property(const char *prop_name, int *value) {
+    char prop_file_name[PROPERTY_KEY_MAX + 32];
+    char prop_value[PROPERTY_VALUE_MAX];
+    int result;
+
+    get_property_file_name(prop_file_name, prop_name);
+    result = get_text_file_contents(prop_value, PROPERTY_VALUE_MAX, prop_file_name);
+
+    if (result < 0) {
+        return result;
+    }
+
+    if (strncmp(prop_value, "1", 1) == 0) {
+        *value = 1;
+    } else if (strncmp(prop_value, "0", 1) == 0) {
+        *value = 0;
+    } else {
+        LOGE("Secure FS: error accessing properties.");
+        return ENCRYPTED_FS_ERROR;
+    }
+
+    return ENCRYPTED_FS_OK;
+}
+
+int write_encrypted_fs_boolean_property(const char *prop_name, int value) {
+    char prop_file_name[PROPERTY_KEY_MAX + 32];
+    char prop_value[PROPERTY_VALUE_MAX];
+    int result;
+
+    get_property_file_name(prop_file_name, prop_name);
+
+    // Create the directory if needed
+    mkdir(encrypted_fs_property_dir, 0755);
+    if (value == 1) {
+        result = set_text_file_contents("1", prop_file_name);
+    } else if (value == 0) {
+        result = set_text_file_contents("0", prop_file_name);
+    } else {
+        return ENCRYPTED_FS_ERROR;
+    }
+    if (result < 0) {
+        return result;
+    }
+
+    return ENCRYPTED_FS_OK;
+}
+
+int read_encrypted_fs_info(encrypted_fs_info *encrypted_fs_data) {
+    int result;
+    int value;
+    result = ensure_path_mounted("/data");
+    if (result != 0) {
+        LOGE("Secure FS: error mounting userdata partition.");
+        return ENCRYPTED_FS_ERROR;
+    }
+
+    // Read the pre-generated encrypted FS key, password hash and salt.
+    result = get_binary_file_contents(encrypted_fs_data->key, ENCRYPTED_FS_KEY_SIZE,
+            encrypted_fs_key_file_name, NULL);
+    if (result != 0) {
+        LOGE("Secure FS: error reading generated file system key.");
+        return ENCRYPTED_FS_ERROR;
+    }
+
+    result = get_binary_file_contents(encrypted_fs_data->salt, ENCRYPTED_FS_SALT_SIZE,
+            encrypted_fs_salt_file_name, &(encrypted_fs_data->salt_length));
+    if (result != 0) {
+        LOGE("Secure FS: error reading file system salt.");
+        return ENCRYPTED_FS_ERROR;
+    }
+
+    result = get_binary_file_contents(encrypted_fs_data->hash, ENCRYPTED_FS_MAX_HASH_SIZE,
+            encrypted_fs_hash_file_src_name, &(encrypted_fs_data->hash_length));
+    if (result != 0) {
+        LOGE("Secure FS: error reading password hash.");
+        return ENCRYPTED_FS_ERROR;
+    }
+
+    result = get_binary_file_contents(encrypted_fs_data->entropy, ENTROPY_MAX_SIZE,
+            encrypted_fs_entropy_file_src_name, &(encrypted_fs_data->entropy_length));
+    if (result != 0) {
+        LOGE("Secure FS: error reading ported entropy.");
+        return ENCRYPTED_FS_ERROR;
+    }
+
+    result = ensure_path_unmounted("/data");
+    if (result != 0) {
+        LOGE("Secure FS: error unmounting data partition.");
+        return ENCRYPTED_FS_ERROR;
+    }
+
+    return ENCRYPTED_FS_OK;
+}
+
+int restore_encrypted_fs_info(encrypted_fs_info *encrypted_fs_data) {
+    int result;
+    result = ensure_path_mounted("/data");
+    if (result != 0) {
+        LOGE("Secure FS: error mounting userdata partition.");
+        return ENCRYPTED_FS_ERROR;
+    }
+
+    // Write the pre-generated secure FS key, password hash and salt.
+    result = set_binary_file_contents(encrypted_fs_data->key, ENCRYPTED_FS_KEY_SIZE,
+            encrypted_fs_key_file_name);
+    if (result != 0) {
+        LOGE("Secure FS: error writing generated file system key.");
+        return ENCRYPTED_FS_ERROR;
+    }
+
+    result = set_binary_file_contents(encrypted_fs_data->salt, encrypted_fs_data->salt_length,
+        encrypted_fs_salt_file_name);
+    if (result != 0) {
+        LOGE("Secure FS: error writing file system salt.");
+        return ENCRYPTED_FS_ERROR;
+    }
+
+    result = set_binary_file_contents(encrypted_fs_data->hash, encrypted_fs_data->hash_length,
+            encrypted_fs_hash_file_dst_name);
+    if (result != 0) {
+        LOGE("Secure FS: error writing password hash.");
+        return ENCRYPTED_FS_ERROR;
+    }
+
+    result = set_binary_file_contents(encrypted_fs_data->entropy, encrypted_fs_data->entropy_length,
+            encrypted_fs_entropy_file_dst_name);
+    if (result != 0) {
+        LOGE("Secure FS: error writing ported entropy.");
+        return ENCRYPTED_FS_ERROR;
+    }
+
+    // Set the secure FS properties to their respective values
+    result = write_encrypted_fs_boolean_property(encrypted_fs_enabled_property, encrypted_fs_data->mode);
+    if (result != 0) {
+        return result;
+    }
+
+    result = ensure_path_unmounted("/data");
+    if (result != 0) {
+        LOGE("Secure FS: error unmounting data partition.");
+        return ENCRYPTED_FS_ERROR;
+    }
+
+    return ENCRYPTED_FS_OK;
+}
diff --git a/encryptedfs_provisioning.h b/encryptedfs_provisioning.h
new file mode 100644
index 0000000..284605d
--- /dev/null
+++ b/encryptedfs_provisioning.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2009 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>
+
+#ifndef __ENCRYPTEDFS_PROVISIONING_H__
+#define __ENCRYPTEDFS_PROVISIONING_H__
+
+#define MODE_ENCRYPTED_FS_DISABLED    0
+#define MODE_ENCRYPTED_FS_ENABLED     1
+
+#define ENCRYPTED_FS_OK               0
+#define ENCRYPTED_FS_ERROR          (-1)
+
+#define ENCRYPTED_FS_KEY_SIZE        16
+#define ENCRYPTED_FS_SALT_SIZE       16
+#define ENCRYPTED_FS_MAX_HASH_SIZE  128
+#define ENTROPY_MAX_SIZE        4096
+
+struct encrypted_fs_info {
+    int mode;
+    char key[ENCRYPTED_FS_KEY_SIZE];
+    char salt[ENCRYPTED_FS_SALT_SIZE];
+    int salt_length;
+    char hash[ENCRYPTED_FS_MAX_HASH_SIZE];
+    int hash_length;
+    char entropy[ENTROPY_MAX_SIZE];
+    int entropy_length;
+};
+
+typedef struct encrypted_fs_info encrypted_fs_info;
+
+int read_encrypted_fs_info(encrypted_fs_info *secure_fs_data);
+
+int restore_encrypted_fs_info(encrypted_fs_info *secure_data);
+
+#endif /* __ENCRYPTEDFS_PROVISIONING_H__ */
+
diff --git a/etc/init.rc b/etc/init.rc
index 56c6542..e6b43e0 100644
--- a/etc/init.rc
+++ b/etc/init.rc
@@ -1,3 +1,5 @@
+on early-init
+    start ueventd
 
 on init
     export PATH /sbin
@@ -22,10 +24,13 @@
 
     class_start default
 
+service ueventd /sbin/ueventd
+    critical
 
 service recovery /sbin/recovery
 
 service adbd /sbin/adbd recovery
+    disabled
 
 on property:persist.service.adb.enable=1
     start adbd
diff --git a/extendedcommands.c b/extendedcommands.c
index 3d47341..df22448 100644
--- a/extendedcommands.c
+++ b/extendedcommands.c
@@ -30,18 +30,16 @@
 #include "roots.h"
 #include "recovery_ui.h"
 
-#include "commands.h"
-#include "amend/amend.h"
-
 #include "../../external/yaffs2/yaffs2/utils/mkyaffs2image.h"
 #include "../../external/yaffs2/yaffs2/utils/unyaffs.h"
 
 #include "extendedcommands.h"
 #include "nandroid.h"
+#include "mounts.h"
 
 int signature_check_enabled = 1;
 int script_assert_enabled = 1;
-static const char *SDCARD_PACKAGE_FILE = "SDCARD:update.zip";
+static const char *SDCARD_UPDATE_FILE = "/sdcard/update.zip";
 
 void
 toggle_signature_check()
@@ -69,17 +67,12 @@
         ui_print("Installation aborted.\n");
         return 1;
     }
-#ifndef BOARD_HAS_NO_MISC_PARTITION
-    if (firmware_update_pending()) {
-        ui_print("\nReboot via menu to complete\ninstallation.\n");
-    }
-#endif
     ui_set_background(BACKGROUND_ICON_NONE);
     ui_print("\nInstall from sdcard complete.\n");
     return 0;
 }
 
-char* INSTALL_MENU_ITEMS[] = {  "apply sdcard:update.zip",
+char* INSTALL_MENU_ITEMS[] = {  "apply /sdcard/update.zip",
                                 "choose zip from sdcard",
                                 "toggle signature verification",
                                 "toggle script asserts",
@@ -97,7 +90,7 @@
     };
     for (;;)
     {
-        int chosen_item = get_menu_selection(headers, INSTALL_MENU_ITEMS, 0);
+        int chosen_item = get_menu_selection(headers, INSTALL_MENU_ITEMS, 0, 0);
         switch (chosen_item)
         {
             case ITEM_ASSERTS:
@@ -109,7 +102,7 @@
             case ITEM_APPLY_SDCARD:
             {
                 if (confirm_selection("Confirm install?", "Yes - Install /sdcard/update.zip"))
-                    install_zip(SDCARD_PACKAGE_FILE);
+                    install_zip(SDCARD_UPDATE_FILE);
                 break;
             }
             case ITEM_CHOOSE_ZIP:
@@ -276,7 +269,7 @@
 
         for (;;)
         {
-            int chosen_item = get_menu_selection(headers, list, 0);
+            int chosen_item = get_menu_selection(headers, list, 0, 0);
             if (chosen_item == GO_BACK)
                 break;
             static char ret[PATH_MAX];
@@ -305,7 +298,7 @@
 
 void show_choose_zip_menu()
 {
-    if (ensure_root_path_mounted("SDCARD:") != 0) {
+    if (ensure_path_mounted("/sdcard") != 0) {
         LOGE ("Can't mount /sdcard\n");
         return;
     }
@@ -318,19 +311,16 @@
     char* file = choose_file_menu("/sdcard/", ".zip", headers);
     if (file == NULL)
         return;
-    char sdcard_package_file[1024];
-    strcpy(sdcard_package_file, "SDCARD:");
-    strcat(sdcard_package_file,  file + strlen("/sdcard/"));
     static char* confirm_install  = "Confirm install?";
     static char confirm[PATH_MAX];
     sprintf(confirm, "Yes - Install %s", basename(file));
     if (confirm_selection(confirm_install, confirm))
-        install_zip(sdcard_package_file);
+        install_zip(file);
 }
 
 void show_nandroid_restore_menu()
 {
-    if (ensure_root_path_mounted("SDCARD:") != 0) {
+    if (ensure_path_mounted("/sdcard") != 0) {
         LOGE ("Can't mount /sdcard\n");
         return;
     }
@@ -351,7 +341,8 @@
 void show_mount_usb_storage_menu()
 {
     char command[PATH_MAX];
-    sprintf(command, "echo %s > /sys/devices/platform/usb_mass_storage/lun0/file", BOARD_SDCARD_DEVICE_PRIMARY);
+    Volume *vol = volume_for_path("/sdcard");
+    sprintf(command, "echo %s > /sys/devices/platform/usb_mass_storage/lun0/file", vol->device);
     __system(command);
     static char* headers[] = {  "USB Mass Storage device",
                                 "Leaving this menu unmount",
@@ -364,7 +355,7 @@
 
     for (;;)
     {
-        int chosen_item = get_menu_selection(headers, list, 0);
+        int chosen_item = get_menu_selection(headers, list, 0, 0);
         if (chosen_item == GO_BACK || chosen_item == 0)
             break;
     }
@@ -393,26 +384,25 @@
                       "No",
                       NULL };
 
-    int chosen_item = get_menu_selection(confirm_headers, items, 0);
+    int chosen_item = get_menu_selection(confirm_headers, items, 0, 0);
     return chosen_item == 7;
 }
 
-int format_unknown_device(const char* root)
+int format_unknown_device(const char* path)
 {
     // if this is SDEXT:, don't worry about it.
-    if (0 == strcmp(root, "SDEXT:"))
+    if (0 == strcmp(path, "/sd-ext"))
     {
         struct stat st;
-        if (0 != stat(BOARD_SDEXT_DEVICE, &st))
+        Volume *vol = volume_for_path("/sd-ext");
+        if (vol == NULL || 0 != stat(vol->device, &st))
         {
             ui_print("No app2sd partition found. Skipping format of /sd-ext.\n");
             return 0;
         }
     }
 
-    char path[PATH_MAX];
-    translate_root_path(root, path, PATH_MAX);
-    if (0 != ensure_root_path_mounted(root))
+    if (0 != ensure_path_mounted(path))
     {
         ui_print("Error mounting %s!\n", path);
         ui_print("Skipping format...\n");
@@ -425,13 +415,13 @@
     sprintf(tmp, "rm -rf %s/.*", path);
     __system(tmp);
 
-    ensure_root_path_unmounted(root);
+    ensure_path_unmounted(path);
     return 0;
 }
 
-#define MOUNTABLE_COUNT 5
-#define MTD_COUNT 4
-#define MMC_COUNT 2
+//#define MOUNTABLE_COUNT 5
+//#define DEVICE_COUNT 4
+//#define MMC_COUNT 2
 
 void show_partition_menu()
 {
@@ -441,32 +431,32 @@
     };
 
     typedef char* string;
-    string mounts[MOUNTABLE_COUNT][3] = {
-        { "mount /system", "unmount /system", "SYSTEM:" },
-        { "mount /data", "unmount /data", "DATA:" },
-        { "mount /cache", "unmount /cache", "CACHE:" },
-        { "mount /sdcard", "unmount /sdcard", "SDCARD:" },
+    string mounts[][3] = {
+        { "mount /system", "unmount /system", "/system" },
+        { "mount /data", "unmount /data", "/data" },
+        { "mount /cache", "unmount /cache", "/cache" },
+        { "mount /sdcard", "unmount /sdcard", "/sdcard:" },
 #ifdef BOARD_HAS_SDCARD_INTERNAL
-        { "mount /emmc", "unmount /emmc", "SDINTERNAL:" },
+        { "mount /emmc", "unmount /emmc", "/emmc" },
 #endif
-        { "mount /sd-ext", "unmount /sd-ext", "SDEXT:" }
-        };
-
-    string mtds[MTD_COUNT][2] = {
-        { "format boot", "BOOT:" },
-        { "format system", "SYSTEM:" },
-        { "format data", "DATA:" },
-        { "format cache", "CACHE:" },
+        { "mount /sd-ext", "unmount /sd-ext", "/sd-ext" }
     };
 
-    string mmcs[MMC_COUNT][3] = {
-      { "format sdcard", "SDCARD:" },
+    string devices[][2] = {
+        { "format boot", "boot:" },
+        { "format system", "/system" },
+        { "format data", "/data" },
+        { "format cache", "/cache" },
+        { "format sdcard", "/sdcard" },
+        { "format sd-ext", "/sd-ext" },
 #ifdef BOARD_HAS_SDCARD_INTERNAL
-      { "format internal sdcard", "SDINTERNAL:" },
+        { "format internal sdcard", "/emmc" }
 #endif
-      { "format sd-ext", "SDEXT:" }
     };
 
+    const int MOUNTABLE_COUNT = sizeof(mounts) / sizeof(string) / 3;
+    const int DEVICE_COUNT = sizeof(devices) / sizeof(string) / 2;
+
     static char* confirm_format  = "Confirm format?";
     static char* confirm = "Yes - Format";
 
@@ -474,30 +464,25 @@
     {
         int ismounted[MOUNTABLE_COUNT];
         int i;
-        static string options[MOUNTABLE_COUNT + MTD_COUNT + MMC_COUNT + 1 + 1]; // mountables, format mtds, format mmcs, usb storage, null
+        static string options[MOUNTABLE_COUNT + DEVICE_COUNT + 1 + 1]; // mountables, format mtds, format mmcs, usb storage, null
         for (i = 0; i < MOUNTABLE_COUNT; i++)
         {
-            ismounted[i] = is_root_path_mounted(mounts[i][2]);
+            ismounted[i] = is_path_mounted(mounts[i][2]);
             options[i] = ismounted[i] ? mounts[i][1] : mounts[i][0];
         }
 
-        for (i = 0; i < MTD_COUNT; i++)
+        for (i = 0; i < DEVICE_COUNT; i++)
         {
-            options[MOUNTABLE_COUNT + i] = mtds[i][0];
+            options[DEVICE_COUNT + i] = devices[i][0];
         }
 
-        for (i = 0; i < MMC_COUNT; i++)
-        {
-            options[MOUNTABLE_COUNT + MTD_COUNT + i] = mmcs[i][0];
-        }
+        options[MOUNTABLE_COUNT + DEVICE_COUNT] = "mount USB storage";
+        options[MOUNTABLE_COUNT + DEVICE_COUNT + 1] = NULL;
 
-        options[MOUNTABLE_COUNT + MTD_COUNT + MMC_COUNT] = "mount USB storage";
-        options[MOUNTABLE_COUNT + MTD_COUNT + MMC_COUNT + 1] = NULL;
-
-        int chosen_item = get_menu_selection(headers, options, 0);
+        int chosen_item = get_menu_selection(headers, options, 0, 0);
         if (chosen_item == GO_BACK)
             break;
-        if (chosen_item == MOUNTABLE_COUNT + MTD_COUNT + MMC_COUNT)
+        if (chosen_item == MOUNTABLE_COUNT + DEVICE_COUNT)
         {
             show_mount_usb_storage_menu();
         }
@@ -505,34 +490,23 @@
         {
             if (ismounted[chosen_item])
             {
-                if (0 != ensure_root_path_unmounted(mounts[chosen_item][2]))
+                if (0 != ensure_path_unmounted(mounts[chosen_item][2]))
                     ui_print("Error unmounting %s!\n", mounts[chosen_item][2]);
             }
             else
             {
-                if (0 != ensure_root_path_mounted(mounts[chosen_item][2]))
+                if (0 != ensure_path_mounted(mounts[chosen_item][2]))
                     ui_print("Error mounting %s!\n", mounts[chosen_item][2]);
             }
         }
-        else if (chosen_item < MOUNTABLE_COUNT + MTD_COUNT)
+        else if (chosen_item < MOUNTABLE_COUNT + DEVICE_COUNT)
         {
             chosen_item = chosen_item - MOUNTABLE_COUNT;
             if (!confirm_selection(confirm_format, confirm))
                 continue;
-            ui_print("Formatting %s...\n", mtds[chosen_item][1]);
-            if (0 != format_root_device(mtds[chosen_item][1]))
-                ui_print("Error formatting %s!\n", mtds[chosen_item][1]);
-            else
-                ui_print("Done.\n");
-        }
-        else if (chosen_item < MOUNTABLE_COUNT + MTD_COUNT + MMC_COUNT)
-        {
-            chosen_item = chosen_item - MOUNTABLE_COUNT - MTD_COUNT;
-            if (!confirm_selection(confirm_format, confirm))
-                continue;
-            ui_print("Formatting %s...\n", mmcs[chosen_item][1]);
-            if (0 != format_unknown_device(mmcs[chosen_item][1]))
-                ui_print("Error formatting %s!\n", mmcs[chosen_item][1]);
+            ui_print("Formatting %s...\n", devices[chosen_item][1]);
+            if (0 != format_device(devices[chosen_item][1]))
+                ui_print("Error formatting %s!\n", devices[chosen_item][1]);
             else
                 ui_print("Done.\n");
         }
@@ -549,8 +523,10 @@
 
 int run_script_from_buffer(char* script_data, int script_len, char* filename)
 {
-    /* Parse the script.  Note that the script and parse tree are never freed.
-     */
+    ui_print("not yet implemented.\n");
+    return -1;
+
+    /*
     const AmCommandList *commands = parseAmendScript(script_data, script_len);
     if (commands == NULL) {
         printf("Syntax error in update script\n");
@@ -559,8 +535,6 @@
         printf("Parsed %.*s\n", script_len, filename);
     }
 
-    /* Execute the script.
-     */
     int ret = execCommandList((ExecContext *)1, commands);
     if (ret != 0) {
         int num = ret;
@@ -573,8 +547,7 @@
         printf("Failure at line %d:\n%s\n", num, next ? line : "(not found)");
         return 1;
     }
-
-    return 0;
+    */
 }
 
 int run_script(char* filename)
@@ -609,7 +582,7 @@
     int i = 0;
     for (i = 20; i > 0; i--) {
         ui_print("Waiting for SD Card to mount (%ds)\n", i);
-        if (ensure_root_path_mounted("SDCARD:") == 0) {
+        if (ensure_path_mounted("/sdcard") == 0) {
             ui_print("SD Card mounted...\n");
             break;
         }
@@ -624,24 +597,9 @@
     return run_script(tmp);
 }
 
-int amend_main(int argc, char** argv)
-{
-    if (argc != 2)
-    {
-        printf("Usage: amend <script>\n");
-        return 0;
-    }
-
-    RecoveryCommandContext ctx = { NULL };
-    if (register_update_commands(&ctx)) {
-        LOGE("Can't install update commands\n");
-    }
-    return run_script(argv[1]);
-}
-
 void show_nandroid_advanced_restore_menu()
 {
-    if (ensure_root_path_mounted("SDCARD:") != 0) {
+    if (ensure_path_mounted("/sdcard") != 0) {
         LOGE ("Can't mount /sdcard\n");
         return;
     }
@@ -675,7 +633,7 @@
 
     static char* confirm_restore  = "Confirm restore?";
 
-    int chosen_item = get_menu_selection(headers, list, 0);
+    int chosen_item = get_menu_selection(headers, list, 0, 0);
     switch (chosen_item)
     {
         case 0:
@@ -714,7 +672,7 @@
                             NULL
     };
 
-    int chosen_item = get_menu_selection(headers, list, 0);
+    int chosen_item = get_menu_selection(headers, list, 0, 0);
     switch (chosen_item)
     {
         case 0:
@@ -746,9 +704,9 @@
 
 void wipe_battery_stats()
 {
-    ensure_root_path_mounted("DATA:");
+    ensure_path_mounted("/data");
     remove("/data/system/batterystats.bin");
-    ensure_root_path_unmounted("DATA:");
+    ensure_path_unmounted("/data");
 }
 
 void show_advanced_menu()
@@ -775,7 +733,7 @@
 
     for (;;)
     {
-        int chosen_item = get_menu_selection(headers, list, 0);
+        int chosen_item = get_menu_selection(headers, list, 0, 0);
         if (chosen_item == GO_BACK)
             break;
         switch (chosen_item)
@@ -785,16 +743,16 @@
                 break;
             case 1:
             {
-                if (0 != ensure_root_path_mounted("DATA:"))
+                if (0 != ensure_path_mounted("/data"))
                     break;
-                ensure_root_path_mounted("SDEXT:");
-                ensure_root_path_mounted("CACHE:");
+                ensure_path_mounted("/sd-ext");
+                ensure_path_mounted("/cache");
                 if (confirm_selection( "Confirm wipe?", "Yes - Wipe Dalvik Cache")) {
                     __system("rm -r /data/dalvik-cache");
                     __system("rm -r /cache/dalvik-cache");
                     __system("rm -r /sd-ext/dalvik-cache");
                 }
-                ensure_root_path_unmounted("DATA:");
+                ensure_path_unmounted("/data");
                 ui_print("Dalvik Cache wiped.\n");
                 break;
             }
@@ -842,17 +800,17 @@
                 static char* ext_headers[] = { "Ext Size", "", NULL };
                 static char* swap_headers[] = { "Swap Size", "", NULL };
 
-                int ext_size = get_menu_selection(ext_headers, ext_sizes, 0);
+                int ext_size = get_menu_selection(ext_headers, ext_sizes, 0, 0);
                 if (ext_size == GO_BACK)
                     continue;
 
-                int swap_size = get_menu_selection(swap_headers, swap_sizes, 0);
+                int swap_size = get_menu_selection(swap_headers, swap_sizes, 0, 0);
                 if (swap_size == GO_BACK)
                     continue;
 
                 char sddevice[256];
-                const RootInfo *ri = get_root_info_for_path("SDCARD:");
-                strcpy(sddevice, ri->device);
+                Volume *vol = volume_for_path("/sdcard");
+                strcpy(sddevice, vol->device);
                 // we only want the mmcblk, not the partition
                 sddevice[strlen("/dev/block/mmcblkX")] = NULL;
                 char cmd[PATH_MAX];
@@ -867,8 +825,8 @@
             }
             case 6:
             {
-                ensure_root_path_mounted("SYSTEM:");
-                ensure_root_path_mounted("DATA:");
+                ensure_path_mounted("/system");
+                ensure_path_mounted("/data");
                 ui_print("Fixing permissions...\n");
                 __system("fix_permissions");
                 ui_print("Done!\n");
@@ -894,7 +852,7 @@
                 static char* ext_headers[] = { "Data Size", "", NULL };
                 static char* swap_headers[] = { "Swap Size", "", NULL };
 
-                int ext_size = get_menu_selection(ext_headers, ext_sizes, 0);
+                int ext_size = get_menu_selection(ext_headers, ext_sizes, 0, 0);
                 if (ext_size == GO_BACK)
                     continue;
 
@@ -903,8 +861,8 @@
                     continue;
 
                 char sddevice[256];
-                const RootInfo *ri = get_root_info_for_path("SDINTERNAL:");
-                strcpy(sddevice, ri->device);
+                Volume *vol = volume_for_path("/emmc");
+                strcpy(sddevice, vol->device);
                 // we only want the mmcblk, not the partition
                 sddevice[strlen("/dev/block/mmcblkX")] = NULL;
                 char cmd[PATH_MAX];
@@ -921,6 +879,7 @@
     }
 }
 
+/*
 void write_fstab_root(char *root_path, FILE *file)
 {
     RootInfo *info = get_root_info_for_path(root_path);
@@ -961,14 +920,53 @@
     write_fstab_root("SDEXT:", file);
     fclose(file);
 }
+*/
 
 void handle_failure(int ret)
 {
     if (ret == 0)
         return;
-    if (0 != ensure_root_path_mounted("SDCARD:"))
+    if (0 != ensure_path_mounted("/sdcard"))
         return;
     mkdir("/sdcard/clockworkmod", S_IRWXU);
     __system("cp /tmp/recovery.log /sdcard/clockworkmod/recovery.log");
     ui_print("/tmp/recovery.log was copied to /sdcard/clockworkmod/recovery.log. Please open ROM Manager to report the issue.\n");
 }
+
+int format_device(const char* device) {
+    if (device == NULL)
+        return -1;
+    if (device[0] == '/') {
+        Volume *vol = volume_for_path(device);
+        if (vol == NULL)
+            return -1;
+        return erase_partition(device, vol->fs_type);
+    }
+    return erase_raw_partition(device);
+}
+
+int is_path_mounted(const char* path) {
+    Volume* v = volume_for_path(path);
+    if (v == NULL) {
+        return 0;
+    }
+    if (strcmp(v->fs_type, "ramdisk") == 0) {
+        // the ramdisk is always mounted.
+        return 1;
+    }
+
+    int result;
+    result = scan_mounted_volumes();
+    if (result < 0) {
+        LOGE("failed to scan mounted volumes\n");
+        return 0;
+    }
+
+    const MountedVolume* mv =
+        find_mounted_volume_by_mount_point(v->mount_point);
+    if (mv) {
+        // volume is already mounted
+        return 1;
+    }
+    return 0;
+}
diff --git a/firmware.c b/firmware.c
deleted file mode 100644
index e2e4fe6..0000000
--- a/firmware.c
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Copyright (C) 2008 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 "bootloader.h"
-#include "common.h"
-#include "firmware.h"
-#include "roots.h"
-
-#include <errno.h>
-#include <string.h>
-#include <sys/reboot.h>
-
-static const char *update_type = NULL;
-static const char *update_data = NULL;
-static int update_length = 0;
-
-int remember_firmware_update(const char *type, const char *data, int length) {
-    if (update_type != NULL || update_data != NULL) {
-        LOGE("Multiple firmware images\n");
-        return -1;
-    }
-
-    update_type = type;
-    update_data = data;
-    update_length = length;
-    return 0;
-}
-
-// Return true if there is a firmware update pending.
-int firmware_update_pending() {
-  return update_data != NULL && update_length > 0;
-}
-
-/* Bootloader / Recovery Flow
- *
- * On every boot, the bootloader will read the bootloader_message
- * from flash and check the command field.  The bootloader should
- * deal with the command field not having a 0 terminator correctly
- * (so as to not crash if the block is invalid or corrupt).
- *
- * The bootloader will have to publish the partition that contains
- * the bootloader_message to the linux kernel so it can update it.
- *
- * if command == "boot-recovery" -> boot recovery.img
- * else if command == "update-radio" -> update radio image (below)
- * else if command == "update-hboot" -> update hboot image (below)
- * else -> boot boot.img (normal boot)
- *
- * Radio/Hboot Update Flow
- * 1. the bootloader will attempt to load and validate the header
- * 2. if the header is invalid, status="invalid-update", goto #8
- * 3. display the busy image on-screen
- * 4. if the update image is invalid, status="invalid-radio-image", goto #8
- * 5. attempt to update the firmware (depending on the command)
- * 6. if successful, status="okay", goto #8
- * 7. if failed, and the old image can still boot, status="failed-update"
- * 8. write the bootloader_message, leaving the recovery field
- *    unchanged, updating status, and setting command to
- *    "boot-recovery"
- * 9. reboot
- *
- * The bootloader will not modify or erase the cache partition.
- * It is recovery's responsibility to clean up the mess afterwards.
- */
-
-int maybe_install_firmware_update(const char *send_intent) {
-    if (update_data == NULL || update_length == 0) return 0;
-
-    /* We destroy the cache partition to pass the update image to the
-     * bootloader, so all we can really do afterwards is wipe cache and reboot.
-     * Set up this instruction now, in case we're interrupted while writing.
-     */
-
-    struct bootloader_message boot;
-    memset(&boot, 0, sizeof(boot));
-    strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
-    strlcpy(boot.recovery, "recovery\n--wipe_cache\n", sizeof(boot.command));
-    if (send_intent != NULL) {
-        strlcat(boot.recovery, "--send_intent=", sizeof(boot.recovery));
-        strlcat(boot.recovery, send_intent, sizeof(boot.recovery));
-        strlcat(boot.recovery, "\n", sizeof(boot.recovery));
-    }
-    if (set_bootloader_message(&boot)) return -1;
-
-    int width = 0, height = 0, bpp = 0;
-    char *busy_image = ui_copy_image(
-        BACKGROUND_ICON_FIRMWARE_INSTALLING, &width, &height, &bpp);
-    char *fail_image = ui_copy_image(
-        BACKGROUND_ICON_FIRMWARE_ERROR, &width, &height, &bpp);
-
-    ui_print("Writing %s image...\n", update_type);
-    if (write_update_for_bootloader(
-            update_data, update_length,
-            width, height, bpp, busy_image, fail_image)) {
-        LOGE("Can't write %s image\n(%s)\n", update_type, strerror(errno));
-        format_root_device("CACHE:");  // Attempt to clean cache up, at least.
-        return -1;
-    }
-
-    free(busy_image);
-    free(fail_image);
-
-    /* The update image is fully written, so now we can instruct the bootloader
-     * to install it.  (After doing so, it will come back here, and we will
-     * wipe the cache and reboot into the system.)
-     */
-    snprintf(boot.command, sizeof(boot.command), "update-%s", update_type);
-    if (set_bootloader_message(&boot)) {
-        format_root_device("CACHE:");
-        return -1;
-    }
-
-    reboot(RB_AUTOBOOT);
-
-    // Can't reboot?  WTF?
-    LOGE("Can't reboot\n");
-    return -1;
-}
diff --git a/flashutils/Android.mk b/flashutils/Android.mk
index 5f6fea2..324480a 100644
--- a/flashutils/Android.mk
+++ b/flashutils/Android.mk
@@ -6,6 +6,7 @@
 include $(CLEAR_VARS)
 LOCAL_SRC_FILES := flashutils.c
 LOCAL_MODULE := libflashutils
+LOCAL_MODULE_TAGS := eng
 LOCAL_C_INCLUDES += bootable/recovery
 LOCAL_STATIC_LIBRARIES := libmmcutils libmtdutils libbmlutils
 include $(BUILD_STATIC_LIBRARY)
@@ -38,18 +39,21 @@
 include $(CLEAR_VARS)
 LOCAL_SRC_FILES := flash_image.c
 LOCAL_MODULE := libflash_image
+LOCAL_MODULE_TAGS := eng
 LOCAL_CFLAGS += -Dmain=flash_image_main
 include $(BUILD_STATIC_LIBRARY)
 
 include $(CLEAR_VARS)
 LOCAL_SRC_FILES := dump_image.c
 LOCAL_MODULE := libdump_image
+LOCAL_MODULE_TAGS := eng
 LOCAL_CFLAGS += -Dmain=dump_image_main
 include $(BUILD_STATIC_LIBRARY)
 
 include $(CLEAR_VARS)
 LOCAL_SRC_FILES := erase_image.c
 LOCAL_MODULE := liberase_image
+LOCAL_MODULE_TAGS := eng
 LOCAL_CFLAGS += -Dmain=erase_image_main
 include $(BUILD_STATIC_LIBRARY)
 
@@ -57,6 +61,7 @@
 include $(CLEAR_VARS)
 LOCAL_SRC_FILES := dump_image.c
 LOCAL_MODULE := utility_dump_image
+LOCAL_MODULE_TAGS := eng
 LOCAL_MODULE_CLASS := UTILITY_EXECUTABLES
 LOCAL_MODULE_PATH := $(PRODUCT_OUT)/utilities
 LOCAL_UNSTRIPPED_PATH := $(PRODUCT_OUT)/symbols/utilities
@@ -68,6 +73,7 @@
 include $(CLEAR_VARS)
 LOCAL_SRC_FILES := flash_image.c
 LOCAL_MODULE := utility_flash_image
+LOCAL_MODULE_TAGS := eng
 LOCAL_MODULE_CLASS := UTILITY_EXECUTABLES
 LOCAL_MODULE_PATH := $(PRODUCT_OUT)/utilities
 LOCAL_UNSTRIPPED_PATH := $(PRODUCT_OUT)/symbols/utilities
@@ -79,6 +85,7 @@
 include $(CLEAR_VARS)
 LOCAL_SRC_FILES := erase_image.c
 LOCAL_MODULE := utility_erase_image
+LOCAL_MODULE_TAGS := eng
 LOCAL_MODULE_CLASS := UTILITY_EXECUTABLES
 LOCAL_MODULE_PATH := $(PRODUCT_OUT)/utilities
 LOCAL_UNSTRIPPED_PATH := $(PRODUCT_OUT)/symbols/utilities
diff --git a/install.c b/install.c
index 8b319a0..2d8a4cd 100644
--- a/install.c
+++ b/install.c
@@ -34,7 +34,6 @@
 #include "verifier.h"
 
 #include "firmware.h"
-#include "legacy.h"
 
 #include "extendedcommands.h"
 
@@ -92,13 +91,6 @@
         fclose(f);
     }
 
-#ifndef BOARD_HAS_NO_MISC_PARTITION
-    if (remember_firmware_update(type, data, data_size)) {
-        LOGE("Can't store %s image\n", type);
-        free(data);
-        return INSTALL_ERROR;
-    }
-#endif
     free(filename);
 
     return INSTALL_SUCCESS;
@@ -110,6 +102,7 @@
     const ZipEntry* binary_entry =
             mzFindZipEntry(zip, ASSUMED_UPDATE_BINARY_NAME);
     if (binary_entry == NULL) {
+        mzCloseZipArchive(zip);
         return INSTALL_UPDATE_BINARY_MISSING;
     }
 
@@ -117,11 +110,13 @@
     unlink(binary);
     int fd = creat(binary, 0755);
     if (fd < 0) {
+        mzCloseZipArchive(zip);
         LOGE("Can't make %s\n", binary);
         return 1;
     }
     bool ok = mzExtractZipEntryToFile(zip, binary_entry, fd);
     close(fd);
+    mzCloseZipArchive(zip);
 
     if (!ok) {
         LOGE("Can't copy %s\n", ASSUMED_UPDATE_BINARY_NAME);
@@ -177,7 +172,7 @@
     if (pid == 0) {
         close(pipefd[0]);
         execv(binary, args);
-        fprintf(stderr, "E:Can't run %s (%s)\n", binary, strerror(errno));
+        fprintf(stdout, "E:Can't run %s (%s)\n", binary, strerror(errno));
         _exit(-1);
     }
     close(pipefd[1]);
@@ -219,7 +214,7 @@
         } else if (strcmp(command, "ui_print") == 0) {
             char* str = strtok(NULL, "\n");
             if (str) {
-                ui_print(str);
+                ui_print("%s", str);
             } else {
                 ui_print("\n");
             }
@@ -244,34 +239,6 @@
     return INSTALL_SUCCESS;
 }
 
-static int
-handle_update_package(const char *path, ZipArchive *zip)
-{
-    // Update should take the rest of the progress bar.
-    ui_print("Installing update...\n");
-
-    LOGI("Trying update-binary.\n");
-    int result = try_update_binary(path, zip);
-
-    if (result == INSTALL_UPDATE_BINARY_MISSING)
-    {
-        register_package_root(NULL, NULL);  // Unregister package root
-        if (register_package_root(zip, path) < 0) {
-            LOGE("Can't register package root\n");
-            return INSTALL_ERROR;
-        }
-        const ZipEntry *script_entry;
-        script_entry = find_update_script(zip);
-        LOGI("Trying update-script.\n");
-        result = handle_update_script(zip, script_entry);
-        if (result == INSTALL_UPDATE_SCRIPT_MISSING)
-            result = INSTALL_ERROR;
-    }
-    
-    register_package_root(NULL, NULL);  // Unregister package root
-    return result;
-}
-
 // Reads a file containing one or more public keys as produced by
 // DumpPublicKey:  this is an RSAPublicKey struct as it would appear
 // as a C source literal, eg:
@@ -347,26 +314,19 @@
 }
 
 int
-install_package(const char *root_path)
+install_package(const char *path)
 {
     ui_set_background(BACKGROUND_ICON_INSTALLING);
     ui_print("Finding update package...\n");
     ui_show_indeterminate_progress();
-    LOGI("Update location: %s\n", root_path);
+    LOGI("Update location: %s\n", path);
 
-    if (ensure_root_path_mounted(root_path) != 0) {
-        LOGE("Can't mount %s\n", root_path);
-        return INSTALL_CORRUPT;
-    }
-
-    char path[PATH_MAX] = "";
-    if (translate_root_path(root_path, path, sizeof(path)) == NULL) {
-        LOGE("Bad path %s\n", root_path);
+    if (ensure_path_mounted(path) != 0) {
+        LOGE("Can't mount %s\n", path);
         return INSTALL_CORRUPT;
     }
 
     ui_print("Opening update package...\n");
-    LOGI("Update file path: %s\n", path);
 
     int err;
 
@@ -405,7 +365,6 @@
 
     /* Verify and install the contents of the package.
      */
-    int status = handle_update_package(path, &zip);
-    mzCloseZipArchive(&zip);
-    return status;
+    ui_print("Installing update...\n");
+    return try_update_binary(path, &zip);
 }
diff --git a/legacy.c b/legacy.c
deleted file mode 100644
index bb6f194..0000000
--- a/legacy.c
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright (C) 2007 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 <ctype.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <limits.h>
-#include <sys/stat.h>
-#include <sys/wait.h>
-#include <unistd.h>
-
-#include "common.h"
-#include "install.h"
-#include "mincrypt/rsa.h"
-#include "minui/minui.h"
-#include "minzip/SysUtil.h"
-#include "minzip/Zip.h"
-#include "mtdutils/mtdutils.h"
-#include "roots.h"
-#include "verifier.h"
-#include "firmware.h"
-
-#include "amend/amend.h"
-#include "common.h"
-#include "install.h"
-#include "mincrypt/rsa.h"
-#include "minui/minui.h"
-#include "minzip/SysUtil.h"
-#include "minzip/Zip.h"
-#include "mounts.h"
-#include "mtdutils/mtdutils.h"
-#include "roots.h"
-#include "verifier.h"
-
-static int read_data(ZipArchive *zip, const ZipEntry *entry,
-        char** ppData, int* pLength) {
-    int len = (int)mzGetZipEntryUncompLen(entry);
-    if (len <= 0) {
-        LOGE("Bad data length %d\n", len);
-        return -1;
-    }
-    char *data = malloc(len + 1);
-    if (data == NULL) {
-        LOGE("Can't allocate %d bytes for data\n", len + 1);
-        return -2;
-    }
-    bool ok = mzReadZipEntry(zip, entry, data, len);
-    if (!ok) {
-        LOGE("Error while reading data\n");
-        free(data);
-        return -3;
-    }
-    data[len] = '\0';     // not necessary, but just to be safe
-    *ppData = data;
-    if (pLength) {
-        *pLength = len;
-    }
-    return 0;
-}
-
-int
-handle_update_script(ZipArchive *zip, const ZipEntry *update_script_entry)
-{
-    /* Read the entire script into a buffer.
-     */
-    int script_len;
-    char* script_data;
-    if (read_data(zip, update_script_entry, &script_data, &script_len) < 0) {
-        LOGE("Can't read update script\n");
-        return INSTALL_UPDATE_SCRIPT_MISSING;
-    }
-
-    /* Parse the script.  Note that the script and parse tree are never freed.
-     */
-    const AmCommandList *commands = parseAmendScript(script_data, script_len);
-    if (commands == NULL) {
-        LOGE("Syntax error in update script\n");
-        return INSTALL_ERROR;
-    } else {
-        UnterminatedString name = mzGetZipEntryFileName(update_script_entry);
-        LOGI("Parsed %.*s\n", name.len, name.str);
-    }
-
-    /* Execute the script.
-     */
-    int ret = execCommandList((ExecContext *)1, commands);
-    if (ret != 0) {
-        int num = ret;
-        char *line, *next = script_data;
-        while (next != NULL && ret-- > 0) {
-            line = next;
-            next = memchr(line, '\n', script_data + script_len - line);
-            if (next != NULL) *next++ = '\0';
-        }
-        LOGE("Failure at line %d:\n%s\n", num, next ? line : "(not found)");
-        return INSTALL_ERROR;
-    }
-
-    ui_print("Installation complete.\n");
-    return INSTALL_SUCCESS;
-}
-
-#define ASSUMED_UPDATE_SCRIPT_NAME  "META-INF/com/google/android/update-script"
-
-const ZipEntry *
-find_update_script(ZipArchive *zip)
-{
-//TODO: Get the location of this script from the MANIFEST.MF file
-    return mzFindZipEntry(zip, ASSUMED_UPDATE_SCRIPT_NAME);
-}
diff --git a/legacy.h b/legacy.h
deleted file mode 100644
index a3ca77c..0000000
--- a/legacy.h
+++ /dev/null
@@ -1,5 +0,0 @@
-int
-handle_update_script(ZipArchive *zip, const ZipEntry *update_script_entry);
-
-const ZipEntry *
-find_update_script(ZipArchive *zip);
\ No newline at end of file
diff --git a/minui/events.c b/minui/events.c
index 955ce4d..efdca95 100644
--- a/minui/events.c
+++ b/minui/events.c
@@ -34,6 +34,11 @@
 
 #define PRESS_THRESHHOLD    10
 
+#define ABS_MT_POSITION_X 0x35
+#define ABS_MT_POSITION_Y 0x36
+#define ABS_MT_TOUCH_MAJOR 0x30
+#define SYN_MT_REPORT 2
+
 struct virtualkey {
     int scancode;
     int centerx, centery;
diff --git a/minui/graphics.c b/minui/graphics.c
index bf90484..a96342f 100644
--- a/minui/graphics.c
+++ b/minui/graphics.c
@@ -93,6 +93,7 @@
     fb->stride = fi.line_length/2; /* stride is the number of pixels until the data of next row, >= xres */;
     fb->data = bits;
     fb->format = GGL_PIXEL_FORMAT_RGB_565;
+    memset(fb->data, 0, vi.yres * vi.xres * 2);
 
     fb++;
 
@@ -100,8 +101,9 @@
     fb->width = vi.xres;
     fb->height = vi.yres;
     fb->stride = fi.line_length/2;
-    fb->data = (void*) (((unsigned) bits) + vi.yres * fi.line_length);
+    fb->data = (void*) (((unsigned) bits) + vi.yres * fi.line_length / 2);
     fb->format = GGL_PIXEL_FORMAT_RGB_565;
+    memset(fb->data, 0, vi.yres * vi.xres * 2);
 
     return fd;
 }
@@ -282,7 +284,6 @@
     set_active_framebuffer(0);
     gl->colorBuffer(gl, &gr_mem_surface);
 
-
     gl->activeTexture(gl, 0);
     gl->enable(gl, GGL_BLEND);
     gl->blendFunc(gl, GGL_SRC_ALPHA, GGL_ONE_MINUS_SRC_ALPHA);
diff --git a/mmcutils/Android.mk b/mmcutils/Android.mk
index 36bd515..f1fe294 100644
--- a/mmcutils/Android.mk
+++ b/mmcutils/Android.mk
@@ -8,6 +8,7 @@
 	mmcutils.c
 
 LOCAL_MODULE := libmmcutils
+LOCAL_MODULE_TAGS := eng
 
 include $(BUILD_STATIC_LIBRARY)
 
diff --git a/mounts.c b/mounts.c
index 2ab3ff6..c90fc8a 100644
--- a/mounts.c
+++ b/mounts.c
@@ -212,3 +212,11 @@
     }
     return ret;
 }
+
+int
+remount_read_only(const MountedVolume* volume)
+{
+    return mount(volume->device, volume->mount_point, volume->filesystem,
+                 MS_NOATIME | MS_NODEV | MS_NODIRATIME |
+                 MS_RDONLY | MS_REMOUNT, 0);
+}
diff --git a/mounts.h b/mounts.h
index 2e2765a..30b2927 100644
--- a/mounts.h
+++ b/mounts.h
@@ -28,4 +28,6 @@
 
 int unmount_mounted_volume(const MountedVolume *volume);
 
+int remount_read_only(const MountedVolume* volume);
+
 #endif  // MTDUTILS_MOUNTS_H_
diff --git a/mtdutils/mtdutils.c b/mtdutils/mtdutils.c
index 9fd5c6c..c8dbba4 100644
--- a/mtdutils/mtdutils.c
+++ b/mtdutils/mtdutils.c
@@ -272,6 +272,12 @@
     return ctx;
 }
 
+// Seeks to a location in the partition.  Don't mix with reads of
+// anything other than whole blocks; unpredictable things will result.
+void mtd_read_skip_to(const MtdReadContext* ctx, size_t offset) {
+    lseek64(ctx->fd, offset, SEEK_SET);
+}
+
 static int read_block(const MtdPartition *partition, int fd, char *data)
 {
     struct mtd_ecc_stats before, after;
@@ -296,19 +302,14 @@
             fprintf(stderr, "mtd: ECC errors (%d soft, %d hard) at 0x%08llx\n",
                     after.corrected - before.corrected,
                     after.failed - before.failed, pos);
+            // copy the comparison baseline for the next read.
+            memcpy(&before, &after, sizeof(struct mtd_ecc_stats));
         } else if ((mgbb = ioctl(fd, MEMGETBADBLOCK, &pos))) {
             fprintf(stderr,
                     "mtd: MEMGETBADBLOCK returned %d at 0x%08llx (errno=%d)\n",
                     mgbb, pos, errno);
         } else {
-            int i;
-            for (i = 0; i < size; ++i) {
-                if (data[i] != 0) {
-                    return 0;  // Success!
-                }
-            }
-            fprintf(stderr, "mtd: read all-zero block at 0x%08llx; skipping\n",
-                    pos);
+            return 0;  // Success!
         }
 
         pos += partition->erase_size;
@@ -407,9 +408,12 @@
     ssize_t size = partition->erase_size;
     while (pos + size <= (int) partition->size) {
         loff_t bpos = pos;
-        if (ioctl(fd, MEMGETBADBLOCK, &bpos) > 0) {
+        int ret = ioctl(fd, MEMGETBADBLOCK, &bpos);
+        if (ret != 0 && !(ret == -1 && errno == EOPNOTSUPP)) {
             add_bad_block_offset(ctx, pos);
-            fprintf(stderr, "mtd: not writing bad block at 0x%08lx\n", pos);
+            fprintf(stderr,
+                    "mtd: not writing bad block at 0x%08lx (ret %d errno %d)\n",
+                    pos, ret, errno);
             pos += partition->erase_size;
             continue;  // Don't try to erase known factory-bad blocks.
         }
@@ -446,6 +450,7 @@
             if (retry > 0) {
                 fprintf(stderr, "mtd: wrote block after %d retries\n", retry);
             }
+            fprintf(stderr, "mtd: successfully wrote block at %llx\n", pos);
             return 0;  // Success!
         }
 
diff --git a/mtdutils/mtdutils.h b/mtdutils/mtdutils.h
index b3bad68..c57d45d 100644
--- a/mtdutils/mtdutils.h
+++ b/mtdutils/mtdutils.h
@@ -45,6 +45,7 @@
 MtdReadContext *mtd_read_partition(const MtdPartition *);
 ssize_t mtd_read_data(MtdReadContext *, char *data, size_t data_len);
 void mtd_read_close(MtdReadContext *);
+void mtd_read_skip_to(const MtdReadContext *, size_t offset);
 
 MtdWriteContext *mtd_write_partition(const MtdPartition *);
 ssize_t mtd_write_data(MtdWriteContext *, const char *data, size_t data_len);
diff --git a/nandroid.c b/nandroid.c
index 0b38294..4f7724a 100644
--- a/nandroid.c
+++ b/nandroid.c
@@ -30,9 +30,6 @@
 #include "roots.h"
 #include "recovery_ui.h"
 
-#include "commands.h"
-#include "amend/amend.h"
-
 #include "../../external/yaffs2/yaffs2/utils/mkyaffs2image.h"
 #include "../../external/yaffs2/yaffs2/utils/unyaffs.h"
 
@@ -41,8 +38,8 @@
 #include "extendedcommands.h"
 #include "nandroid.h"
 
-int print_and_error(char* message) {
-    ui_print(message);
+int print_and_error(const char* message) {
+    ui_print("%s", message);
     return 1;
 }
 
@@ -52,7 +49,7 @@
 {
     char* justfile = basename(filename);
     if (strlen(justfile) < 30)
-        ui_print(justfile);
+        ui_print("%s", justfile);
     yaffs_files_count++;
     if (yaffs_files_total != 0)
         ui_set_progress((float)yaffs_files_count / (float)yaffs_files_total);
@@ -74,10 +71,8 @@
     ui_show_progress(1, 0);
 }
 
-int nandroid_backup_partition_extended(const char* backup_path, char* root, int umount_when_finished) {
+int nandroid_backup_partition_extended(const char* backup_path, const char* mount_point, int umount_when_finished) {
     int ret = 0;
-    char mount_point[PATH_MAX];
-    translate_root_path(root, mount_point, PATH_MAX);
     char* name = basename(mount_point);
 
     struct stat file_info;
@@ -87,7 +82,7 @@
     }
     
     ui_print("Backing up %s...\n", name);
-    if (0 != (ret = ensure_root_path_mounted(root) != 0)) {
+    if (0 != (ret = ensure_path_mounted(mount_point) != 0)) {
         ui_print("Can't mount %s!\n", mount_point);
         return ret;
     }
@@ -96,7 +91,7 @@
     sprintf(tmp, "%s/%s.img", backup_path, name);
     ret = mkyaffs2image(mount_point, tmp, 0, callback);
     if (umount_when_finished) {
-        ensure_root_path_unmounted(root);
+        ensure_path_unmounted(mount_point);
     }
     if (0 != ret) {
         ui_print("Error while making a yaffs2 image of %s!\n", mount_point);
@@ -113,7 +108,7 @@
 {
     ui_set_background(BACKGROUND_ICON_INSTALLING);
     
-    if (ensure_root_path_mounted("SDCARD:") != 0)
+    if (ensure_path_mounted("/sdcard") != 0)
         return print_and_error("Can't mount /sdcard\n");
     
     int ret;
@@ -146,14 +141,14 @@
         return print_and_error("Error while dumping recovery image!\n");
 #endif
 
-    if (0 != (ret = nandroid_backup_partition(backup_path, "SYSTEM:")))
+    if (0 != (ret = nandroid_backup_partition(backup_path, "/system")))
         return ret;
 
-    if (0 != (ret = nandroid_backup_partition(backup_path, "DATA:")))
+    if (0 != (ret = nandroid_backup_partition(backup_path, "/data")))
         return ret;
 
 #ifdef BOARD_HAS_DATADATA
-    if (0 != (ret = nandroid_backup_partition(backup_path, "DATADATA:")))
+    if (0 != (ret = nandroid_backup_partition(backup_path, "/datadata")))
         return ret;
 #endif
 
@@ -164,20 +159,21 @@
     }
     else
     {
-        if (0 != (ret = nandroid_backup_partition_extended(backup_path, "SDCARD:/.android_secure", 0)))
+        if (0 != (ret = nandroid_backup_partition_extended(backup_path, "/sdcard/.android_secure", 0)))
             return ret;
     }
 
-    if (0 != (ret = nandroid_backup_partition_extended(backup_path, "CACHE:", 0)))
+    if (0 != (ret = nandroid_backup_partition_extended(backup_path, "/cache", 0)))
         return ret;
 
-    if (0 != stat(BOARD_SDEXT_DEVICE, &st))
+    Volume *vol = volume_for_path("/sd-ext");
+    if (vol == NULL || 0 != stat(vol->device, &st))
     {
         ui_print("No sd-ext found. Skipping backup of sd-ext.\n");
     }
     else
     {
-        if (0 != ensure_root_path_mounted("SDEXT:"))
+        if (0 != ensure_path_mounted("/sd-ext"))
             ui_print("Could not mount sd-ext. sd-ext backup may not be supported on this device. Skipping backup of sd-ext.\n");
         else if (0 != (ret = nandroid_backup_partition(backup_path, "SDEXT:")))
             return ret;
@@ -205,10 +201,8 @@
     __system(tmp);
 }
 
-int nandroid_restore_partition_extended(const char* backup_path, const char* root, int umount_when_finished) {
+int nandroid_restore_partition_extended(const char* backup_path, const char* mount_point, int umount_when_finished) {
     int ret = 0;
-    char mount_point[PATH_MAX];
-    translate_root_path(root, mount_point, PATH_MAX);
     char* name = basename(mount_point);
     
     char tmp[PATH_MAX];
@@ -233,12 +227,12 @@
         return ret;
     }
     */
-    if (0 != (ret = format_root_device(root))) {
-        ui_print("Error while formatting %s!\n", root);
+    if (0 != (ret = format_device(mount_point))) {
+        ui_print("Error while formatting %s!\n", mount_point);
         return ret;
     }
     
-    if (0 != (ret = ensure_root_path_mounted(root))) {
+    if (0 != (ret = ensure_path_mounted(mount_point))) {
         ui_print("Can't mount %s!\n", mount_point);
         return ret;
     }
@@ -249,7 +243,7 @@
     }
 
     if (umount_when_finished) {
-        ensure_root_path_unmounted(root);
+        ensure_path_unmounted(mount_point);
     }
     
     return 0;
@@ -265,7 +259,7 @@
     ui_show_indeterminate_progress();
     yaffs_files_total = 0;
 
-    if (ensure_root_path_mounted("SDCARD:") != 0)
+    if (ensure_path_mounted("/sdcard") != 0)
         return print_and_error("Can't mount /sdcard\n");
     
     char tmp[PATH_MAX];
@@ -280,7 +274,7 @@
     if (restore_boot)
     {
         ui_print("Erasing boot before restore...\n");
-        if (0 != (ret = format_root_device("BOOT:")))
+        if (0 != (ret = format_device("boot")))
             return print_and_error("Error while formatting BOOT:!\n");
         sprintf(tmp, "%s/boot.img", backup_path);
         ui_print("Restoring boot image...\n");
@@ -291,24 +285,24 @@
     }
 #endif
     
-    if (restore_system && 0 != (ret = nandroid_restore_partition(backup_path, "SYSTEM:")))
+    if (restore_system && 0 != (ret = nandroid_restore_partition(backup_path, "/system")))
         return ret;
 
-    if (restore_data && 0 != (ret = nandroid_restore_partition(backup_path, "DATA:")))
+    if (restore_data && 0 != (ret = nandroid_restore_partition(backup_path, "/data")))
         return ret;
         
 #ifdef BOARD_HAS_DATADATA
-    if (restore_data && 0 != (ret = nandroid_restore_partition(backup_path, "DATADATA:")))
+    if (restore_data && 0 != (ret = nandroid_restore_partition(backup_path, "/datadata")))
         return ret;
 #endif
 
-    if (restore_data && 0 != (ret = nandroid_restore_partition_extended(backup_path, "SDCARD:/.android_secure", 0)))
+    if (restore_data && 0 != (ret = nandroid_restore_partition_extended(backup_path, "/sdcard/.android_secure", 0)))
         return ret;
 
-    if (restore_cache && 0 != (ret = nandroid_restore_partition_extended(backup_path, "CACHE:", 0)))
+    if (restore_cache && 0 != (ret = nandroid_restore_partition_extended(backup_path, "/cache", 0)))
         return ret;
 
-    if (restore_sdext && 0 != (ret = nandroid_restore_partition(backup_path, "SDEXT:")))
+    if (restore_sdext && 0 != (ret = nandroid_restore_partition(backup_path, "/sd-ext")))
         return ret;
 
     sync();
diff --git a/recovery.c b/recovery.c
index bda808c..f91edc1 100644
--- a/recovery.c
+++ b/recovery.c
@@ -25,9 +25,11 @@
 #include <stdlib.h>
 #include <string.h>
 #include <sys/reboot.h>
+#include <sys/stat.h>
 #include <sys/types.h>
 #include <time.h>
 #include <unistd.h>
+#include <dirent.h>
 #include <sys/stat.h>
 
 #include "bootloader.h"
@@ -38,25 +40,29 @@
 #include "minzip/DirUtil.h"
 #include "roots.h"
 #include "recovery_ui.h"
+#include "encryptedfs_provisioning.h"
 
 #include "extendedcommands.h"
-#include "commands.h"
 
 static const struct option OPTIONS[] = {
   { "send_intent", required_argument, NULL, 's' },
   { "update_package", required_argument, NULL, 'u' },
   { "wipe_data", no_argument, NULL, 'w' },
   { "wipe_cache", no_argument, NULL, 'c' },
+  { "set_encrypted_filesystems", required_argument, NULL, 'e' },
+  { "show_text", no_argument, NULL, 't' },
   { NULL, 0, NULL, 0 },
 };
 
+static const char *COMMAND_FILE = "/cache/recovery/command";
+static const char *INTENT_FILE = "/cache/recovery/intent";
+static const char *LOG_FILE = "/cache/recovery/log";
+static const char *LAST_LOG_FILE = "/cache/recovery/last_log";
+static const char *SDCARD_ROOT = "/sdcard";
 static int allow_display_toggle = 1;
-
-static const char *COMMAND_FILE = "CACHE:recovery/command";
-static const char *INTENT_FILE = "CACHE:recovery/intent";
-static const char *LOG_FILE = "CACHE:recovery/log";
-static const char *SDCARD_PACKAGE_FILE = "SDCARD:update.zip";
+static const char *SDCARD_PACKAGE_FILE = "/sdcard/update.zip";
 static const char *TEMPORARY_LOG_FILE = "/tmp/recovery.log";
+static const char *SIDELOAD_TEMP_DIR = "/tmp/sideload";
 
 /*
  * The recovery tool communicates with the main system through /cache files.
@@ -66,7 +72,7 @@
  *
  * The arguments which may be supplied in the recovery.command file:
  *   --send_intent=anystring - write the text out to recovery.intent
- *   --update_package=root:path - verify install an OTA package file
+ *   --update_package=path - verify install an OTA package file
  *   --wipe_data - erase user data (and cache), then reboot
  *   --wipe_cache - wipe cache (but not user data), then reboot
  *   --set_encrypted_filesystem=on|off - enables / diasables encrypted fs
@@ -81,15 +87,15 @@
  * 3. main system reboots into recovery
  * 4. get_args() writes BCB with "boot-recovery" and "--wipe_data"
  *    -- after this, rebooting will restart the erase --
- * 5. erase_root() reformats /data
- * 6. erase_root() reformats /cache
+ * 5. erase_volume() reformats /data
+ * 6. erase_volume() reformats /cache
  * 7. finish_recovery() erases BCB
  *    -- after this, rebooting will restart the main system --
  * 8. main() calls reboot() to boot main system
  *
  * OTA INSTALL
  * 1. main system downloads OTA package to /cache/some-filename.zip
- * 2. main system writes "--update_package=CACHE:some-filename.zip"
+ * 2. main system writes "--update_package=/cache/some-filename.zip"
  * 3. main system reboots into recovery
  * 4. get_args() writes BCB with "boot-recovery" and "--update_package=..."
  *    -- after this, rebooting will attempt to reinstall the update --
@@ -110,14 +116,14 @@
  *    8d. bootloader tries to flash firmware
  *    8e. bootloader writes BCB with "boot-recovery" (keeping "--wipe_cache")
  *        -- after this, rebooting will reformat cache & restart main system --
- *    8f. erase_root() reformats /cache
+ *    8f. erase_volume() reformats /cache
  *    8g. finish_recovery() erases BCB
  *        -- after this, rebooting will (try to) restart the main system --
  * 9. main() calls reboot() to boot main system
  *
- * ENCRYPTED FILE SYSTEMS ENABLE/DISABLE
+ * SECURE FILE SYSTEMS ENABLE/DISABLE
  * 1. user selects "enable encrypted file systems"
- * 2. main system writes "--set_encrypted_filesystem=on|off" to
+ * 2. main system writes "--set_encrypted_filesystems=on|off" to
  *    /cache/recovery/command
  * 3. main system reboots into recovery
  * 4. get_args() writes BCB with "boot-recovery" and
@@ -126,8 +132,8 @@
  * 5. read_encrypted_fs_info() retrieves encrypted file systems settings from /data
  *    Settings include: property to specify the Encrypted FS istatus and
  *    FS encryption key if enabled (not yet implemented)
- * 6. erase_root() reformats /data
- * 7. erase_root() reformats /cache
+ * 6. erase_volume() reformats /data
+ * 7. erase_volume() reformats /cache
  * 8. restore_encrypted_fs_info() writes required encrypted file systems settings to /data
  *    Settings include: property to specify the Encrypted FS status and
  *    FS encryption key if enabled (not yet implemented)
@@ -139,17 +145,11 @@
 static const int MAX_ARG_LENGTH = 4096;
 static const int MAX_ARGS = 100;
 
-// open a file given in root:path format, mounting partitions as necessary
+// open a given path, mounting partitions as necessary
 static FILE*
-fopen_root_path(const char *root_path, const char *mode) {
-    if (ensure_root_path_mounted(root_path) != 0) {
-        LOGE("Can't mount %s\n", root_path);
-        return NULL;
-    }
-
-    char path[PATH_MAX] = "";
-    if (translate_root_path(root_path, path, sizeof(path)) == NULL) {
-        LOGE("Bad path %s\n", root_path);
+fopen_path(const char *path, const char *mode) {
+    if (ensure_path_mounted(path) != 0) {
+        LOGE("Can't mount %s\n", path);
         return NULL;
     }
 
@@ -158,7 +158,7 @@
     if (strchr("wa", mode[0])) dirCreateHierarchy(path, 0777, NULL, 1);
 
     FILE *fp = fopen(path, mode);
-    if (fp == NULL && root_path != COMMAND_FILE) LOGE("Can't open %s\n", path);
+    if (fp == NULL && path != COMMAND_FILE) LOGE("Can't open %s\n", path);
     return fp;
 }
 
@@ -211,7 +211,7 @@
 
     // --- if that doesn't work, try the command file
     if (*argc <= 1) {
-        FILE *fp = fopen_root_path(COMMAND_FILE, "r");
+        FILE *fp = fopen_path(COMMAND_FILE, "r");
         if (fp != NULL) {
             char *argv0 = (*argv)[0];
             *argv = (char **) malloc(sizeof(char *) * MAX_ARGS);
@@ -253,6 +253,34 @@
 }
 #endif
 
+// How much of the temp log we have copied to the copy in cache.
+static long tmplog_offset = 0;
+
+static void
+copy_log_file(const char* destination, int append) {
+    FILE *log = fopen_path(destination, append ? "a" : "w");
+    if (log == NULL) {
+        LOGE("Can't open %s\n", destination);
+    } else {
+        FILE *tmplog = fopen(TEMPORARY_LOG_FILE, "r");
+        if (tmplog == NULL) {
+            LOGE("Can't open %s\n", TEMPORARY_LOG_FILE);
+        } else {
+            if (append) {
+                fseek(tmplog, tmplog_offset, SEEK_SET);  // Since last write
+            }
+            char buf[4096];
+            while (fgets(buf, sizeof(buf), tmplog)) fputs(buf, log);
+            if (append) {
+                tmplog_offset = ftell(tmplog);
+            }
+            check_and_fclose(tmplog, TEMPORARY_LOG_FILE);
+        }
+        check_and_fclose(log, destination);
+    }
+}
+
+
 // clear the recovery command and prepare to boot a (hopefully working) system,
 // copy our log file to cache as well (for the system to read), and
 // record any intent we were asked to communicate back to the system.
@@ -261,7 +289,7 @@
 finish_recovery(const char *send_intent) {
     // By this point, we're ready to return to the main system...
     if (send_intent != NULL) {
-        FILE *fp = fopen_root_path(INTENT_FILE, "w");
+        FILE *fp = fopen_path(INTENT_FILE, "w");
         if (fp == NULL) {
             LOGE("Can't open %s\n", INTENT_FILE);
         } else {
@@ -271,23 +299,9 @@
     }
 
     // Copy logs to cache so the system can find out what happened.
-    FILE *log = fopen_root_path(LOG_FILE, "a");
-    if (log == NULL) {
-        LOGE("Can't open %s\n", LOG_FILE);
-    } else {
-        FILE *tmplog = fopen(TEMPORARY_LOG_FILE, "r");
-        if (tmplog == NULL) {
-            LOGE("Can't open %s\n", TEMPORARY_LOG_FILE);
-        } else {
-            static long tmplog_offset = 0;
-            fseek(tmplog, tmplog_offset, SEEK_SET);  // Since last write
-            char buf[4096];
-            while (fgets(buf, sizeof(buf), tmplog)) fputs(buf, log);
-            tmplog_offset = ftell(tmplog);
-            check_and_fclose(tmplog, TEMPORARY_LOG_FILE);
-        }
-        check_and_fclose(log, LOG_FILE);
-    }
+    copy_log_file(LOG_FILE, true);
+    copy_log_file(LAST_LOG_FILE, false);
+    chmod(LAST_LOG_FILE, 0640);
 
 #ifndef BOARD_HAS_NO_MISC_PARTITION
     // Reset to mormal system boot so recovery won't cycle indefinitely.
@@ -297,10 +311,8 @@
 #endif
 
     // Remove the command file, so recovery won't repeat indefinitely.
-    char path[PATH_MAX] = "";
-    if (ensure_root_path_mounted(COMMAND_FILE) != 0 ||
-        translate_root_path(COMMAND_FILE, path, sizeof(path)) == NULL ||
-        (unlink(path) && errno != ENOENT)) {
+    if (ensure_path_mounted(COMMAND_FILE) != 0 ||
+        (unlink(COMMAND_FILE) && errno != ENOENT)) {
         LOGW("Can't unlink %s\n", COMMAND_FILE);
     }
 
@@ -308,11 +320,109 @@
 }
 
 static int
-erase_root(const char *root) {
+erase_volume(const char *volume) {
     ui_set_background(BACKGROUND_ICON_INSTALLING);
     ui_show_indeterminate_progress();
-    ui_print("Formatting %s...\n", root);
-    return format_root_device(root);
+    ui_print("Formatting %s...\n", volume);
+
+    if (strcmp(volume, "/cache") == 0) {
+        // Any part of the log we'd copied to cache is now gone.
+        // Reset the pointer so we copy from the beginning of the temp
+        // log.
+        tmplog_offset = 0;
+    }
+
+    return format_volume(volume);
+}
+
+static char*
+copy_sideloaded_package(const char* original_path) {
+  if (ensure_path_mounted(original_path) != 0) {
+    LOGE("Can't mount %s\n", original_path);
+    return NULL;
+  }
+
+  if (ensure_path_mounted(SIDELOAD_TEMP_DIR) != 0) {
+    LOGE("Can't mount %s\n", SIDELOAD_TEMP_DIR);
+    return NULL;
+  }
+
+  if (mkdir(SIDELOAD_TEMP_DIR, 0700) != 0) {
+    if (errno != EEXIST) {
+      LOGE("Can't mkdir %s (%s)\n", SIDELOAD_TEMP_DIR, strerror(errno));
+      return NULL;
+    }
+  }
+
+  // verify that SIDELOAD_TEMP_DIR is exactly what we expect: a
+  // directory, owned by root, readable and writable only by root.
+  struct stat st;
+  if (stat(SIDELOAD_TEMP_DIR, &st) != 0) {
+    LOGE("failed to stat %s (%s)\n", SIDELOAD_TEMP_DIR, strerror(errno));
+    return NULL;
+  }
+  if (!S_ISDIR(st.st_mode)) {
+    LOGE("%s isn't a directory\n", SIDELOAD_TEMP_DIR);
+    return NULL;
+  }
+  if ((st.st_mode & 0777) != 0700) {
+    LOGE("%s has perms %o\n", SIDELOAD_TEMP_DIR, st.st_mode);
+    return NULL;
+  }
+  if (st.st_uid != 0) {
+    LOGE("%s owned by %lu; not root\n", SIDELOAD_TEMP_DIR, st.st_uid);
+    return NULL;
+  }
+
+  char copy_path[PATH_MAX];
+  strcpy(copy_path, SIDELOAD_TEMP_DIR);
+  strcat(copy_path, "/package.zip");
+
+  char* buffer = malloc(BUFSIZ);
+  if (buffer == NULL) {
+    LOGE("Failed to allocate buffer\n");
+    return NULL;
+  }
+
+  size_t read;
+  FILE* fin = fopen(original_path, "rb");
+  if (fin == NULL) {
+    LOGE("Failed to open %s (%s)\n", original_path, strerror(errno));
+    return NULL;
+  }
+  FILE* fout = fopen(copy_path, "wb");
+  if (fout == NULL) {
+    LOGE("Failed to open %s (%s)\n", copy_path, strerror(errno));
+    return NULL;
+  }
+
+  while ((read = fread(buffer, 1, BUFSIZ, fin)) > 0) {
+    if (fwrite(buffer, 1, read, fout) != read) {
+      LOGE("Short write of %s (%s)\n", copy_path, strerror(errno));
+      return NULL;
+    }
+  }
+
+  free(buffer);
+
+  if (fclose(fout) != 0) {
+    LOGE("Failed to close %s (%s)\n", copy_path, strerror(errno));
+    return NULL;
+  }
+
+  if (fclose(fin) != 0) {
+    LOGE("Failed to close %s (%s)\n", original_path, strerror(errno));
+    return NULL;
+  }
+
+  // "adb push" is happy to overwrite read-only files when it's
+  // running as root, but we'll try anyway.
+  if (chmod(copy_path, 0400) != 0) {
+    LOGE("Failed to chmod %s (%s)\n", copy_path, strerror(errno));
+    return NULL;
+  }
+
+  return strdup(copy_path);
 }
 
 static char**
@@ -338,13 +448,14 @@
 }
 
 int
-get_menu_selection(char** headers, char** items, int menu_only) {
+get_menu_selection(char** headers, char** items, int menu_only,
+                   int initial_selection) {
     // throw away keys pressed previously, so user doesn't
     // accidentally trigger menu items.
     ui_clear_key_queue();
 
-    int item_count = ui_start_menu(headers, items);
-    int selected = 0;
+    int item_count = ui_start_menu(headers, items, initial_selection);
+    int selected = initial_selection;
     int chosen_item = -1;
 
     // Some users with dead enter keys need a way to turn on power to select.
@@ -409,6 +520,131 @@
     return chosen_item;
 }
 
+static int compare_string(const void* a, const void* b) {
+    return strcmp(*(const char**)a, *(const char**)b);
+}
+
+static int
+sdcard_directory(const char* path) {
+    ensure_path_mounted(SDCARD_ROOT);
+
+    const char* MENU_HEADERS[] = { "Choose a package to install:",
+                                   path,
+                                   "",
+                                   NULL };
+    DIR* d;
+    struct dirent* de;
+    d = opendir(path);
+    if (d == NULL) {
+        LOGE("error opening %s: %s\n", path, strerror(errno));
+        ensure_path_unmounted(SDCARD_ROOT);
+        return 0;
+    }
+
+    char** headers = prepend_title(MENU_HEADERS);
+
+    int d_size = 0;
+    int d_alloc = 10;
+    char** dirs = malloc(d_alloc * sizeof(char*));
+    int z_size = 1;
+    int z_alloc = 10;
+    char** zips = malloc(z_alloc * sizeof(char*));
+    zips[0] = strdup("../");
+
+    while ((de = readdir(d)) != NULL) {
+        int name_len = strlen(de->d_name);
+
+        if (de->d_type == DT_DIR) {
+            // skip "." and ".." entries
+            if (name_len == 1 && de->d_name[0] == '.') continue;
+            if (name_len == 2 && de->d_name[0] == '.' &&
+                de->d_name[1] == '.') continue;
+
+            if (d_size >= d_alloc) {
+                d_alloc *= 2;
+                dirs = realloc(dirs, d_alloc * sizeof(char*));
+            }
+            dirs[d_size] = malloc(name_len + 2);
+            strcpy(dirs[d_size], de->d_name);
+            dirs[d_size][name_len] = '/';
+            dirs[d_size][name_len+1] = '\0';
+            ++d_size;
+        } else if (de->d_type == DT_REG &&
+                   name_len >= 4 &&
+                   strncasecmp(de->d_name + (name_len-4), ".zip", 4) == 0) {
+            if (z_size >= z_alloc) {
+                z_alloc *= 2;
+                zips = realloc(zips, z_alloc * sizeof(char*));
+            }
+            zips[z_size++] = strdup(de->d_name);
+        }
+    }
+    closedir(d);
+
+    qsort(dirs, d_size, sizeof(char*), compare_string);
+    qsort(zips, z_size, sizeof(char*), compare_string);
+
+    // append dirs to the zips list
+    if (d_size + z_size + 1 > z_alloc) {
+        z_alloc = d_size + z_size + 1;
+        zips = realloc(zips, z_alloc * sizeof(char*));
+    }
+    memcpy(zips + z_size, dirs, d_size * sizeof(char*));
+    free(dirs);
+    z_size += d_size;
+    zips[z_size] = NULL;
+
+    int result;
+    int chosen_item = 0;
+    do {
+        chosen_item = get_menu_selection(headers, zips, 1, chosen_item);
+
+        char* item = zips[chosen_item];
+        int item_len = strlen(item);
+        if (chosen_item == 0) {          // item 0 is always "../"
+            // go up but continue browsing (if the caller is sdcard_directory)
+            result = -1;
+            break;
+        } else if (item[item_len-1] == '/') {
+            // recurse down into a subdirectory
+            char new_path[PATH_MAX];
+            strlcpy(new_path, path, PATH_MAX);
+            strlcat(new_path, "/", PATH_MAX);
+            strlcat(new_path, item, PATH_MAX);
+            new_path[strlen(new_path)-1] = '\0';  // truncate the trailing '/'
+            result = sdcard_directory(new_path);
+            if (result >= 0) break;
+        } else {
+            // selected a zip file:  attempt to install it, and return
+            // the status to the caller.
+            char new_path[PATH_MAX];
+            strlcpy(new_path, path, PATH_MAX);
+            strlcat(new_path, "/", PATH_MAX);
+            strlcat(new_path, item, PATH_MAX);
+
+            ui_print("\n-- Install %s ...\n", path);
+            set_sdcard_update_bootloader_message();
+            char* copy = copy_sideloaded_package(new_path);
+            ensure_path_unmounted(SDCARD_ROOT);
+            if (copy) {
+                result = install_package(copy);
+                free(copy);
+            } else {
+                result = INSTALL_ERROR;
+            }
+            break;
+        }
+    } while (true);
+
+    int i;
+    for (i = 0; i < z_size; ++i) free(zips[i]);
+    free(zips);
+    free(headers);
+
+    ensure_path_unmounted(SDCARD_ROOT);
+    return result;
+}
+
 static void
 wipe_data(int confirm) {
     if (confirm) {
@@ -419,7 +655,7 @@
                                 "  THIS CAN NOT BE UNDONE.",
                                 "",
                                 NULL };
-            title_headers = prepend_title(headers);
+            title_headers = prepend_title((const char**)headers);
         }
 
         char* items[] = { " No",
@@ -435,7 +671,7 @@
                           " No",
                           NULL };
 
-        int chosen_item = get_menu_selection(title_headers, items, 1);
+        int chosen_item = get_menu_selection(title_headers, items, 1, 0);
         if (chosen_item != 7) {
             return;
         }
@@ -443,26 +679,25 @@
 
     ui_print("\n-- Wiping data...\n");
     device_wipe_data();
-    erase_root("DATA:");
-#ifdef BOARD_HAS_DATADATA
-    erase_root("DATADATA:");
-#endif
-    erase_root("CACHE:");
-    erase_root("SDEXT:");
-    erase_root("SDCARD:/.android_secure");
+    erase_volume("/data");
+    erase_volume("/cache");
+    erase_volume("/datadata");
+    erase_volume("/datadata");
+    erase_volume("/sd-ext");
+    erase_volume("/sdcard/.android_secure");
     ui_print("Data wipe complete.\n");
 }
 
 static void
 prompt_and_wait() {
-    char** headers = prepend_title(MENU_HEADERS);
-    
+    char** headers = prepend_title((const char**)MENU_HEADERS);
+
     for (;;) {
         finish_recovery(NULL);
         ui_reset_progress();
 
         allow_display_toggle = 1;
-        int chosen_item = get_menu_selection(headers, MENU_ITEMS, 0);
+        int chosen_item = get_menu_selection(headers, MENU_ITEMS, 0, 0);
         allow_display_toggle = 0;
 
         // device-specific code may take some action here.  It may
@@ -483,7 +718,7 @@
                 if (confirm_selection("Confirm wipe?", "Yes - Wipe Cache"))
                 {
                     ui_print("\n-- Wiping cache...\n");
-                    erase_root("CACHE:");
+                    erase_volume("/cache");
                     ui_print("Cache wipe complete.\n");
                     if (!ui_text_visible()) return;
                 }
@@ -493,9 +728,6 @@
                 if (confirm_selection("Confirm install?", "Yes - Install /sdcard/update.zip"))
                 {
                     ui_print("\n-- Install from sdcard...\n");
-#ifndef BOARD_HAS_NO_MISC_PARTITION
-                    set_sdcard_update_bootloader_message();
-#endif
                     int status = install_package(SDCARD_PACKAGE_FILE);
                     if (status != INSTALL_SUCCESS) {
                         ui_set_background(BACKGROUND_ICON_ERROR);
@@ -503,16 +735,7 @@
                     } else if (!ui_text_visible()) {
                         return;  // reboot if logs aren't visible
                     } else {
-#ifndef BOARD_HAS_NO_MISC_PARTITION
-                        if (firmware_update_pending()) {
-                            ui_print("\nReboot via menu to complete\n"
-                                     "installation.\n");
-                        } else {
-                            ui_print("\nInstall from sdcard complete.\n");
-                        }
-#else
                         ui_print("\nInstall from sdcard complete.\n");
-#endif
                     }
                 }
                 break;
@@ -534,7 +757,7 @@
 
 static void
 print_property(const char *key, const char *name, void *cookie) {
-    fprintf(stderr, "%s=%s\n", key, name);
+    printf("%s=%s\n", key, name);
 }
 
 int
@@ -551,8 +774,6 @@
 	        return mkyaffs2image_main(argc, argv);
 	    if (strstr(argv[0], "unyaffs") != NULL)
 	        return unyaffs_main(argc, argv);
-        if (strstr(argv[0], "amend"))
-            return amend_main(argc, argv);
         if (strstr(argv[0], "nandroid"))
             return nandroid_main(argc, argv);
         if (strstr(argv[0], "reboot"))
@@ -562,7 +783,6 @@
 		return busybox_driver(argc, argv);
 	}
     __system("/sbin/postrecoveryboot.sh");
-    create_fstab();
     
     int is_user_initiated_recovery = 0;
     time_t start = time(NULL);
@@ -570,16 +790,20 @@
     // If these fail, there's not really anywhere to complain...
     freopen(TEMPORARY_LOG_FILE, "a", stdout); setbuf(stdout, NULL);
     freopen(TEMPORARY_LOG_FILE, "a", stderr); setbuf(stderr, NULL);
-    fprintf(stderr, "Starting recovery on %s", ctime(&start));
+    printf("Starting recovery on %s", ctime(&start));
 
     ui_init();
     ui_print(EXPAND(RECOVERY_VERSION)"\n");
+    load_volume_table();
     get_args(&argc, &argv);
 
     int previous_runs = 0;
     const char *send_intent = NULL;
     const char *update_package = NULL;
+    const char *encrypted_fs_mode = NULL;
     int wipe_data = 0, wipe_cache = 0;
+    int toggle_secure_fs = 0;
+    encrypted_fs_info encrypted_fs_data;
 
     int arg;
     while ((arg = getopt_long(argc, argv, "", OPTIONS, NULL)) != -1) {
@@ -589,6 +813,8 @@
         case 'u': update_package = optarg; break;
         case 'w': wipe_data = wipe_cache = 1; break;
         case 'c': wipe_cache = 1; break;
+        case 'e': encrypted_fs_mode = optarg; toggle_secure_fs = 1; break;
+        case 't': ui_show_text(1); break;
         case '?':
             LOGE("Invalid command argument\n");
             continue;
@@ -597,33 +823,79 @@
 
     device_recovery_start();
 
-    fprintf(stderr, "Command:");
+    printf("Command:");
     for (arg = 0; arg < argc; arg++) {
-        fprintf(stderr, " \"%s\"", argv[arg]);
+        printf(" \"%s\"", argv[arg]);
     }
-    fprintf(stderr, "\n\n");
+    printf("\n");
+
+    if (update_package) {
+        // For backwards compatibility on the cache partition only, if
+        // we're given an old 'root' path "CACHE:foo", change it to
+        // "/cache/foo".
+        if (strncmp(update_package, "CACHE:", 6) == 0) {
+            int len = strlen(update_package) + 10;
+            char* modified_path = malloc(len);
+            strlcpy(modified_path, "/cache/", len);
+            strlcat(modified_path, update_package+6, len);
+            printf("(replacing path \"%s\" with \"%s\")\n",
+                   update_package, modified_path);
+            update_package = modified_path;
+        }
+    }
+    printf("\n");
 
     property_list(print_property, NULL);
-    fprintf(stderr, "\n");
+    printf("\n");
 
     int status = INSTALL_SUCCESS;
     
-    RecoveryCommandContext ctx = { NULL };
-    if (register_update_commands(&ctx)) {
-        LOGE("Can't install update commands\n");
-    }
+    if (toggle_secure_fs) {
+        if (strcmp(encrypted_fs_mode,"on") == 0) {
+            encrypted_fs_data.mode = MODE_ENCRYPTED_FS_ENABLED;
+            ui_print("Enabling Encrypted FS.\n");
+        } else if (strcmp(encrypted_fs_mode,"off") == 0) {
+            encrypted_fs_data.mode = MODE_ENCRYPTED_FS_DISABLED;
+            ui_print("Disabling Encrypted FS.\n");
+        } else {
+            ui_print("Error: invalid Encrypted FS setting.\n");
+            status = INSTALL_ERROR;
+        }
 
-    if (update_package != NULL) {
-        if (wipe_data && erase_root("DATA:")) status = INSTALL_ERROR;
+        // Recovery strategy: if the data partition is damaged, disable encrypted file systems.
+        // This preventsthe device recycling endlessly in recovery mode.
+        if ((encrypted_fs_data.mode == MODE_ENCRYPTED_FS_ENABLED) &&
+                (read_encrypted_fs_info(&encrypted_fs_data))) {
+            ui_print("Encrypted FS change aborted, resetting to disabled state.\n");
+            encrypted_fs_data.mode = MODE_ENCRYPTED_FS_DISABLED;
+        }
+
+        if (status != INSTALL_ERROR) {
+            if (erase_volume("/data")) {
+                ui_print("Data wipe failed.\n");
+                status = INSTALL_ERROR;
+            } else if (erase_volume("/cache")) {
+                ui_print("Cache wipe failed.\n");
+                status = INSTALL_ERROR;
+            } else if ((encrypted_fs_data.mode == MODE_ENCRYPTED_FS_ENABLED) &&
+                      (restore_encrypted_fs_info(&encrypted_fs_data))) {
+                ui_print("Encrypted FS change aborted.\n");
+                status = INSTALL_ERROR;
+            } else {
+                ui_print("Successfully updated Encrypted FS.\n");
+                status = INSTALL_SUCCESS;
+            }
+        }
+    } else if (update_package != NULL) {
         status = install_package(update_package);
         if (status != INSTALL_SUCCESS) ui_print("Installation aborted.\n");
     } else if (wipe_data) {
         if (device_wipe_data()) status = INSTALL_ERROR;
-        if (erase_root("DATA:")) status = INSTALL_ERROR;
-        if (wipe_cache && erase_root("CACHE:")) status = INSTALL_ERROR;
+        if (erase_volume("/data")) status = INSTALL_ERROR;
+        if (wipe_cache && erase_volume("/cache")) status = INSTALL_ERROR;
         if (status != INSTALL_SUCCESS) ui_print("Data wipe failed.\n");
     } else if (wipe_cache) {
-        if (wipe_cache && erase_root("CACHE:")) status = INSTALL_ERROR;
+        if (wipe_cache && erase_volume("/cache")) status = INSTALL_ERROR;
         if (status != INSTALL_SUCCESS) ui_print("Cache wipe failed.\n");
     } else {
         LOGI("Checking for extendedcommand...\n");
@@ -651,12 +923,9 @@
     }
 
     if (status != INSTALL_SUCCESS && !is_user_initiated_recovery) ui_set_background(BACKGROUND_ICON_ERROR);
-    if (status != INSTALL_SUCCESS || ui_text_visible()) prompt_and_wait();
-
-#ifndef BOARD_HAS_NO_MISC_PARTITION
-    // If there is a radio image pending, reboot now to install it.
-    maybe_install_firmware_update(send_intent);
-#endif
+    if (status != INSTALL_SUCCESS || ui_text_visible()) {
+        prompt_and_wait();
+    }
 
     // Otherwise, get ready to boot the main system...
     finish_recovery(send_intent);
diff --git a/recovery_ui.h b/recovery_ui.h
index 662c6db..f34365f 100644
--- a/recovery_ui.h
+++ b/recovery_ui.h
@@ -82,7 +82,7 @@
 extern char* MENU_ITEMS[];
 
 int
-get_menu_selection(char** headers, char** items, int menu_only);
+get_menu_selection(char** headers, char** items, int menu_only, int initial_selection);
 
 void
 set_sdcard_update_bootloader_message();
diff --git a/roots.c b/roots.c
index 3788d45..184bfd0 100644
--- a/roots.c
+++ b/roots.c
@@ -1,6 +1,5 @@
 /*
  * Copyright (C) 2007 The Android Open Source Project
- * Copyright (c) 2010, Code Aurora Forum. All rights reserved.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -21,363 +20,233 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <unistd.h>
+#include <ctype.h>
 
-#include <limits.h>
-
-#include "flashutils/flashutils.h"
-#include "minzip/Zip.h"
+#include "mtdutils/mtdutils.h"
+#include "mounts.h"
 #include "roots.h"
 #include "common.h"
+#include "make_ext4fs.h"
 
-#include "mounts.h"
-#include "extendedcommands.h"
+static int num_volumes = 0;
+static Volume* device_volumes = NULL;
 
-/* Canonical pointers.
-xxx may just want to use enums
- */
-static const char g_default_device[] = "@\0g_default_device";
-static const char g_raw[] = "@\0g_raw";
-static const char g_package_file[] = "@\0g_package_file";
+void load_volume_table() {
+    int alloc = 2;
+    device_volumes = malloc(alloc * sizeof(Volume));
 
-static RootInfo g_roots[] = {
-    { "BOOT:", g_default_device, NULL, "boot", NULL, g_raw, NULL },
-    { "CACHE:", BOARD_CACHE_DEVICE, NULL, "cache", "/cache", BOARD_CACHE_FILESYSTEM, BOARD_CACHE_FILESYSTEM_OPTIONS },
-    { "DATA:", BOARD_DATA_DEVICE, NULL, "userdata", "/data", BOARD_DATA_FILESYSTEM, BOARD_DATA_FILESYSTEM_OPTIONS },
-#ifdef BOARD_HAS_DATADATA
-    { "DATADATA:", BOARD_DATADATA_DEVICE, NULL, "datadata", "/datadata", BOARD_DATADATA_FILESYSTEM, BOARD_DATADATA_FILESYSTEM_OPTIONS },
-#endif
-    { "MISC:", g_default_device, NULL, "misc", NULL, g_raw, NULL },
-    { "PACKAGE:", NULL, NULL, NULL, NULL, g_package_file, NULL },
-    { "RECOVERY:", g_default_device, NULL, "recovery", "/", g_raw, NULL },
-    { "SDCARD:", BOARD_SDCARD_DEVICE_PRIMARY, BOARD_SDCARD_DEVICE_SECONDARY, NULL, "/sdcard", "vfat", NULL },
-#ifdef BOARD_HAS_SDCARD_INTERNAL
-    { "SDINTERNAL:", BOARD_SDCARD_DEVICE_INTERNAL, NULL, NULL, "/emmc", "vfat", NULL },
-#endif
-    { "SDEXT:", BOARD_SDEXT_DEVICE, NULL, NULL, "/sd-ext", BOARD_SDEXT_FILESYSTEM, NULL },
-    { "SYSTEM:", BOARD_SYSTEM_DEVICE, NULL, "system", "/system", BOARD_SYSTEM_FILESYSTEM, BOARD_SYSTEM_FILESYSTEM_OPTIONS },
-    { "MBM:", g_default_device, NULL, "mbm", NULL, g_raw, NULL },
-    { "TMP:", NULL, NULL, NULL, "/tmp", NULL, NULL },
-};
-#define NUM_ROOTS (sizeof(g_roots) / sizeof(g_roots[0]))
+    // Insert an entry for /tmp, which is the ramdisk and is always mounted.
+    device_volumes[0].mount_point = "/tmp";
+    device_volumes[0].fs_type = "ramdisk";
+    device_volumes[0].device = NULL;
+    device_volumes[0].device2 = NULL;
+    num_volumes = 1;
 
-// TODO: for SDCARD:, try /dev/block/mmcblk0 if mmcblk0p1 fails
-
-const RootInfo *
-get_root_info_for_path(const char *root_path)
-{
-    const char *c;
-
-    /* Find the first colon.
-     */
-    c = root_path;
-    while (*c != '\0' && *c != ':') {
-        c++;
+    FILE* fstab = fopen("/etc/recovery.fstab", "r");
+    if (fstab == NULL) {
+        LOGE("failed to open /etc/recovery.fstab (%s)\n", strerror(errno));
+        return;
     }
-    if (*c == '\0') {
-        return NULL;
+
+    char buffer[1024];
+    int i;
+    while (fgets(buffer, sizeof(buffer)-1, fstab)) {
+        for (i = 0; buffer[i] && isspace(buffer[i]); ++i);
+        if (buffer[i] == '\0' || buffer[i] == '#') continue;
+
+        char* original = strdup(buffer);
+
+        char* mount_point = strtok(buffer+i, " \t\n");
+        char* fs_type = strtok(NULL, " \t\n");
+        char* device = strtok(NULL, " \t\n");
+        // lines may optionally have a second device, to use if
+        // mounting the first one fails.
+        char* device2 = strtok(NULL, " \t\n");
+
+        if (mount_point && fs_type && device) {
+            while (num_volumes >= alloc) {
+                alloc *= 2;
+                device_volumes = realloc(device_volumes, alloc*sizeof(Volume));
+            }
+            device_volumes[num_volumes].mount_point = strdup(mount_point);
+            device_volumes[num_volumes].fs_type = strdup(fs_type);
+            device_volumes[num_volumes].device = strdup(device);
+            device_volumes[num_volumes].device2 =
+                device2 ? strdup(device2) : NULL;
+            ++num_volumes;
+        } else {
+            LOGE("skipping malformed recovery.fstab line: %s\n", original);
+        }
+        free(original);
     }
-    size_t len = c - root_path + 1;
-    size_t i;
-    for (i = 0; i < NUM_ROOTS; i++) {
-        RootInfo *info = &g_roots[i];
-        if (strncmp(info->name, root_path, len) == 0) {
-            return info;
+
+    fclose(fstab);
+
+    printf("recovery filesystem table\n");
+    printf("=========================\n");
+    for (i = 0; i < num_volumes; ++i) {
+        Volume* v = &device_volumes[i];
+        printf("  %d %s %s %s %s\n", i, v->mount_point, v->fs_type,
+               v->device, v->device2);
+    }
+    printf("\n");
+}
+
+Volume* volume_for_path(const char* path) {
+    int i;
+    for (i = 0; i < num_volumes; ++i) {
+        Volume* v = device_volumes+i;
+        int len = strlen(v->mount_point);
+        if (strncmp(path, v->mount_point, len) == 0 &&
+            (path[len] == '\0' || path[len] == '/')) {
+            return v;
         }
     }
     return NULL;
 }
 
-static const ZipArchive *g_package = NULL;
-static char *g_package_path = NULL;
-
-int
-register_package_root(const ZipArchive *package, const char *package_path)
-{
-    if (package != NULL) {
-        package_path = strdup(package_path);
-        if (package_path == NULL) {
-            return -1;
-        }
-        g_package_path = (char *)package_path;
-    } else {
-        free(g_package_path);
-        g_package_path = NULL;
-    }
-    g_package = package;
-    return 0;
-}
-
-int
-is_package_root_path(const char *root_path)
-{
-    const RootInfo *info = get_root_info_for_path(root_path);
-    return info != NULL && info->filesystem == g_package_file;
-}
-
-const char *
-translate_package_root_path(const char *root_path,
-        char *out_buf, size_t out_buf_len, const ZipArchive **out_package)
-{
-    const RootInfo *info = get_root_info_for_path(root_path);
-    if (info == NULL || info->filesystem != g_package_file) {
-        return NULL;
-    }
-
-    /* Strip the package root off of the path.
-     */
-    size_t root_len = strlen(info->name);
-    root_path += root_len;
-    size_t root_path_len = strlen(root_path);
-
-    if (out_buf_len < root_path_len + 1) {
-        return NULL;
-    }
-    strcpy(out_buf, root_path);
-    *out_package = g_package;
-    return out_buf;
-}
-
-/* Takes a string like "SYSTEM:lib" and turns it into a string
- * like "/system/lib".  The translated path is put in out_buf,
- * and out_buf is returned if the translation succeeded.
- */
-const char *
-translate_root_path(const char *root_path, char *out_buf, size_t out_buf_len)
-{
-    if (out_buf_len < 1) {
-        return NULL;
-    }
-
-    const RootInfo *info = get_root_info_for_path(root_path);
-    if (info == NULL || info->mount_point == NULL) {
-        return NULL;
-    }
-
-    /* Find the relative part of the non-root part of the path.
-     */
-    root_path += strlen(info->name);  // strip off the "root:"
-    while (*root_path != '\0' && *root_path == '/') {
-        root_path++;
-    }
-
-    size_t mp_len = strlen(info->mount_point);
-    size_t rp_len = strlen(root_path);
-    if (mp_len + 1 + rp_len + 1 > out_buf_len) {
-        return NULL;
-    }
-
-    /* Glue the mount point to the relative part of the path.
-     */
-    memcpy(out_buf, info->mount_point, mp_len);
-    if (out_buf[mp_len - 1] != '/') out_buf[mp_len++] = '/';
-
-    memcpy(out_buf + mp_len, root_path, rp_len);
-    out_buf[mp_len + rp_len] = '\0';
-
-    return out_buf;
-}
-
-static int
-internal_root_mounted(const RootInfo *info)
-{
-    if (info->mount_point == NULL) {
+int ensure_path_mounted(const char* path) {
+    Volume* v = volume_for_path(path);
+    if (v == NULL) {
+        LOGE("unknown volume for path [%s]\n", path);
         return -1;
     }
-//xxx if TMP: (or similar) just say "yes"
-
-    /* See if this root is already mounted.
-     */
-    int ret = scan_mounted_volumes();
-    if (ret < 0) {
-        return ret;
-    }
-    const MountedVolume *volume;
-    volume = find_mounted_volume_by_mount_point(info->mount_point);
-    if (volume != NULL) {
-        /* It's already mounted.
-         */
+    if (strcmp(v->fs_type, "ramdisk") == 0) {
+        // the ramdisk is always mounted.
         return 0;
     }
+
+    int result;
+    result = scan_mounted_volumes();
+    if (result < 0) {
+        LOGE("failed to scan mounted volumes\n");
+        return -1;
+    }
+
+    const MountedVolume* mv =
+        find_mounted_volume_by_mount_point(v->mount_point);
+    if (mv) {
+        // volume is already mounted
+        return 0;
+    }
+
+    mkdir(v->mount_point, 0755);  // in case it doesn't already exist
+
+    if (strcmp(v->fs_type, "yaffs2") == 0) {
+        // mount an MTD partition as a YAFFS2 filesystem.
+        mtd_scan_partitions();
+        const MtdPartition* partition;
+        partition = mtd_find_partition_by_name(v->device);
+        if (partition == NULL) {
+            LOGE("failed to find \"%s\" partition to mount at \"%s\"\n",
+                 v->device, v->mount_point);
+            return -1;
+        }
+        return mtd_mount_partition(partition, v->mount_point, v->fs_type, 0);
+    } else if (strcmp(v->fs_type, "ext4") == 0 ||
+               strcmp(v->fs_type, "vfat") == 0) {
+        result = mount(v->device, v->mount_point, v->fs_type,
+                       MS_NOATIME | MS_NODEV | MS_NODIRATIME, "");
+        if (result == 0) return 0;
+
+        if (v->device2) {
+            LOGW("failed to mount %s (%s); trying %s\n",
+                 v->device, strerror(errno), v->device2);
+            result = mount(v->device2, v->mount_point, v->fs_type,
+                           MS_NOATIME | MS_NODEV | MS_NODIRATIME, "");
+            if (result == 0) return 0;
+        }
+
+        LOGE("failed to mount %s (%s)\n", v->mount_point, strerror(errno));
+        return -1;
+    }
+
+    LOGE("unknown fs_type \"%s\" for %s\n", v->fs_type, v->mount_point);
     return -1;
 }
 
-int
-is_root_path_mounted(const char *root_path)
-{
-    const RootInfo *info = get_root_info_for_path(root_path);
-    if (info == NULL) {
+int ensure_path_unmounted(const char* path) {
+    Volume* v = volume_for_path(path);
+    if (v == NULL) {
+        LOGE("unknown volume for path [%s]\n", path);
         return -1;
     }
-    return internal_root_mounted(info) >= 0;
-}
-
-static int mount_internal(const char* device, const char* mount_point, const char* filesystem, const char* filesystem_options)
-{
-    if (strcmp(filesystem, "auto") != 0 && filesystem_options == NULL) {
-        return mount(device, mount_point, filesystem, MS_NOATIME | MS_NODEV | MS_NODIRATIME, "");
-    }
-    else {
-        char mount_cmd[PATH_MAX];
-        const char* options = filesystem_options == NULL ? "noatime,nodiratime,nodev" : filesystem_options;
-        sprintf(mount_cmd, "mount -t %s -o%s %s %s", filesystem, options, device, mount_point);
-        return __system(mount_cmd);
-    }
-}
-
-int
-ensure_root_path_mounted(const char *root_path)
-{
-    const RootInfo *info = get_root_info_for_path(root_path);
-    if (info == NULL) {
+    if (strcmp(v->fs_type, "ramdisk") == 0) {
+        // the ramdisk is always mounted; you can't unmount it.
         return -1;
     }
 
-    int ret = internal_root_mounted(info);
-    if (ret >= 0) {
-        /* It's already mounted.
-         */
+    int result;
+    result = scan_mounted_volumes();
+    if (result < 0) {
+        LOGE("failed to scan mounted volumes\n");
+        return -1;
+    }
+
+    const MountedVolume* mv =
+        find_mounted_volume_by_mount_point(v->mount_point);
+    if (mv == NULL) {
+        // volume is already unmounted
         return 0;
     }
 
-    /* It's not mounted.
-     */
-    if (info->device == NULL || info->mount_point == NULL ||
-        info->filesystem == NULL ||
-        info->filesystem == g_raw ||
-        info->filesystem == g_package_file) {
-        return -1;
-    }
-
-    if (info->device == g_default_device) {
-        if (info->partition_name == NULL) {
-            return -1;
-        }
-        return mount_partition(info->partition_name, info->mount_point, info->filesystem, 0);
-    }
-
-    mkdir(info->mount_point, 0755);  // in case it doesn't already exist
-    if (mount_internal(info->device, info->mount_point, info->filesystem, info->filesystem_options)) {
-        if (info->device2 == NULL) {
-            LOGE("Can't mount %s\n(%s)\n", info->device, strerror(errno));
-            return -1;
-        } else if (mount(info->device2, info->mount_point, info->filesystem,
-                MS_NOATIME | MS_NODEV | MS_NODIRATIME, "")) {
-            LOGE("Can't mount %s (or %s)\n(%s)\n",
-                    info->device, info->device2, strerror(errno));
-            return -1;
-        }
-    }
-    return 0;
+    return unmount_mounted_volume(mv);
 }
 
-int
-ensure_root_path_unmounted(const char *root_path)
-{
-    const RootInfo *info = get_root_info_for_path(root_path);
-    if (info == NULL) {
+int format_volume(const char* volume) {
+    Volume* v = volume_for_path(volume);
+    if (v == NULL) {
+        LOGE("unknown volume \"%s\"\n", volume);
         return -1;
     }
-    if (info->mount_point == NULL) {
-        /* This root can't be mounted, so by definition it isn't.
-         */
-        return 0;
+    if (strcmp(v->fs_type, "ramdisk") == 0) {
+        // you can't format the ramdisk.
+        LOGE("can't format_volume \"%s\"", volume);
+        return -1;
     }
-//xxx if TMP: (or similar) just return error
+    if (strcmp(v->mount_point, volume) != 0) {
+        LOGE("can't give path \"%s\" to format_volume\n", volume);
+        return -1;
+    }
 
-    /* See if this root is already mounted.
-     */
-    int ret = scan_mounted_volumes();
-    if (ret < 0) {
-        return ret;
+    if (ensure_path_unmounted(volume) != 0) {
+        LOGE("format_volume failed to unmount \"%s\"\n", v->mount_point);
+        return -1;
     }
-    const MountedVolume *volume;
-    volume = find_mounted_volume_by_mount_point(info->mount_point);
-    if (volume == NULL) {
-        /* It's not mounted.
-         */
+
+    if (strcmp(v->fs_type, "yaffs2") == 0 || strcmp(v->fs_type, "mtd") == 0) {
+        mtd_scan_partitions();
+        const MtdPartition* partition = mtd_find_partition_by_name(v->device);
+        if (partition == NULL) {
+            LOGE("format_volume: no MTD partition \"%s\"\n", v->device);
+            return -1;
+        }
+
+        MtdWriteContext *write = mtd_write_partition(partition);
+        if (write == NULL) {
+            LOGW("format_volume: can't open MTD \"%s\"\n", v->device);
+            return -1;
+        } else if (mtd_erase_blocks(write, -1) == (off_t) -1) {
+            LOGW("format_volume: can't erase MTD \"%s\"\n", v->device);
+            mtd_write_close(write);
+            return -1;
+        } else if (mtd_write_close(write)) {
+            LOGW("format_volume: can't close MTD \"%s\"\n", v->device);
+            return -1;
+        }
         return 0;
     }
 
-    return unmount_mounted_volume(volume);
-}
-
-int
-get_root_partition_device(const char *root_path, char *device)
-{
-    const RootInfo *info = get_root_info_for_path(root_path);
-    if (info == NULL)
-    {
-        return NULL;
-    }
-    if (info->device == g_default_device)
-        return get_partition_device(info->partition_name, device);
-    return info->device;
-}
-
-const MtdPartition *
-get_root_mtd_partition(const char *root_path)
-{
-    const RootInfo *info = get_root_info_for_path(root_path);
-    if (info == NULL || info->device != g_default_device ||
-            info->partition_name == NULL)
-    {
-#ifdef BOARD_HAS_MTD_CACHE
-        if (strcmp(root_path, "CACHE:") != 0)
-            return NULL;
-#else
-        return NULL;
-#endif
-    }
-    mtd_scan_partitions();
-    return mtd_find_partition_by_name(info->partition_name);
-}
-
-int
-format_root_device(const char *root)
-{
-    /* Be a little safer here; require that "root" is just
-     * a device with no relative path after it.
-     */
-    const char *c = root;
-    while (*c != '\0' && *c != ':') {
-        c++;
-    }
-    /*
-    if (c[0] != ':' || c[1] != '\0') {
-        LOGW("format_root_device: bad root name \"%s\"\n", root);
-        return -1;
-    }
-    */
-
-    const RootInfo *info = get_root_info_for_path(root);
-    if (info == NULL || info->device == NULL) {
-        LOGW("format_root_device: can't resolve \"%s\"\n", root);
-        return -1;
-    }
-
-    if (info->mount_point != NULL && info->device == g_default_device) {
-        /* Don't try to format a mounted device.
-         */
-        int ret = ensure_root_path_unmounted(root);
-        if (ret < 0) {
-            LOGW("format_root_device: can't unmount \"%s\"\n", root);
-            return ret;
+    if (strcmp(v->fs_type, "ext4") == 0) {
+        reset_ext4fs_info();
+        int result = make_ext4fs(v->device, NULL, NULL, 0, 0, 0);
+        if (result != 0) {
+            LOGE("format_volume: make_extf4fs failed on %s\n", v->device);
+            return -1;
         }
+        return 0;
     }
 
-    /* Format the device.
-     */
-    if (info->device == g_default_device) {
-        int ret = 0;
-        if (info->filesystem == g_raw)
-            ret = erase_raw_partition(info->partition_name);
-        else
-            ret = erase_partition(info->partition_name, info->filesystem);
-        
-        if (ret != 0)
-            LOGE("Error erasing device %s\n", info->device);
-        return ret;
-    }
-
-    return format_unknown_device(root);
+    LOGE("format_volume: fs_type \"%s\" unsupported\n", v->fs_type);
+    return -1;
 }
diff --git a/roots.h b/roots.h
index 833c1b5..cf59bfd 100644
--- a/roots.h
+++ b/roots.h
@@ -1,6 +1,5 @@
 /*
  * Copyright (C) 2007 The Android Open Source Project
- * Copyright (c) 2010, Code Aurora Forum. All rights reserved.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -18,135 +17,25 @@
 #ifndef RECOVERY_ROOTS_H_
 #define RECOVERY_ROOTS_H_
 
-#include "minzip/Zip.h"
-#include "flashutils/flashutils.h"
-#include "mtdutils/mtdutils.h"
-#include "mmcutils/mmcutils.h"
+#include "common.h"
 
-#ifndef BOARD_USES_MMCUTILS
-#define DEFAULT_FILESYSTEM "yaffs2"
-#else
-#define DEFAULT_FILESYSTEM "ext3"
-#endif
+// Load and parse volume data from /etc/recovery.fstab.
+void load_volume_table();
 
-#ifndef BOARD_SDCARD_DEVICE_PRIMARY
-#define BOARD_SDCARD_DEVICE_PRIMARY "/dev/block/mmcblk0p1"
-#endif
+// Return the Volume* record for this path (or NULL).
+Volume* volume_for_path(const char* path);
 
-#ifndef BOARD_SDCARD_DEVICE_SECONDARY
-#define BOARD_SDCARD_DEVICE_SECONDARY "/dev/block/mmcblk0"
-#endif
+// Make sure that the volume 'path' is on is mounted.  Returns 0 on
+// success (volume is mounted).
+int ensure_path_mounted(const char* path);
 
-#ifndef BOARD_SDCARD_DEVICE_INTERNAL
-#define BOARD_SDCARD_DEVICE_INTERNAL g_default_device
-#endif
+// Make sure that the volume 'path' is on is mounted.  Returns 0 on
+// success (volume is unmounted);
+int ensure_path_unmounted(const char* path);
 
-#ifndef BOARD_SDEXT_DEVICE
-#define BOARD_SDEXT_DEVICE "/dev/block/mmcblk0p2"
-#endif
-
-#ifndef BOARD_SDEXT_FILESYSTEM
-#define BOARD_SDEXT_FILESYSTEM "auto"
-#endif
-
-#ifndef BOARD_DATA_DEVICE
-#define BOARD_DATA_DEVICE g_default_device
-#endif
-
-#ifndef BOARD_DATA_FILESYSTEM
-#define BOARD_DATA_FILESYSTEM DEFAULT_FILESYSTEM
-#endif
-
-#ifndef BOARD_DATADATA_DEVICE
-#define BOARD_DATADATA_DEVICE g_default_device
-#endif
-
-#ifndef BOARD_DATADATA_FILESYSTEM
-#define BOARD_DATADATA_FILESYSTEM DEFAULT_FILESYSTEM
-#endif
-
-#ifndef BOARD_CACHE_DEVICE
-#define BOARD_CACHE_DEVICE g_default_device
-#endif
-
-#ifndef BOARD_CACHE_FILESYSTEM
-#define BOARD_CACHE_FILESYSTEM DEFAULT_FILESYSTEM
-#endif
-
-#ifndef BOARD_SYSTEM_DEVICE
-#define BOARD_SYSTEM_DEVICE g_default_device
-#endif
-
-#ifndef BOARD_SYSTEM_FILESYSTEM
-#define BOARD_SYSTEM_FILESYSTEM DEFAULT_FILESYSTEM
-#endif
-
-#ifndef BOARD_DATA_FILESYSTEM_OPTIONS
-#define BOARD_DATA_FILESYSTEM_OPTIONS NULL
-#endif
-
-#ifndef BOARD_CACHE_FILESYSTEM_OPTIONS
-#define BOARD_CACHE_FILESYSTEM_OPTIONS NULL
-#endif
-
-#ifndef BOARD_DATADATA_FILESYSTEM_OPTIONS
-#define BOARD_DATADATA_FILESYSTEM_OPTIONS NULL
-#endif
-
-#ifndef BOARD_SYSTEM_FILESYSTEM_OPTIONS
-#define BOARD_SYSTEM_FILESYSTEM_OPTIONS NULL
-#endif
-
-
-/* Any of the "root_path" arguments can be paths with relative
- * components, like "SYSTEM:a/b/c".
- */
-
-/* Associate this package with the package root "PKG:".
- */
-int register_package_root(const ZipArchive *package, const char *package_path);
-
-/* Returns non-zero iff root_path points inside a package.
- */
-int is_package_root_path(const char *root_path);
-
-/* Takes a string like "SYSTEM:lib" and turns it into a string
- * like "/system/lib".  The translated path is put in out_buf,
- * and out_buf is returned if the translation succeeded.
- */
-const char *translate_root_path(const char *root_path,
-        char *out_buf, size_t out_buf_len);
-
-/* Takes a string like "PKG:lib/libc.so" and returns a pointer to
- * the containing zip file and a path like "lib/libc.so".
- */
-const char *translate_package_root_path(const char *root_path,
-        char *out_buf, size_t out_buf_len, const ZipArchive **out_package);
-
-/* Returns negative on error, positive if it's mounted, zero if it isn't.
- */
-int is_root_path_mounted(const char *root_path);
-
-int ensure_root_path_mounted(const char *root_path);
-
-int ensure_root_path_unmounted(const char *root_path);
-
-const MtdPartition *get_root_mtd_partition(const char *root_path);
-int get_root_partition_device(const char *root_path, char *device);
-
-/* "root" must be the exact name of the root; no relative path is permitted.
- * If the named root is mounted, this will attempt to unmount it first.
- */
-int format_root_device(const char *root);
-
-typedef struct {
-    const char *name;
-    const char *device;
-    const char *device2;  // If the first one doesn't work (may be NULL)
-    const char *partition_name;
-    const char *mount_point;
-    const char *filesystem;
-    const char *filesystem_options;
-} RootInfo;
+// Reformat the given volume (must be the mount point only, eg
+// "/cache"), no paths permitted.  Attempts to unmount the volume if
+// it is mounted.
+int format_volume(const char* volume);
 
 #endif  // RECOVERY_ROOTS_H_
diff --git a/test_roots.c b/test_roots.c
deleted file mode 100644
index f49f55e..0000000
--- a/test_roots.c
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2007 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 "roots.h"
-#include "common.h"
-
-#define CANARY_FILE "/system/build.prop"
-#define CANARY_FILE_ROOT_PATH "SYSTEM:build.prop"
-
-int
-file_exists(const char *path)
-{
-    struct stat st;
-    int ret;
-    ret = stat(path, &st);
-    if (ret == 0) {
-        return S_ISREG(st.st_mode);
-    }
-    return 0;
-}
-
-int
-test_roots()
-{
-    int ret;
-
-    /* Make sure that /system isn't mounted yet.
-     */
-    if (file_exists(CANARY_FILE)) return -__LINE__;
-    if (is_root_path_mounted(CANARY_FILE_ROOT_PATH)) return -__LINE__;
-
-    /* Try to mount the root.
-     */
-    ret = ensure_root_path_mounted(CANARY_FILE_ROOT_PATH);
-    if (ret < 0) return -__LINE__;
-
-    /* Make sure we can see the file now and that we know the root is mounted.
-     */
-    if (!file_exists(CANARY_FILE)) return -__LINE__;
-    if (!is_root_path_mounted(CANARY_FILE_ROOT_PATH)) return -__LINE__;
-
-    /* Make sure that the root path corresponds to the regular path.
-     */
-    struct stat st1, st2;
-    char buf[128];
-    const char *path = translate_root_path(CANARY_FILE_ROOT_PATH,
-            buf, sizeof(buf));
-    if (path == NULL) return -__LINE__;
-    ret = stat(CANARY_FILE, &st1);
-    if (ret != 0) return -__LINE__;
-    ret = stat(path, &st2);
-    if (ret != 0) return -__LINE__;
-    if (st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino) return -__LINE__;
-
-    /* Try to unmount the root.
-     */
-    ret = ensure_root_path_unmounted(CANARY_FILE_ROOT_PATH);
-    if (ret < 0) return -__LINE__;
-
-    /* Make sure that we can't see the file anymore and that
-     * we don't think the root is mounted.
-     */
-    if (file_exists(CANARY_FILE)) return -__LINE__;
-    if (is_root_path_mounted(CANARY_FILE_ROOT_PATH)) return -__LINE__;
-
-    return 0;
-}
diff --git a/ui.c b/ui.c
index 4426d97..d973a50 100644
--- a/ui.c
+++ b/ui.c
@@ -35,8 +35,8 @@
 static int gShowBackButton = 0;
 #endif
 
-#define MAX_COLS 64
-#define MAX_ROWS 32 
+#define MAX_COLS 96
+#define MAX_ROWS 32
 
 #define MENU_MAX_COLS 64
 #define MENU_MAX_ROWS 250
@@ -459,7 +459,7 @@
     vsnprintf(buf, 256, fmt, ap);
     va_end(ap);
 
-    fputs(buf, stderr);
+    fputs(buf, stdout);
 
     // This can get called before ui_init(), so be careful.
     pthread_mutex_lock(&gUpdateMutex);
@@ -490,7 +490,7 @@
 #define MENU_ITEM_HEADER " - "
 #define MENU_ITEM_HEADER_LENGTH strlen(MENU_ITEM_HEADER)
 
-int ui_start_menu(char** headers, char** items) {
+int ui_start_menu(char** headers, char** items, int initial_selection) {
     int i;
     pthread_mutex_lock(&gUpdateMutex);
     if (text_rows > 0 && text_cols > 0) {
@@ -514,7 +514,7 @@
 
         menu_items = i - menu_top;
         show_menu = 1;
-        menu_sel = menu_show_start = 0;
+        menu_sel = menu_show_start = initial_selection;
         update_screen_locked();
     }
     pthread_mutex_unlock(&gUpdateMutex);
@@ -569,6 +569,14 @@
     return visible;
 }
 
+void ui_show_text(int visible)
+{
+    pthread_mutex_lock(&gUpdateMutex);
+    show_text = visible;
+    update_screen_locked();
+    pthread_mutex_unlock(&gUpdateMutex);
+}
+
 int ui_wait_key()
 {
     pthread_mutex_lock(&key_queue_mutex);
diff --git a/updater/Android.mk b/updater/Android.mk
index d312493..5c43e1c 100644
--- a/updater/Android.mk
+++ b/updater/Android.mk
@@ -19,10 +19,16 @@
 
 LOCAL_SRC_FILES := $(updater_src_files)
 
+ifeq ($(TARGET_USERIMAGES_USE_EXT4), true)
+LOCAL_CFLAGS += -DUSE_EXT4
+LOCAL_C_INCLUDES += system/extras/ext4_utils
+LOCAL_STATIC_LIBRARIES += libext4_utils libz
+endif
+
 LOCAL_STATIC_LIBRARIES += libflashutils libmtdutils libmmcutils libbmlutils
 
 LOCAL_STATIC_LIBRARIES += $(TARGET_RECOVERY_UPDATER_LIBS) $(TARGET_RECOVERY_UPDATER_EXTRA_LIBS)
-LOCAL_STATIC_LIBRARIES += libapplypatch libedify libminzip libz
+LOCAL_STATIC_LIBRARIES += libapplypatch libedify libmtdutils libminzip libz
 LOCAL_STATIC_LIBRARIES += libmincrypt libbz
 LOCAL_STATIC_LIBRARIES += libcutils libstdc++ libc
 LOCAL_C_INCLUDES += $(LOCAL_PATH)/..
diff --git a/updater/install.c b/updater/install.c
index 7b9aa80..478055d 100644
--- a/updater/install.c
+++ b/updater/install.c
@@ -39,24 +39,35 @@
 #include "updater.h"
 #include "applypatch/applypatch.h"
 
-// mount(type, location, mount_point)
+#ifdef USE_EXT4
+#include "make_ext4fs.h"
+#endif
+
+// mount(fs_type, partition_type, location, mount_point)
 //
-//   what:  type="MTD"   location="<partition>"            to mount a yaffs2 filesystem
-//          type="vfat"  location="/dev/block/<whatever>"  to mount a device
+//    fs_type="yaffs2" partition_type="MTD"     location=partition
+//    fs_type="ext4"   partition_type="EMMC"    location=device
 Value* MountFn(const char* name, State* state, int argc, Expr* argv[]) {
     char* result = NULL;
-    if (argc != 3) {
-        return ErrorAbort(state, "%s() expects 3 args, got %d", name, argc);
+    if (argc != 4) {
+        return ErrorAbort(state, "%s() expects 4 args, got %d", name, argc);
     }
-    char* type;
+    char* fs_type;
+    char* partition_type;
     char* location;
     char* mount_point;
-    if (ReadArgs(state, argv, 3, &type, &location, &mount_point) < 0) {
+    if (ReadArgs(state, argv, 4, &fs_type, &partition_type,
+                 &location, &mount_point) < 0) {
         return NULL;
     }
 
-    if (strlen(type) == 0) {
-        ErrorAbort(state, "type argument to %s() can't be empty", name);
+    if (strlen(fs_type) == 0) {
+        ErrorAbort(state, "fs_type argument to %s() can't be empty", name);
+        goto done;
+    }
+    if (strlen(partition_type) == 0) {
+        ErrorAbort(state, "partition_type argument to %s() can't be empty",
+                   name);
         goto done;
     }
     if (strlen(location) == 0) {
@@ -70,13 +81,13 @@
 
     mkdir(mount_point, 0755);
 
-    if (strcmp(type, "MTD") == 0 || strcmp(type, "MMC") == 0) {
+    if (strcmp(partition_type, "MTD") == 0 || strcmp(partition_type, "MMC") == 0) {
         if (0 == mount_partition(location, mount_point, get_default_filesystem(), 0))
             result = mount_point;
         else
             result = strdup("");
     } else {
-        if (mount(location, mount_point, type,
+        if (mount(location, mount_point, fs_type,
                   MS_NOATIME | MS_NODEV | MS_NODIRATIME, "") < 0) {
             fprintf(stderr, "%s: failed to mount %s at %s: %s\n",
                     name, location, mount_point, strerror(errno));
@@ -87,7 +98,8 @@
     }
 
 done:
-    free(type);
+    free(fs_type);
+    free(partition_type);
     free(location);
     if (result != mount_point) free(mount_point);
     return StringValue(result);
@@ -153,22 +165,29 @@
 }
 
 
-// format(type, location)
+// format(fs_type, partition_type, location)
 //
-//    type="MTD"  location=partition
+//    fs_type="yaffs2" partition_type="MTD"     location=partition
+//    fs_type="ext4"   partition_type="EMMC"    location=device
 Value* FormatFn(const char* name, State* state, int argc, Expr* argv[]) {
     char* result = NULL;
-    if (argc != 2) {
-        return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc);
+    if (argc != 3) {
+        return ErrorAbort(state, "%s() expects 3 args, got %d", name, argc);
     }
-    char* type;
+    char* fs_type;
+    char* partition_type;
     char* location;
-    if (ReadArgs(state, argv, 2, &type, &location) < 0) {
+    if (ReadArgs(state, argv, 3, &fs_type, &partition_type, &location) < 0) {
         return NULL;
     }
 
-    if (strlen(type) == 0) {
-        ErrorAbort(state, "type argument to %s() can't be empty", name);
+    if (strlen(fs_type) == 0) {
+        ErrorAbort(state, "fs_type argument to %s() can't be empty", name);
+        goto done;
+    }
+    if (strlen(partition_type) == 0) {
+        ErrorAbort(state, "partition_type argument to %s() can't be empty",
+                   name);
         goto done;
     }
     if (strlen(location) == 0) {
@@ -176,17 +195,31 @@
         goto done;
     }
 
-    if (strcmp(type, "MTD") == 0 || strcmp(type, "MMC") == 0) {
+    if (strcmp(partition_type, "MTD") == 0 || strcmp(partition_type, "MMC") == 0) {
         if (0 != erase_partition(location, NULL)) {
             result = strdup("");
             goto done;
         }
+#ifdef USE_EXT4
+    } else if (strcmp(fs_type, "ext4") == 0) {
+        reset_ext4fs_info();
+        int status = make_ext4fs(location, NULL, NULL, 0, 0, 0);
+        if (status != 0) {
+            fprintf(stderr, "%s: make_ext4fs failed (%d) on %s",
+                    name, status, location);
+            result = strdup("");
+            goto done;
+        }
+        result = location;
+#endif
     } else {
-        fprintf(stderr, "%s: unsupported type \"%s\"", name, type);
+        fprintf(stderr, "%s: unsupported fs_type \"%s\" partition_type \"%s\"",
+                name, fs_type, partition_type);
     }
     result = location;
 done:
-    free(type);
+    free(fs_type);
+    free(partition_type);
     if (result != location) free(location);
     return StringValue(result);
 }
diff --git a/updater/updater.c b/updater/updater.c
index 6537a94..aa626d2 100644
--- a/updater/updater.c
+++ b/updater/updater.c
@@ -136,7 +136,9 @@
         free(result);
     }
 
-    mzCloseZipArchive(&za);
+    if (updater_info.package_zip) {
+        mzCloseZipArchive(updater_info.package_zip);
+    }
     free(script);
 
     return 0;