am ddaaccb3: am 29759e9d: Merge commit \'25c2b2f670a3b11f7ce78eccde10c1a79188c9a8\' into HEAD

* commit 'ddaaccb3db623ff6f19b7dadf51d41cc46bb05c3':
diff --git a/Android.mk b/Android.mk
index e51862c..577679c 100644
--- a/Android.mk
+++ b/Android.mk
@@ -35,6 +35,7 @@
 RECOVERY_API_VERSION := 3
 RECOVERY_FSTAB_VERSION := 2
 LOCAL_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION)
+LOCAL_CFLAGS += -Wno-unused-parameter
 
 LOCAL_STATIC_LIBRARIES := \
     libext4_utils_static \
@@ -56,7 +57,7 @@
 
 ifeq ($(TARGET_USERIMAGES_USE_EXT4), true)
     LOCAL_CFLAGS += -DUSE_EXT4
-    LOCAL_C_INCLUDES += system/extras/ext4_utils
+    LOCAL_C_INCLUDES += system/extras/ext4_utils system/vold
     LOCAL_STATIC_LIBRARIES += libext4_utils_static libz
 endif
 
@@ -73,6 +74,7 @@
 endif
 
 LOCAL_C_INCLUDES += system/extras/ext4_utils
+LOCAL_C_INCLUDES += external/openssl/include
 
 include $(BUILD_EXECUTABLE)
 
@@ -89,6 +91,7 @@
 LOCAL_FORCE_STATIC_EXECUTABLE := true
 LOCAL_MODULE_TAGS := tests
 LOCAL_CFLAGS += -DNO_RECOVERY_MOUNT
+LOCAL_CFLAGS += -Wno-unused-parameter
 LOCAL_SRC_FILES := \
     verifier_test.cpp \
     asn1_decoder.cpp \
@@ -97,6 +100,7 @@
 LOCAL_STATIC_LIBRARIES := \
     libmincrypt \
     libminui \
+    libminzip \
     libcutils \
     libstdc++ \
     libc
@@ -104,12 +108,12 @@
 
 
 include $(LOCAL_PATH)/minui/Android.mk \
-    $(LOCAL_PATH)/minelf/Android.mk \
     $(LOCAL_PATH)/minzip/Android.mk \
     $(LOCAL_PATH)/minadbd/Android.mk \
     $(LOCAL_PATH)/mtdutils/Android.mk \
     $(LOCAL_PATH)/tests/Android.mk \
     $(LOCAL_PATH)/tools/Android.mk \
     $(LOCAL_PATH)/edify/Android.mk \
+    $(LOCAL_PATH)/uncrypt/Android.mk \
     $(LOCAL_PATH)/updater/Android.mk \
     $(LOCAL_PATH)/applypatch/Android.mk
diff --git a/applypatch/Android.mk b/applypatch/Android.mk
index ef57f24..4984093 100644
--- a/applypatch/Android.mk
+++ b/applypatch/Android.mk
@@ -28,7 +28,7 @@
 LOCAL_SRC_FILES := main.c
 LOCAL_MODULE := applypatch
 LOCAL_C_INCLUDES += bootable/recovery
-LOCAL_STATIC_LIBRARIES += libapplypatch libmtdutils libmincrypt libbz libminelf
+LOCAL_STATIC_LIBRARIES += libapplypatch libmtdutils libmincrypt libbz
 LOCAL_SHARED_LIBRARIES += libz libcutils libstdc++ libc
 
 include $(BUILD_EXECUTABLE)
@@ -40,7 +40,7 @@
 LOCAL_FORCE_STATIC_EXECUTABLE := true
 LOCAL_MODULE_TAGS := eng
 LOCAL_C_INCLUDES += bootable/recovery
-LOCAL_STATIC_LIBRARIES += libapplypatch libmtdutils libmincrypt libbz libminelf
+LOCAL_STATIC_LIBRARIES += libapplypatch libmtdutils libmincrypt libbz
 LOCAL_STATIC_LIBRARIES += libz libcutils libstdc++ libc
 
 include $(BUILD_EXECUTABLE)
diff --git a/applypatch/applypatch.c b/applypatch/applypatch.c
index 9e631dd..60e9e4a 100644
--- a/applypatch/applypatch.c
+++ b/applypatch/applypatch.c
@@ -24,6 +24,7 @@
 #include <sys/types.h>
 #include <fcntl.h>
 #include <unistd.h>
+#include <stdbool.h>
 
 #include "mincrypt/sha.h"
 #include "applypatch.h"
@@ -44,14 +45,11 @@
 
 static int mtd_partitions_scanned = 0;
 
-// Read a file into memory; optionally (retouch_flag == RETOUCH_DO_MASK) mask
-// the retouched entries back to their original value (such that SHA-1 checks
-// don't fail due to randomization); store the file contents and associated
+// Read a file into memory; store the file contents and associated
 // metadata in *file.
 //
 // Return 0 on success.
-int LoadFileContents(const char* filename, FileContents* file,
-                     int retouch_flag) {
+int LoadFileContents(const char* filename, FileContents* file) {
     file->data = NULL;
 
     // A special 'filename' beginning with "MTD:" or "EMMC:" means to
@@ -87,20 +85,6 @@
     }
     fclose(f);
 
-    // apply_patch[_check] functions are blind to randomization. Randomization
-    // is taken care of in [Undo]RetouchBinariesFn. If there is a mismatch
-    // within a file, this means the file is assumed "corrupt" for simplicity.
-    if (retouch_flag) {
-        int32_t desired_offset = 0;
-        if (retouch_mask_data(file->data, file->size,
-                              &desired_offset, NULL) != RETOUCH_DATA_MATCHED) {
-            printf("error trying to mask retouch entries\n");
-            free(file->data);
-            file->data = NULL;
-            return -1;
-        }
-    }
-
     SHA_hash(file->data, file->size, file->sha1);
     return 0;
 }
@@ -579,7 +563,7 @@
     // LoadFileContents is successful.  (Useful for reading
     // partitions, where the filename encodes the sha1s; no need to
     // check them twice.)
-    if (LoadFileContents(filename, &file, RETOUCH_DO_MASK) != 0 ||
+    if (LoadFileContents(filename, &file) != 0 ||
         (num_patches > 0 &&
          FindMatchingPatch(file.sha1, patch_sha1_str, num_patches) < 0)) {
         printf("file \"%s\" doesn't have any of expected "
@@ -594,7 +578,7 @@
         // exists and matches the sha1 we're looking for, the check still
         // passes.
 
-        if (LoadFileContents(CACHE_TEMP_SOURCE, &file, RETOUCH_DO_MASK) != 0) {
+        if (LoadFileContents(CACHE_TEMP_SOURCE, &file) != 0) {
             printf("failed to load cache file\n");
             return 1;
         }
@@ -730,8 +714,7 @@
     const Value* copy_patch_value = NULL;
 
     // We try to load the target file into the source_file object.
-    if (LoadFileContents(target_filename, &source_file,
-                         RETOUCH_DO_MASK) == 0) {
+    if (LoadFileContents(target_filename, &source_file) == 0) {
         if (memcmp(source_file.sha1, target_sha1, SHA_DIGEST_SIZE) == 0) {
             // The early-exit case:  the patch was already applied, this file
             // has the desired hash, nothing for us to do.
@@ -750,8 +733,7 @@
         // target file, or we did but it's different from the source file.
         free(source_file.data);
         source_file.data = NULL;
-        LoadFileContents(source_filename, &source_file,
-                         RETOUCH_DO_MASK);
+        LoadFileContents(source_filename, &source_file);
     }
 
     if (source_file.data != NULL) {
@@ -767,8 +749,7 @@
         source_file.data = NULL;
         printf("source file is bad; trying copy\n");
 
-        if (LoadFileContents(CACHE_TEMP_SOURCE, &copy_file,
-                             RETOUCH_DO_MASK) < 0) {
+        if (LoadFileContents(CACHE_TEMP_SOURCE, &copy_file) < 0) {
             // fail.
             printf("failed to read copy file\n");
             return 1;
diff --git a/applypatch/applypatch.h b/applypatch/applypatch.h
index f1f13a1..ee54c24 100644
--- a/applypatch/applypatch.h
+++ b/applypatch/applypatch.h
@@ -19,7 +19,6 @@
 
 #include <sys/stat.h>
 #include "mincrypt/sha.h"
-#include "minelf/Retouch.h"
 #include "edify/expr.h"
 
 typedef struct _Patch {
@@ -61,8 +60,7 @@
                      int num_patches,
                      char** const patch_sha1_str);
 
-int LoadFileContents(const char* filename, FileContents* file,
-                     int retouch_flag);
+int LoadFileContents(const char* filename, FileContents* file);
 int SaveFileContents(const char* filename, const FileContents* file);
 void FreeFileContents(FileContents* file);
 int FindMatchingPatch(uint8_t* sha1, char* const * const patch_sha1_str,
diff --git a/applypatch/main.c b/applypatch/main.c
index f61db5d..8e9fe80 100644
--- a/applypatch/main.c
+++ b/applypatch/main.c
@@ -74,7 +74,7 @@
             (*patches)[i] = NULL;
         } else {
             FileContents fc;
-            if (LoadFileContents(colon, &fc, RETOUCH_DONT_MASK) != 0) {
+            if (LoadFileContents(colon, &fc) != 0) {
                 goto abort;
             }
             (*patches)[i] = malloc(sizeof(Value));
@@ -103,7 +103,7 @@
     Value* bonus = NULL;
     if (argc >= 3 && strcmp(argv[1], "-b") == 0) {
         FileContents fc;
-        if (LoadFileContents(argv[2], &fc, RETOUCH_DONT_MASK) != 0) {
+        if (LoadFileContents(argv[2], &fc) != 0) {
             printf("failed to load bonus file %s\n", argv[2]);
             return 1;
         }
diff --git a/default_device.cpp b/default_device.cpp
index 648eaec..a25f05f 100644
--- a/default_device.cpp
+++ b/default_device.cpp
@@ -29,22 +29,14 @@
                                "apply update from ADB",
                                "wipe data/factory reset",
                                "wipe cache partition",
+                               "reboot to bootloader",
+                               "power down",
                                NULL };
 
-class DefaultUI : public ScreenRecoveryUI {
-  public:
-    virtual KeyAction CheckKey(int key) {
-        if (key == KEY_HOME) {
-            return TOGGLE;
-        }
-        return ENQUEUE;
-    }
-};
-
 class DefaultDevice : public Device {
   public:
     DefaultDevice() :
-        ui(new DefaultUI) {
+        ui(new ScreenRecoveryUI) {
     }
 
     RecoveryUI* GetUI() { return ui; }
@@ -61,6 +53,7 @@
                 return kHighlightUp;
 
               case KEY_ENTER:
+              case KEY_POWER:
                 return kInvokeItem;
             }
         }
@@ -74,6 +67,8 @@
           case 1: return APPLY_ADB_SIDELOAD;
           case 2: return WIPE_DATA;
           case 3: return WIPE_CACHE;
+          case 4: return REBOOT_BOOTLOADER;
+          case 5: return SHUTDOWN;
           default: return NO_ACTION;
         }
     }
diff --git a/device.h b/device.h
index 583de75..df71377 100644
--- a/device.h
+++ b/device.h
@@ -66,7 +66,8 @@
     virtual int HandleMenuKey(int key, int visible) = 0;
 
     enum BuiltinAction { NO_ACTION, REBOOT, APPLY_EXT, APPLY_CACHE,
-                         APPLY_ADB_SIDELOAD, WIPE_DATA, WIPE_CACHE };
+                         APPLY_ADB_SIDELOAD, WIPE_DATA, WIPE_CACHE,
+                         REBOOT_BOOTLOADER, SHUTDOWN };
 
     // Perform a recovery action selected from the menu.
     // 'menu_position' will be the item number of the selected menu
diff --git a/edify/Android.mk b/edify/Android.mk
index fac0ba7..61ed6fa 100644
--- a/edify/Android.mk
+++ b/edify/Android.mk
@@ -23,6 +23,7 @@
 LOCAL_CFLAGS := $(edify_cflags) -g -O0
 LOCAL_MODULE := edify
 LOCAL_YACCFLAGS := -v
+LOCAL_CFLAGS += -Wno-unused-parameter
 
 include $(BUILD_HOST_EXECUTABLE)
 
@@ -34,6 +35,7 @@
 LOCAL_SRC_FILES := $(edify_src_files)
 
 LOCAL_CFLAGS := $(edify_cflags)
+LOCAL_CFLAGS += -Wno-unused-parameter
 LOCAL_MODULE := libedify
 
 include $(BUILD_STATIC_LIBRARY)
diff --git a/edify/expr.c b/edify/expr.c
index a2f1f99..79f6282 100644
--- a/edify/expr.c
+++ b/edify/expr.c
@@ -287,13 +287,11 @@
 
     long l_int = strtol(left, &end, 10);
     if (left[0] == '\0' || *end != '\0') {
-        printf("[%s] is not an int\n", left);
         goto done;
     }
 
     long r_int = strtol(right, &end, 10);
     if (right[0] == '\0' || *end != '\0') {
-        printf("[%s] is not an int\n", right);
         goto done;
     }
 
diff --git a/edify/expr.h b/edify/expr.h
index 0d8ed8f..a9ed2f9 100644
--- a/edify/expr.h
+++ b/edify/expr.h
@@ -164,6 +164,8 @@
 // Free a Value object.
 void FreeValue(Value* v);
 
+int parse_string(const char* str, Expr** root, int* error_count);
+
 #ifdef __cplusplus
 }  // extern "C"
 #endif
diff --git a/edify/main.c b/edify/main.c
index 9e6bab7..b3fad53 100644
--- a/edify/main.c
+++ b/edify/main.c
@@ -30,9 +30,7 @@
 
     printf(".");
 
-    yy_scan_string(expr_str);
-    int error_count = 0;
-    error = yyparse(&e, &error_count);
+    int error_count = parse_string(expr_str, &e, &error_count);
     if (error > 0 || error_count > 0) {
         printf("error parsing \"%s\" (%d errors)\n",
                expr_str, error_count);
@@ -193,8 +191,7 @@
 
     Expr* root;
     int error_count = 0;
-    yy_scan_bytes(buffer, size);
-    int error = yyparse(&root, &error_count);
+    int error = parse_string(buffer, &root, &error_count);
     printf("parse returned %d; %d errors encountered\n", error, error_count);
     if (error == 0 || error_count > 0) {
 
diff --git a/edify/parser.y b/edify/parser.y
index 3f9ade1..f8fb2d1 100644
--- a/edify/parser.y
+++ b/edify/parser.y
@@ -29,6 +29,10 @@
 void yyerror(Expr** root, int* error_count, const char* s);
 int yyparse(Expr** root, int* error_count);
 
+struct yy_buffer_state;
+void yy_switch_to_buffer(struct yy_buffer_state* new_buffer);
+struct yy_buffer_state* yy_scan_string(const char* yystr);
+
 %}
 
 %locations
@@ -128,3 +132,8 @@
   printf("line %d col %d: %s\n", gLine, gColumn, s);
   ++*error_count;
 }
+
+int parse_string(const char* str, Expr** root, int* error_count) {
+    yy_switch_to_buffer(yy_scan_string(str));
+    return yyparse(root, error_count);
+}
diff --git a/etc/init.rc b/etc/init.rc
index 6e0595b..8ed0038 100644
--- a/etc/init.rc
+++ b/etc/init.rc
@@ -58,7 +58,7 @@
     critical
     seclabel u:r:ueventd:s0
 
-service healthd /sbin/healthd -n
+service healthd /sbin/healthd -r
     critical
     seclabel u:r:healthd:s0
 
diff --git a/install.cpp b/install.cpp
index 797a525..0bd7945 100644
--- a/install.cpp
+++ b/install.cpp
@@ -120,6 +120,7 @@
 
     pid_t pid = fork();
     if (pid == 0) {
+        umask(022);
         close(pipefd[0]);
         execv(binary, (char* const*)args);
         fprintf(stdout, "E:Can't run %s (%s)\n", binary, strerror(errno));
@@ -185,12 +186,22 @@
     ui->ShowProgress(VERIFICATION_PROGRESS_FRACTION, VERIFICATION_PROGRESS_TIME);
     LOGI("Update location: %s\n", path);
 
-    if (ensure_path_mounted(path) != 0) {
-        LOGE("Can't mount %s\n", path);
-        return INSTALL_CORRUPT;
+    // Map the update package into memory.
+    ui->Print("Opening update package...\n");
+
+    if (path) {
+        if (path[0] == '@') {
+            ensure_path_mounted(path+1);
+        } else {
+            ensure_path_mounted(path);
+        }
     }
 
-    ui->Print("Opening update package...\n");
+    MemMapping map;
+    if (sysMapFile(path, &map) != 0) {
+        LOGE("failed to map file\n");
+        return INSTALL_CORRUPT;
+    }
 
     int numKeys;
     Certificate* loadedKeys = load_keys(PUBLIC_KEYS_FILE, &numKeys);
@@ -203,27 +214,33 @@
     ui->Print("Verifying update package...\n");
 
     int err;
-    err = verify_file(path, loadedKeys, numKeys);
+    err = verify_file(map.addr, map.length, loadedKeys, numKeys);
     free(loadedKeys);
     LOGI("verify_file returned %d\n", err);
     if (err != VERIFY_SUCCESS) {
         LOGE("signature verification failed\n");
+        sysReleaseMap(&map);
         return INSTALL_CORRUPT;
     }
 
     /* Try to open the package.
      */
     ZipArchive zip;
-    err = mzOpenZipArchive(path, &zip);
+    err = mzOpenZipArchive(map.addr, map.length, &zip);
     if (err != 0) {
         LOGE("Can't open %s\n(%s)\n", path, err != -1 ? strerror(err) : "bad");
+        sysReleaseMap(&map);
         return INSTALL_CORRUPT;
     }
 
     /* Verify and install the contents of the package.
      */
     ui->Print("Installing update...\n");
-    return try_update_binary(path, &zip, wipe_cache);
+    int result = try_update_binary(path, &zip, wipe_cache);
+
+    sysReleaseMap(&map);
+
+    return result;
 }
 
 int
diff --git a/minelf/Retouch.c b/minelf/Retouch.c
deleted file mode 100644
index d75eec1..0000000
--- a/minelf/Retouch.c
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * 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 <errno.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <string.h>
-#include <strings.h>
-#include "Retouch.h"
-#include "applypatch/applypatch.h"
-
-typedef struct {
-    int32_t mmap_addr;
-    char tag[4]; /* 'P', 'R', 'E', ' ' */
-} prelink_info_t __attribute__((packed));
-
-#define false 0
-#define true 1
-
-static int32_t offs_prev;
-static uint32_t cont_prev;
-
-static void init_compression_state(void) {
-    offs_prev = 0;
-    cont_prev = 0;
-}
-
-// For details on the encoding used for relocation lists, please
-// refer to build/tools/retouch/retouch-prepare.c. The intent is to
-// save space by removing most of the inherent redundancy.
-
-static void decode_bytes(uint8_t *encoded_bytes, int encoded_size,
-                         int32_t *dst_offset, uint32_t *dst_contents) {
-    if (encoded_size == 2) {
-        *dst_offset = offs_prev + (((encoded_bytes[0]&0x60)>>5)+1)*4;
-
-        // if the original was negative, we need to 1-pad before applying delta
-        int32_t tmp = (((encoded_bytes[0] & 0x0000001f) << 8) |
-                       encoded_bytes[1]);
-        if (tmp & 0x1000) tmp = 0xffffe000 | tmp;
-        *dst_contents = cont_prev + tmp;
-    } else if (encoded_size == 3) {
-        *dst_offset = offs_prev + (((encoded_bytes[0]&0x30)>>4)+1)*4;
-
-        // if the original was negative, we need to 1-pad before applying delta
-        int32_t tmp = (((encoded_bytes[0] & 0x0000000f) << 16) |
-                       (encoded_bytes[1] << 8) |
-                       encoded_bytes[2]);
-        if (tmp & 0x80000) tmp = 0xfff00000 | tmp;
-        *dst_contents = cont_prev + tmp;
-    } else {
-        *dst_offset =
-          (encoded_bytes[0]<<24) |
-          (encoded_bytes[1]<<16) |
-          (encoded_bytes[2]<<8) |
-          encoded_bytes[3];
-        if (*dst_offset == 0x3fffffff) *dst_offset = -1;
-        *dst_contents =
-          (encoded_bytes[4]<<24) |
-          (encoded_bytes[5]<<16) |
-          (encoded_bytes[6]<<8) |
-          encoded_bytes[7];
-    }
-}
-
-static uint8_t *decode_in_memory(uint8_t *encoded_bytes,
-                                 int32_t *offset, uint32_t *contents) {
-    int input_size, charIx;
-    uint8_t input[8];
-
-    input[0] = *(encoded_bytes++);
-    if (input[0] & 0x80)
-        input_size = 2;
-    else if (input[0] & 0x40)
-        input_size = 3;
-    else
-        input_size = 8;
-
-    // we already read one byte..
-    charIx = 1;
-    while (charIx < input_size) {
-        input[charIx++] = *(encoded_bytes++);
-    }
-
-    // depends on the decoder state!
-    decode_bytes(input, input_size, offset, contents);
-
-    offs_prev = *offset;
-    cont_prev = *contents;
-
-    return encoded_bytes;
-}
-
-int retouch_mask_data(uint8_t *binary_object,
-                      int32_t binary_size,
-                      int32_t *desired_offset,
-                      int32_t *retouch_offset) {
-    retouch_info_t *r_info;
-    prelink_info_t *p_info;
-
-    int32_t target_offset = 0;
-    if (desired_offset) target_offset = *desired_offset;
-
-    int32_t p_offs = binary_size-sizeof(prelink_info_t); // prelink_info_t
-    int32_t r_offs = p_offs-sizeof(retouch_info_t); // retouch_info_t
-    int32_t b_offs; // retouch data blob
-
-    // If not retouched, we say it was a match. This might get invoked on
-    // non-retouched binaries, so that's why we need to do this.
-    if (retouch_offset != NULL) *retouch_offset = target_offset;
-    if (r_offs < 0) return (desired_offset == NULL) ?
-                      RETOUCH_DATA_NOTAPPLICABLE : RETOUCH_DATA_MATCHED;
-    p_info = (prelink_info_t *)(binary_object+p_offs);
-    r_info = (retouch_info_t *)(binary_object+r_offs);
-    if (strncmp(p_info->tag, "PRE ", 4) ||
-        strncmp(r_info->tag, "RETOUCH ", 8))
-        return (desired_offset == NULL) ?
-          RETOUCH_DATA_NOTAPPLICABLE : RETOUCH_DATA_MATCHED;
-
-    b_offs = r_offs-r_info->blob_size;
-    if (b_offs < 0) {
-        printf("negative binary offset: %d = %d - %d\n",
-               b_offs, r_offs, r_info->blob_size);
-        return RETOUCH_DATA_ERROR;
-    }
-    uint8_t *b_ptr = binary_object+b_offs;
-
-    // Retouched: let's go through the work then.
-    int32_t offset_candidate = target_offset;
-    bool offset_set = false, offset_mismatch = false;
-    init_compression_state();
-    while (b_ptr < (uint8_t *)r_info) {
-        int32_t retouch_entry_offset;
-        uint32_t *retouch_entry;
-        uint32_t retouch_original_value;
-
-        b_ptr = decode_in_memory(b_ptr,
-                                 &retouch_entry_offset,
-                                 &retouch_original_value);
-        if (retouch_entry_offset < (-1) ||
-            retouch_entry_offset >= b_offs) {
-            printf("bad retouch_entry_offset: %d", retouch_entry_offset);
-            return RETOUCH_DATA_ERROR;
-        }
-
-        // "-1" means this is the value in prelink_info_t, which also gets
-        // randomized.
-        if (retouch_entry_offset == -1)
-            retouch_entry = (uint32_t *)&(p_info->mmap_addr);
-        else
-            retouch_entry = (uint32_t *)(binary_object+retouch_entry_offset);
-
-        if (desired_offset)
-            *retouch_entry = retouch_original_value + target_offset;
-
-        // Infer the randomization shift, compare to previously inferred.
-        int32_t offset_of_this_entry = (int32_t)(*retouch_entry-
-                                                 retouch_original_value);
-        if (!offset_set) {
-            offset_candidate = offset_of_this_entry;
-            offset_set = true;
-        } else {
-            if (offset_candidate != offset_of_this_entry) {
-                offset_mismatch = true;
-                printf("offset is mismatched: %d, this entry is %d,"
-                       " original 0x%x @ 0x%x",
-                       offset_candidate, offset_of_this_entry,
-                       retouch_original_value, retouch_entry_offset);
-            }
-        }
-    }
-    if (b_ptr > (uint8_t *)r_info) {
-        printf("b_ptr went too far: %p, while r_info is %p",
-               b_ptr, r_info);
-        return RETOUCH_DATA_ERROR;
-    }
-
-    if (offset_mismatch) return RETOUCH_DATA_MISMATCHED;
-    if (retouch_offset != NULL) *retouch_offset = offset_candidate;
-    return RETOUCH_DATA_MATCHED;
-}
diff --git a/minelf/Retouch.h b/minelf/Retouch.h
deleted file mode 100644
index 13bacd5..0000000
--- a/minelf/Retouch.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef _MINELF_RETOUCH
-#define _MINELF_RETOUCH
-
-#include <stdbool.h>
-#include <sys/types.h>
-
-typedef struct {
-  char tag[8];        /* "RETOUCH ", not zero-terminated */
-  uint32_t blob_size; /* in bytes, located right before this struct */
-} retouch_info_t __attribute__((packed));
-
-#define RETOUCH_DONT_MASK           0
-#define RETOUCH_DO_MASK             1
-
-#define RETOUCH_DATA_ERROR          0 // This is bad. Should not happen.
-#define RETOUCH_DATA_MATCHED        1 // Up to an uniform random offset.
-#define RETOUCH_DATA_MISMATCHED     2 // Partially randomized, or total mess.
-#define RETOUCH_DATA_NOTAPPLICABLE  3 // Not retouched. Only when inferring.
-
-// Mask retouching in-memory. Used before apply_patch[_check].
-// Also used to determine status of retouching after a crash.
-//
-// If desired_offset is not NULL, then apply retouching instead,
-// and return that in retouch_offset.
-int retouch_mask_data(uint8_t *binary_object,
-                      int32_t binary_size,
-                      int32_t *desired_offset,
-                      int32_t *retouch_offset);
-#endif
diff --git a/minui/events.c b/minui/events.c
index 2918afa..df7dad4 100644
--- a/minui/events.c
+++ b/minui/events.c
@@ -18,7 +18,7 @@
 #include <stdlib.h>
 #include <fcntl.h>
 #include <dirent.h>
-#include <sys/poll.h>
+#include <sys/epoll.h>
 
 #include <linux/input.h>
 
@@ -34,11 +34,15 @@
     ((array)[(bit)/BITS_PER_LONG] & (1 << ((bit) % BITS_PER_LONG)))
 
 struct fd_info {
+    int fd;
     ev_callback cb;
     void *data;
 };
 
-static struct pollfd ev_fds[MAX_DEVICES + MAX_MISC_FDS];
+static int epollfd;
+static struct epoll_event polledevents[MAX_DEVICES + MAX_MISC_FDS];
+static int npolledevents;
+
 static struct fd_info ev_fdinfo[MAX_DEVICES + MAX_MISC_FDS];
 
 static unsigned ev_count = 0;
@@ -50,6 +54,12 @@
     DIR *dir;
     struct dirent *de;
     int fd;
+    struct epoll_event ev;
+    bool epollctlfail = false;
+
+    epollfd = epoll_create(MAX_DEVICES + MAX_MISC_FDS);
+    if (epollfd == -1)
+        return -1;
 
     dir = opendir("/dev/input");
     if(dir != 0) {
@@ -74,8 +84,15 @@
                 continue;
             }
 
-            ev_fds[ev_count].fd = fd;
-            ev_fds[ev_count].events = POLLIN;
+            ev.events = EPOLLIN | EPOLLWAKEUP;
+            ev.data.ptr = (void *)&ev_fdinfo[ev_count];
+            if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev)) {
+                close(fd);
+                epollctlfail = true;
+                continue;
+            }
+
+            ev_fdinfo[ev_count].fd = fd;
             ev_fdinfo[ev_count].cb = input_cb;
             ev_fdinfo[ev_count].data = data;
             ev_count++;
@@ -84,59 +101,78 @@
         }
     }
 
+    if (epollctlfail && !ev_count) {
+        close(epollfd);
+        epollfd = -1;
+        return -1;
+    }
+
     return 0;
 }
 
 int ev_add_fd(int fd, ev_callback cb, void *data)
 {
+    struct epoll_event ev;
+    int ret;
+
     if (ev_misc_count == MAX_MISC_FDS || cb == NULL)
         return -1;
 
-    ev_fds[ev_count].fd = fd;
-    ev_fds[ev_count].events = POLLIN;
-    ev_fdinfo[ev_count].cb = cb;
-    ev_fdinfo[ev_count].data = data;
-    ev_count++;
-    ev_misc_count++;
-    return 0;
+    ev.events = EPOLLIN | EPOLLWAKEUP;
+    ev.data.ptr = (void *)&ev_fdinfo[ev_count];
+    ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev);
+    if (!ret) {
+        ev_fdinfo[ev_count].fd = fd;
+        ev_fdinfo[ev_count].cb = cb;
+        ev_fdinfo[ev_count].data = data;
+        ev_count++;
+        ev_misc_count++;
+    }
+
+    return ret;
+}
+
+int ev_get_epollfd(void)
+{
+    return epollfd;
 }
 
 void ev_exit(void)
 {
     while (ev_count > 0) {
-        close(ev_fds[--ev_count].fd);
+        close(ev_fdinfo[--ev_count].fd);
     }
     ev_misc_count = 0;
     ev_dev_count = 0;
+    close(epollfd);
 }
 
 int ev_wait(int timeout)
 {
-    int r;
-
-    r = poll(ev_fds, ev_count, timeout);
-    if (r <= 0)
+    npolledevents = epoll_wait(epollfd, polledevents, ev_count, timeout);
+    if (npolledevents <= 0)
         return -1;
     return 0;
 }
 
 void ev_dispatch(void)
 {
-    unsigned n;
+    int n;
     int ret;
 
-    for (n = 0; n < ev_count; n++) {
-        ev_callback cb = ev_fdinfo[n].cb;
-        if (cb && (ev_fds[n].revents & ev_fds[n].events))
-            cb(ev_fds[n].fd, ev_fds[n].revents, ev_fdinfo[n].data);
+    for (n = 0; n < npolledevents; n++) {
+        struct fd_info *fdi = polledevents[n].data.ptr;
+        ev_callback cb = fdi->cb;
+        if (cb)
+            cb(fdi->fd, polledevents[n].events, fdi->data);
     }
 }
 
-int ev_get_input(int fd, short revents, struct input_event *ev)
+int ev_get_input(int fd, uint32_t epevents, struct input_event *ev)
 {
     int r;
 
-    if (revents & POLLIN) {
+    if (epevents & EPOLLIN) {
         r = read(fd, ev, sizeof(*ev));
         if (r == sizeof(*ev))
             return 0;
@@ -157,11 +193,11 @@
         memset(key_bits, 0, sizeof(key_bits));
         memset(ev_bits, 0, sizeof(ev_bits));
 
-        ret = ioctl(ev_fds[i].fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits);
+        ret = ioctl(ev_fdinfo[i].fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits);
         if (ret < 0 || !test_bit(EV_KEY, ev_bits))
             continue;
 
-        ret = ioctl(ev_fds[i].fd, EVIOCGKEY(sizeof(key_bits)), key_bits);
+        ret = ioctl(ev_fdinfo[i].fd, EVIOCGKEY(sizeof(key_bits)), key_bits);
         if (ret < 0)
             continue;
 
diff --git a/minui/minui.h b/minui/minui.h
index d8d53fa..733b675 100644
--- a/minui/minui.h
+++ b/minui/minui.h
@@ -60,7 +60,7 @@
 // see http://www.mjmwired.net/kernel/Documentation/input/ for info.
 struct input_event;
 
-typedef int (*ev_callback)(int fd, short revents, void *data);
+typedef int (*ev_callback)(int fd, uint32_t epevents, void *data);
 typedef int (*ev_set_key_callback)(int code, int value, void *data);
 
 int ev_init(ev_callback input_cb, void *data);
@@ -75,8 +75,9 @@
  */
 int ev_wait(int timeout);
 
-int ev_get_input(int fd, short revents, struct input_event *ev);
+int ev_get_input(int fd, uint32_t epevents, struct input_event *ev);
 void ev_dispatch(void);
+int ev_get_epollfd(void);
 
 // Resources
 
diff --git a/minzip/SysUtil.c b/minzip/SysUtil.c
index 31c76d6..ac6f5c3 100644
--- a/minzip/SysUtil.c
+++ b/minzip/SysUtil.c
@@ -8,42 +8,17 @@
 #include <unistd.h>
 #include <string.h>
 #include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
 #include <limits.h>
 #include <errno.h>
 #include <assert.h>
 
-#define LOG_TAG "minzip"
+#define LOG_TAG "sysutil"
 #include "Log.h"
 #include "SysUtil.h"
 
-/*
- * Having trouble finding a portable way to get this.  sysconf(_SC_PAGE_SIZE)
- * seems appropriate, but we don't have that on the device.  Some systems
- * have getpagesize(2), though the linux man page has some odd cautions.
- */
-#define DEFAULT_PAGE_SIZE   4096
-
-
-/*
- * Create an anonymous shared memory segment large enough to hold "length"
- * bytes.  The actual segment may be larger because mmap() operates on
- * page boundaries (usually 4K).
- */
-static void* sysCreateAnonShmem(size_t length)
-{
-    void* ptr;
-
-    ptr = mmap(NULL, length, PROT_READ | PROT_WRITE,
-            MAP_SHARED | MAP_ANON, -1, 0);
-    if (ptr == MAP_FAILED) {
-        LOGW("mmap(%d, RW, SHARED|ANON) failed: %s\n", (int) length,
-            strerror(errno));
-        return NULL;
-    }
-
-    return ptr;
-}
-
 static int getFileStartAndLength(int fd, off_t *start_, size_t *length_)
 {
     off_t start, end;
@@ -74,48 +49,13 @@
 }
 
 /*
- * Pull the contents of a file into an new shared memory segment.  We grab
- * everything from fd's current offset on.
- *
- * We need to know the length ahead of time so we can allocate a segment
- * of sufficient size.
- */
-int sysLoadFileInShmem(int fd, MemMapping* pMap)
-{
-    off_t start;
-    size_t length, actual;
-    void* memPtr;
-
-    assert(pMap != NULL);
-
-    if (getFileStartAndLength(fd, &start, &length) < 0)
-        return -1;
-
-    memPtr = sysCreateAnonShmem(length);
-    if (memPtr == NULL)
-        return -1;
-
-    pMap->baseAddr = pMap->addr = memPtr;
-    pMap->baseLength = pMap->length = length;
-
-    actual = TEMP_FAILURE_RETRY(read(fd, memPtr, length));
-    if (actual != length) {
-        LOGE("only read %d of %d bytes\n", (int) actual, (int) length);
-        sysReleaseShmem(pMap);
-        return -1;
-    }
-
-    return 0;
-}
-
-/*
- * Map a file (from fd's current offset) into a shared, read-only memory
+ * Map a file (from fd's current offset) into a private, read-only memory
  * segment.  The file offset must be a multiple of the page size.
  *
  * On success, returns 0 and fills out "pMap".  On failure, returns a nonzero
  * value and does not disturb "pMap".
  */
-int sysMapFileInShmem(int fd, MemMapping* pMap)
+static int sysMapFD(int fd, MemMapping* pMap)
 {
     off_t start;
     size_t length;
@@ -126,87 +66,148 @@
     if (getFileStartAndLength(fd, &start, &length) < 0)
         return -1;
 
-    memPtr = mmap(NULL, length, PROT_READ, MAP_FILE | MAP_SHARED, fd, start);
+    memPtr = mmap(NULL, length, PROT_READ, MAP_PRIVATE, fd, start);
     if (memPtr == MAP_FAILED) {
-        LOGW("mmap(%d, R, FILE|SHARED, %d, %d) failed: %s\n", (int) length,
+        LOGW("mmap(%d, R, PRIVATE, %d, %d) failed: %s\n", (int) length,
             fd, (int) start, strerror(errno));
         return -1;
     }
 
-    pMap->baseAddr = pMap->addr = memPtr;
-    pMap->baseLength = pMap->length = length;
+    pMap->addr = memPtr;
+    pMap->length = length;
+    pMap->range_count = 1;
+    pMap->ranges = malloc(sizeof(MappedRange));
+    pMap->ranges[0].addr = memPtr;
+    pMap->ranges[0].length = length;
 
     return 0;
 }
 
-/*
- * Map part of a file (from fd's current offset) into a shared, read-only
- * memory segment.
- *
- * On success, returns 0 and fills out "pMap".  On failure, returns a nonzero
- * value and does not disturb "pMap".
- */
-int sysMapFileSegmentInShmem(int fd, off_t start, long length,
-    MemMapping* pMap)
+static int sysMapBlockFile(FILE* mapf, MemMapping* pMap)
 {
-    off_t dummy;
-    size_t fileLength, actualLength;
-    off_t actualStart;
-    int adjust;
-    void* memPtr;
+    char block_dev[PATH_MAX+1];
+    size_t size;
+    unsigned int blksize;
+    unsigned int blocks;
+    unsigned int range_count;
+    unsigned int i;
 
-    assert(pMap != NULL);
-
-    if (getFileStartAndLength(fd, &dummy, &fileLength) < 0)
+    if (fgets(block_dev, sizeof(block_dev), mapf) == NULL) {
+        LOGW("failed to read block device from header\n");
         return -1;
+    }
+    for (i = 0; i < sizeof(block_dev); ++i) {
+        if (block_dev[i] == '\n') {
+            block_dev[i] = 0;
+            break;
+        }
+    }
 
-    if (start + length > (long)fileLength) {
-        LOGW("bad segment: st=%d len=%ld flen=%d\n",
-            (int) start, length, (int) fileLength);
+    if (fscanf(mapf, "%zu %u\n%u\n", &size, &blksize, &range_count) != 3) {
+        LOGW("failed to parse block map header\n");
         return -1;
     }
 
-    /* adjust to be page-aligned */
-    adjust = start % DEFAULT_PAGE_SIZE;
-    actualStart = start - adjust;
-    actualLength = length + adjust;
+    blocks = ((size-1) / blksize) + 1;
 
-    memPtr = mmap(NULL, actualLength, PROT_READ, MAP_FILE | MAP_SHARED,
-                fd, actualStart);
-    if (memPtr == MAP_FAILED) {
-        LOGW("mmap(%d, R, FILE|SHARED, %d, %d) failed: %s\n",
-            (int) actualLength, fd, (int) actualStart, strerror(errno));
+    pMap->range_count = range_count;
+    pMap->ranges = malloc(range_count * sizeof(MappedRange));
+    memset(pMap->ranges, 0, range_count * sizeof(MappedRange));
+
+    // Reserve enough contiguous address space for the whole file.
+    unsigned char* reserve;
+    reserve = mmap64(NULL, blocks * blksize, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0);
+    if (reserve == MAP_FAILED) {
+        LOGW("failed to reserve address space: %s\n", strerror(errno));
         return -1;
     }
 
-    pMap->baseAddr = memPtr;
-    pMap->baseLength = actualLength;
-    pMap->addr = (char*)memPtr + adjust;
-    pMap->length = length;
+    pMap->ranges[range_count-1].addr = reserve;
+    pMap->ranges[range_count-1].length = blocks * blksize;
 
-    LOGVV("mmap seg (st=%d ln=%d): bp=%p bl=%d ad=%p ln=%d\n",
-        (int) start, (int) length,
-        pMap->baseAddr, (int) pMap->baseLength,
-        pMap->addr, (int) pMap->length);
+    int fd = open(block_dev, O_RDONLY);
+    if (fd < 0) {
+        LOGW("failed to open block device %s: %s\n", block_dev, strerror(errno));
+        return -1;
+    }
 
+    unsigned char* next = reserve;
+    for (i = 0; i < range_count; ++i) {
+        int start, end;
+        if (fscanf(mapf, "%d %d\n", &start, &end) != 2) {
+            LOGW("failed to parse range %d in block map\n", i);
+            return -1;
+        }
+
+        void* addr = mmap64(next, (end-start)*blksize, PROT_READ, MAP_PRIVATE | MAP_FIXED, fd, ((off64_t)start)*blksize);
+        if (addr == MAP_FAILED) {
+            LOGW("failed to map block %d: %s\n", i, strerror(errno));
+            return -1;
+        }
+        pMap->ranges[i].addr = addr;
+        pMap->ranges[i].length = (end-start)*blksize;
+
+        next += pMap->ranges[i].length;
+    }
+
+    pMap->addr = reserve;
+    pMap->length = size;
+
+    LOGI("mmapped %d ranges\n", range_count);
+
+    return 0;
+}
+
+int sysMapFile(const char* fn, MemMapping* pMap)
+{
+    memset(pMap, 0, sizeof(*pMap));
+
+    if (fn && fn[0] == '@') {
+        // A map of blocks
+        FILE* mapf = fopen(fn+1, "r");
+        if (mapf == NULL) {
+            LOGV("Unable to open '%s': %s\n", fn+1, strerror(errno));
+            return -1;
+        }
+
+        if (sysMapBlockFile(mapf, pMap) != 0) {
+            LOGW("Map of '%s' failed\n", fn);
+            return -1;
+        }
+
+        fclose(mapf);
+    } else {
+        // This is a regular file.
+        int fd = open(fn, O_RDONLY, 0);
+        if (fd < 0) {
+            LOGE("Unable to open '%s': %s\n", fn, strerror(errno));
+            return -1;
+        }
+
+        if (sysMapFD(fd, pMap) != 0) {
+            LOGE("Map of '%s' failed\n", fn);
+            close(fd);
+            return -1;
+        }
+
+        close(fd);
+    }
     return 0;
 }
 
 /*
  * Release a memory mapping.
  */
-void sysReleaseShmem(MemMapping* pMap)
+void sysReleaseMap(MemMapping* pMap)
 {
-    if (pMap->baseAddr == NULL && pMap->baseLength == 0)
-        return;
-
-    if (munmap(pMap->baseAddr, pMap->baseLength) < 0) {
-        LOGW("munmap(%p, %d) failed: %s\n",
-            pMap->baseAddr, (int)pMap->baseLength, strerror(errno));
-    } else {
-        LOGV("munmap(%p, %d) succeeded\n", pMap->baseAddr, pMap->baseLength);
-        pMap->baseAddr = NULL;
-        pMap->baseLength = 0;
+    int i;
+    for (i = 0; i < pMap->range_count; ++i) {
+        if (munmap(pMap->ranges[i].addr, pMap->ranges[i].length) < 0) {
+            LOGW("munmap(%p, %d) failed: %s\n",
+                 pMap->ranges[i].addr, (int)pMap->ranges[i].length, strerror(errno));
+        }
     }
+    free(pMap->ranges);
+    pMap->ranges = NULL;
+    pMap->range_count = 0;
 }
-
diff --git a/minzip/SysUtil.h b/minzip/SysUtil.h
index ec3a4bc..7adff1e 100644
--- a/minzip/SysUtil.h
+++ b/minzip/SysUtil.h
@@ -6,56 +6,47 @@
 #ifndef _MINZIP_SYSUTIL
 #define _MINZIP_SYSUTIL
 
-#include "inline_magic.h"
-
+#include <stdio.h>
 #include <sys/types.h>
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct MappedRange {
+    void* addr;
+    size_t length;
+} MappedRange;
+
 /*
  * Use this to keep track of mapped segments.
  */
 typedef struct MemMapping {
-    void*   addr;           /* start of data */
-    size_t  length;         /* length of data */
+    unsigned char* addr;           /* start of data */
+    size_t         length;         /* length of data */
 
-    void*   baseAddr;       /* page-aligned base address */
-    size_t  baseLength;     /* length of mapping */
+    int            range_count;
+    MappedRange*   ranges;
 } MemMapping;
 
-/* copy a map */
-INLINE void sysCopyMap(MemMapping* dst, const MemMapping* src) {
-    *dst = *src;
-}
-
 /*
- * Load a file into a new shared memory segment.  All data from the current
- * offset to the end of the file is pulled in.
- *
- * The segment is read-write, allowing VM fixups.  (It should be modified
- * to support .gz/.zip compressed data.)
+ * Map a file into a private, read-only memory segment.  If 'fn'
+ * begins with an '@' character, it is a map of blocks to be mapped,
+ * otherwise it is treated as an ordinary file.
  *
  * On success, "pMap" is filled in, and zero is returned.
  */
-int sysLoadFileInShmem(int fd, MemMapping* pMap);
-
-/*
- * Map a file (from fd's current offset) into a shared,
- * read-only memory segment.
- *
- * On success, "pMap" is filled in, and zero is returned.
- */
-int sysMapFileInShmem(int fd, MemMapping* pMap);
-
-/*
- * Like sysMapFileInShmem, but on only part of a file.
- */
-int sysMapFileSegmentInShmem(int fd, off_t start, long length,
-    MemMapping* pMap);
+int sysMapFile(const char* fn, MemMapping* pMap);
 
 /*
  * Release the pages associated with a shared memory segment.
  *
  * This does not free "pMap"; it just releases the memory.
  */
-void sysReleaseShmem(MemMapping* pMap);
+void sysReleaseMap(MemMapping* pMap);
+
+#ifdef __cplusplus
+}
+#endif
 
 #endif /*_MINZIP_SYSUTIL*/
diff --git a/minzip/Zip.c b/minzip/Zip.c
index f4f38a9..abc9890 100644
--- a/minzip/Zip.c
+++ b/minzip/Zip.c
@@ -184,7 +184,7 @@
  *
  * Returns "true" on success.
  */
-static bool parseZipArchive(ZipArchive* pArchive, const MemMapping* pMap)
+static bool parseZipArchive(ZipArchive* pArchive)
 {
     bool result = false;
     const unsigned char* ptr;
@@ -196,7 +196,7 @@
      * signature for the first file (LOCSIG) or, if the archive doesn't
      * have any files in it, the end-of-central-directory signature (ENDSIG).
      */
-    val = get4LE(pMap->addr);
+    val = get4LE(pArchive->addr);
     if (val == ENDSIG) {
         LOGI("Found Zip archive, but it looks empty\n");
         goto bail;
@@ -209,14 +209,14 @@
      * Find the EOCD.  We'll find it immediately unless they have a file
      * comment.
      */
-    ptr = pMap->addr + pMap->length - ENDHDR;
+    ptr = pArchive->addr + pArchive->length - ENDHDR;
 
-    while (ptr >= (const unsigned char*) pMap->addr) {
+    while (ptr >= (const unsigned char*) pArchive->addr) {
         if (*ptr == (ENDSIG & 0xff) && get4LE(ptr) == ENDSIG)
             break;
         ptr--;
     }
-    if (ptr < (const unsigned char*) pMap->addr) {
+    if (ptr < (const unsigned char*) pArchive->addr) {
         LOGI("Could not find end-of-central-directory in Zip\n");
         goto bail;
     }
@@ -230,9 +230,9 @@
     cdOffset = get4LE(ptr + ENDOFF);
 
     LOGVV("numEntries=%d cdOffset=%d\n", numEntries, cdOffset);
-    if (numEntries == 0 || cdOffset >= pMap->length) {
+    if (numEntries == 0 || cdOffset >= pArchive->length) {
         LOGW("Invalid entries=%d offset=%d (len=%zd)\n",
-            numEntries, cdOffset, pMap->length);
+            numEntries, cdOffset, pArchive->length);
         goto bail;
     }
 
@@ -245,14 +245,14 @@
     if (pArchive->pEntries == NULL || pArchive->pHash == NULL)
         goto bail;
 
-    ptr = pMap->addr + cdOffset;
+    ptr = pArchive->addr + cdOffset;
     for (i = 0; i < numEntries; i++) {
         ZipEntry* pEntry;
         unsigned int fileNameLen, extraLen, commentLen, localHdrOffset;
         const unsigned char* localHdr;
         const char *fileName;
 
-        if (ptr + CENHDR > (const unsigned char*)pMap->addr + pMap->length) {
+        if (ptr + CENHDR > (const unsigned char*)pArchive->addr + pArchive->length) {
             LOGW("Ran off the end (at %d)\n", i);
             goto bail;
         }
@@ -266,7 +266,7 @@
         extraLen = get2LE(ptr + CENEXT);
         commentLen = get2LE(ptr + CENCOM);
         fileName = (const char*)ptr + CENHDR;
-        if (fileName + fileNameLen > (const char*)pMap->addr + pMap->length) {
+        if (fileName + fileNameLen > (const char*)pArchive->addr + pArchive->length) {
             LOGW("Filename ran off the end (at %d)\n", i);
             goto bail;
         }
@@ -352,15 +352,15 @@
         }
         pEntry->externalFileAttributes = get4LE(ptr + CENATX);
 
-        // Perform pMap->addr + localHdrOffset, ensuring that it won't
+        // Perform pArchive->addr + localHdrOffset, ensuring that it won't
         // overflow. This is needed because localHdrOffset is untrusted.
-        if (!safe_add((uintptr_t *)&localHdr, (uintptr_t)pMap->addr,
+        if (!safe_add((uintptr_t *)&localHdr, (uintptr_t)pArchive->addr,
             (uintptr_t)localHdrOffset)) {
             LOGW("Integer overflow adding in parseZipArchive\n");
             goto bail;
         }
         if ((uintptr_t)localHdr + LOCHDR >
-            (uintptr_t)pMap->addr + pMap->length) {
+            (uintptr_t)pArchive->addr + pArchive->length) {
             LOGW("Bad offset to local header: %d (at %d)\n", localHdrOffset, i);
             goto bail;
         }
@@ -374,7 +374,7 @@
             LOGW("Integer overflow adding in parseZipArchive\n");
             goto bail;
         }
-        if ((size_t)pEntry->offset + pEntry->compLen > pMap->length) {
+        if ((size_t)pEntry->offset + pEntry->compLen > pArchive->length) {
             LOGW("Data ran off the end (at %d)\n", i);
             goto bail;
         }
@@ -427,50 +427,30 @@
  *
  * On success, we fill out the contents of "pArchive".
  */
-int mzOpenZipArchive(const char* fileName, ZipArchive* pArchive)
+int mzOpenZipArchive(unsigned char* addr, size_t length, ZipArchive* pArchive)
 {
-    MemMapping map;
     int err;
 
-    LOGV("Opening archive '%s' %p\n", fileName, pArchive);
-
-    map.addr = NULL;
-    memset(pArchive, 0, sizeof(*pArchive));
-
-    pArchive->fd = open(fileName, O_RDONLY, 0);
-    if (pArchive->fd < 0) {
-        err = errno ? errno : -1;
-        LOGV("Unable to open '%s': %s\n", fileName, strerror(err));
-        goto bail;
-    }
-
-    if (sysMapFileInShmem(pArchive->fd, &map) != 0) {
-        err = -1;
-        LOGW("Map of '%s' failed\n", fileName);
-        goto bail;
-    }
-
-    if (map.length < ENDHDR) {
+    if (length < ENDHDR) {
         err = -1;
         LOGV("File '%s' too small to be zip (%zd)\n", fileName, map.length);
         goto bail;
     }
 
-    if (!parseZipArchive(pArchive, &map)) {
+    pArchive->addr = addr;
+    pArchive->length = length;
+
+    if (!parseZipArchive(pArchive)) {
         err = -1;
         LOGV("Parsing '%s' failed\n", fileName);
         goto bail;
     }
 
     err = 0;
-    sysCopyMap(&pArchive->map, &map);
-    map.addr = NULL;
 
 bail:
     if (err != 0)
         mzCloseZipArchive(pArchive);
-    if (map.addr != NULL)
-        sysReleaseShmem(&map);
     return err;
 }
 
@@ -483,16 +463,10 @@
 {
     LOGV("Closing archive %p\n", pArchive);
 
-    if (pArchive->fd >= 0)
-        close(pArchive->fd);
-    if (pArchive->map.addr != NULL)
-        sysReleaseShmem(&pArchive->map);
-
     free(pArchive->pEntries);
 
     mzHashTableFree(pArchive->pHash);
 
-    pArchive->fd = -1;
     pArchive->pHash = NULL;
     pArchive->pEntries = NULL;
 }
@@ -528,29 +502,7 @@
     const ZipEntry *pEntry, ProcessZipEntryContentsFunction processFunction,
     void *cookie)
 {
-    size_t bytesLeft = pEntry->compLen;
-    while (bytesLeft > 0) {
-        unsigned char buf[32 * 1024];
-        ssize_t n;
-        size_t count;
-        bool ret;
-
-        count = bytesLeft;
-        if (count > sizeof(buf)) {
-            count = sizeof(buf);
-        }
-        n = read(pArchive->fd, buf, count);
-        if (n < 0 || (size_t)n != count) {
-            LOGE("Can't read %zu bytes from zip file: %ld\n", count, n);
-            return false;
-        }
-        ret = processFunction(buf, n, cookie);
-        if (!ret) {
-            return false;
-        }
-        bytesLeft -= count;
-    }
-    return true;
+    return processFunction(pArchive->addr + pEntry->offset, pEntry->uncompLen, cookie);
 }
 
 static bool processDeflatedEntry(const ZipArchive *pArchive,
@@ -573,8 +525,8 @@
     zstream.zalloc = Z_NULL;
     zstream.zfree = Z_NULL;
     zstream.opaque = Z_NULL;
-    zstream.next_in = NULL;
-    zstream.avail_in = 0;
+    zstream.next_in = pArchive->addr + pEntry->offset;
+    zstream.avail_in = pEntry->compLen;
     zstream.next_out = (Bytef*) procBuf;
     zstream.avail_out = sizeof(procBuf);
     zstream.data_type = Z_UNKNOWN;
@@ -598,25 +550,6 @@
      * Loop while we have data.
      */
     do {
-        /* read as much as we can */
-        if (zstream.avail_in == 0) {
-            long getSize = (compRemaining > (long)sizeof(readBuf)) ?
-                        (long)sizeof(readBuf) : compRemaining;
-            LOGVV("+++ reading %ld bytes (%ld left)\n",
-                getSize, compRemaining);
-
-            int cc = read(pArchive->fd, readBuf, getSize);
-            if (cc != (int) getSize) {
-                LOGW("inflate read failed (%d vs %ld)\n", cc, getSize);
-                goto z_bail;
-            }
-
-            compRemaining -= getSize;
-
-            zstream.next_in = readBuf;
-            zstream.avail_in = getSize;
-        }
-
         /* uncompress the data */
         zerr = inflate(&zstream, Z_NO_FLUSH);
         if (zerr != Z_OK && zerr != Z_STREAM_END) {
@@ -676,12 +609,6 @@
     bool ret = false;
     off_t oldOff;
 
-    /* save current offset */
-    oldOff = lseek(pArchive->fd, 0, SEEK_CUR);
-
-    /* Seek to the beginning of the entry's compressed data. */
-    lseek(pArchive->fd, pEntry->offset, SEEK_SET);
-
     switch (pEntry->compression) {
     case STORED:
         ret = processStoredEntry(pArchive, pEntry, processFunction, cookie);
@@ -695,8 +622,6 @@
         break;
     }
 
-    /* restore file offset */
-    lseek(pArchive->fd, oldOff, SEEK_SET);
     return ret;
 }
 
@@ -778,7 +703,7 @@
     while (true) {
         ssize_t n = write(fd, data+soFar, dataLen-soFar);
         if (n <= 0) {
-            LOGE("Error writing %ld bytes from zip file from %p: %s\n",
+            LOGE("Error writing %zd bytes from zip file from %p: %s\n",
                  dataLen-soFar, data+soFar, strerror(errno));
             if (errno != EINTR) {
               return false;
@@ -787,7 +712,7 @@
             soFar += n;
             if (soFar == dataLen) return true;
             if (soFar > dataLen) {
-                LOGE("write overrun?  (%ld bytes instead of %d)\n",
+                LOGE("write overrun?  (%zd bytes instead of %d)\n",
                      soFar, dataLen);
                 return false;
             }
@@ -810,6 +735,23 @@
     return true;
 }
 
+/*
+ * Obtain a pointer to the in-memory representation of a stored entry.
+ */
+bool mzGetStoredEntry(const ZipArchive *pArchive,
+    const ZipEntry *pEntry, unsigned char **addr, size_t *length)
+{
+    if (pEntry->compression != STORED) {
+        LOGE("Can't getStoredEntry for '%s'; not stored\n",
+             pEntry->fileName);
+        return false;
+    }
+
+    *addr = pArchive->addr + pEntry->offset;
+    *length = pEntry->uncompLen;
+    return true;
+}
+
 typedef struct {
     unsigned char* buffer;
     long len;
diff --git a/minzip/Zip.h b/minzip/Zip.h
index c942828..2054b38 100644
--- a/minzip/Zip.h
+++ b/minzip/Zip.h
@@ -46,11 +46,11 @@
  * One Zip archive.  Treat as opaque.
  */
 typedef struct ZipArchive {
-    int         fd;
-    unsigned int numEntries;
-    ZipEntry*   pEntries;
-    HashTable*  pHash;          // maps file name to ZipEntry
-    MemMapping  map;
+    unsigned int   numEntries;
+    ZipEntry*      pEntries;
+    HashTable*     pHash;          // maps file name to ZipEntry
+    unsigned char* addr;
+    size_t         length;
 } ZipArchive;
 
 /*
@@ -68,7 +68,7 @@
  * On success, returns 0 and populates "pArchive".  Returns nonzero errno
  * value on failure.
  */
-int mzOpenZipArchive(const char* fileName, ZipArchive* pArchive);
+int mzOpenZipArchive(unsigned char* addr, size_t length, ZipArchive* pArchive);
 
 /*
  * Close archive, releasing resources associated with it.
@@ -183,6 +183,17 @@
     const ZipEntry *pEntry, unsigned char* buffer);
 
 /*
+ * Return a pointer and length for a given entry.  The returned region
+ * should be valid until pArchive is closed, and should be treated as
+ * read-only.
+ *
+ * Only makes sense for entries which are stored (ie, not compressed).
+ * No guarantees are made regarding alignment of the returned pointer.
+ */
+bool mzGetStoredEntry(const ZipArchive *pArchive,
+    const ZipEntry* pEntry, unsigned char **addr, size_t *length);
+
+/*
  * Inflate all entries under zipDir to the directory specified by
  * targetDir, which must exist and be a writable directory.
  *
diff --git a/recovery.cpp b/recovery.cpp
index 8f2183d..09af5f8 100644
--- a/recovery.cpp
+++ b/recovery.cpp
@@ -771,7 +771,10 @@
     ui->Print("Data wipe complete.\n");
 }
 
-static void
+// Return REBOOT, SHUTDOWN, or REBOOT_BOOTLOADER.  Returning NO_ACTION
+// means to take the default, which is to reboot or shutdown depending
+// on if the --shutdown_after flag was passed to recovery.
+static Device::BuiltinAction
 prompt_and_wait(Device* device, int status) {
     const char* const* headers = prepend_title(device->GetMenuHeaders());
 
@@ -795,23 +798,28 @@
         // device-specific code may take some action here.  It may
         // return one of the core actions handled in the switch
         // statement below.
-        chosen_item = device->InvokeMenuItem(chosen_item);
+        Device::BuiltinAction chosen_action = device->InvokeMenuItem(chosen_item);
 
         int wipe_cache;
-        switch (chosen_item) {
+        switch (chosen_action) {
+            case Device::NO_ACTION:
+                break;
+
             case Device::REBOOT:
-                return;
+            case Device::SHUTDOWN:
+            case Device::REBOOT_BOOTLOADER:
+                return chosen_action;
 
             case Device::WIPE_DATA:
                 wipe_data(ui->IsTextVisible(), device);
-                if (!ui->IsTextVisible()) return;
+                if (!ui->IsTextVisible()) return Device::NO_ACTION;
                 break;
 
             case Device::WIPE_CACHE:
                 ui->Print("\n-- Wiping cache...\n");
                 erase_volume("/cache");
                 ui->Print("Cache wipe complete.\n");
-                if (!ui->IsTextVisible()) return;
+                if (!ui->IsTextVisible()) return Device::NO_ACTION;
                 break;
 
             case Device::APPLY_EXT:
@@ -829,7 +837,7 @@
                         ui->SetBackground(RecoveryUI::ERROR);
                         ui->Print("Installation aborted.\n");
                     } else if (!ui->IsTextVisible()) {
-                        return;  // reboot if logs aren't visible
+                        return Device::NO_ACTION;  // reboot if logs aren't visible
                     } else {
                         ui->Print("\nInstall from sdcard complete.\n");
                     }
@@ -852,7 +860,7 @@
                         ui->SetBackground(RecoveryUI::ERROR);
                         ui->Print("Installation aborted.\n");
                     } else if (!ui->IsTextVisible()) {
-                        return;  // reboot if logs aren't visible
+                        return Device::NO_ACTION;  // reboot if logs aren't visible
                     } else {
                         ui->Print("\nInstall from cache complete.\n");
                     }
@@ -867,7 +875,7 @@
                         ui->Print("Installation aborted.\n");
                         copy_logs();
                     } else if (!ui->IsTextVisible()) {
-                        return;  // reboot if logs aren't visible
+                        return Device::NO_ACTION;  // reboot if logs aren't visible
                     } else {
                         ui->Print("\nInstall from ADB complete.\n");
                     }
@@ -939,7 +947,7 @@
         return 0;
     }
 
-    printf("Starting recovery on %s", ctime(&start));
+    printf("Starting recovery (pid %d) on %s", getpid(), ctime(&start));
 
     load_volume_table();
     ensure_path_mounted(LAST_LOG_FILE);
@@ -981,7 +989,7 @@
         load_locale_from_cache();
     }
     printf("locale is [%s]\n", locale);
-    printf("stage is [%s]\n", stage, stage);
+    printf("stage is [%s]\n", stage);
 
     Device* device = make_device();
     ui = device->GetUI();
@@ -1074,18 +1082,31 @@
         copy_logs();
         ui->SetBackground(RecoveryUI::ERROR);
     }
+    Device::BuiltinAction after = shutdown_after ? Device::SHUTDOWN : Device::REBOOT;
     if (status != INSTALL_SUCCESS || ui->IsTextVisible()) {
-        prompt_and_wait(device, status);
+        Device::BuiltinAction temp = prompt_and_wait(device, status);
+        if (temp != Device::NO_ACTION) after = temp;
     }
 
-    // Otherwise, get ready to boot the main system...
+    // Save logs and clean up before rebooting or shutting down.
     finish_recovery(send_intent);
-    if (shutdown_after) {
-        ui->Print("Shutting down...\n");
-        property_set(ANDROID_RB_PROPERTY, "shutdown,");
-    } else {
-        ui->Print("Rebooting...\n");
-        property_set(ANDROID_RB_PROPERTY, "reboot,");
+
+    switch (after) {
+        case Device::SHUTDOWN:
+            ui->Print("Shutting down...\n");
+            property_set(ANDROID_RB_PROPERTY, "shutdown,");
+            break;
+
+        case Device::REBOOT_BOOTLOADER:
+            ui->Print("Rebooting to bootloader...\n");
+            property_set(ANDROID_RB_PROPERTY, "reboot,bootloader");
+            break;
+
+        default:
+            ui->Print("Rebooting...\n");
+            property_set(ANDROID_RB_PROPERTY, "reboot,");
+            break;
     }
+    sleep(5); // should reboot before this finishes
     return EXIT_SUCCESS;
 }
diff --git a/res-hdpi/images/erasing_text.png b/res-hdpi/images/erasing_text.png
new file mode 100644
index 0000000..774244c
--- /dev/null
+++ b/res-hdpi/images/erasing_text.png
Binary files differ
diff --git a/res-hdpi/images/error_text.png b/res-hdpi/images/error_text.png
new file mode 100644
index 0000000..64a57ec
--- /dev/null
+++ b/res-hdpi/images/error_text.png
Binary files differ
diff --git a/res/images/icon_error.png b/res-hdpi/images/icon_error.png
similarity index 100%
rename from res/images/icon_error.png
rename to res-hdpi/images/icon_error.png
Binary files differ
diff --git a/res/images/icon_installing.png b/res-hdpi/images/icon_installing.png
similarity index 100%
rename from res/images/icon_installing.png
rename to res-hdpi/images/icon_installing.png
Binary files differ
diff --git a/res-hdpi/images/installing_text.png b/res-hdpi/images/installing_text.png
new file mode 100644
index 0000000..33b54f1
--- /dev/null
+++ b/res-hdpi/images/installing_text.png
Binary files differ
diff --git a/res-hdpi/images/no_command_text.png b/res-hdpi/images/no_command_text.png
new file mode 100644
index 0000000..9927ecb
--- /dev/null
+++ b/res-hdpi/images/no_command_text.png
Binary files differ
diff --git a/res-hdpi/images/progress_empty.png b/res-hdpi/images/progress_empty.png
new file mode 100644
index 0000000..b29d9be
--- /dev/null
+++ b/res-hdpi/images/progress_empty.png
Binary files differ
diff --git a/res-hdpi/images/progress_fill.png b/res-hdpi/images/progress_fill.png
new file mode 100644
index 0000000..4c5e66b
--- /dev/null
+++ b/res-hdpi/images/progress_fill.png
Binary files differ
diff --git a/res/images/stage_empty.png b/res-hdpi/images/stage_empty.png
similarity index 100%
rename from res/images/stage_empty.png
rename to res-hdpi/images/stage_empty.png
Binary files differ
diff --git a/res/images/stage_fill.png b/res-hdpi/images/stage_fill.png
similarity index 100%
rename from res/images/stage_fill.png
rename to res-hdpi/images/stage_fill.png
Binary files differ
diff --git a/res-mdpi/images/erasing_text.png b/res-mdpi/images/erasing_text.png
new file mode 100644
index 0000000..fd86c3f
--- /dev/null
+++ b/res-mdpi/images/erasing_text.png
Binary files differ
diff --git a/res-mdpi/images/error_text.png b/res-mdpi/images/error_text.png
new file mode 100644
index 0000000..f1b44c9
--- /dev/null
+++ b/res-mdpi/images/error_text.png
Binary files differ
diff --git a/res/images/icon_error.png b/res-mdpi/images/icon_error.png
similarity index 100%
copy from res/images/icon_error.png
copy to res-mdpi/images/icon_error.png
Binary files differ
diff --git a/res/images/icon_installing.png b/res-mdpi/images/icon_installing.png
similarity index 100%
copy from res/images/icon_installing.png
copy to res-mdpi/images/icon_installing.png
Binary files differ
diff --git a/res-mdpi/images/installing_text.png b/res-mdpi/images/installing_text.png
new file mode 100644
index 0000000..064b2a3
--- /dev/null
+++ b/res-mdpi/images/installing_text.png
Binary files differ
diff --git a/res-mdpi/images/no_command_text.png b/res-mdpi/images/no_command_text.png
new file mode 100644
index 0000000..1f29b89
--- /dev/null
+++ b/res-mdpi/images/no_command_text.png
Binary files differ
diff --git a/res-mdpi/images/progress_empty.png b/res-mdpi/images/progress_empty.png
new file mode 100644
index 0000000..0883e00
--- /dev/null
+++ b/res-mdpi/images/progress_empty.png
Binary files differ
diff --git a/res-mdpi/images/progress_fill.png b/res-mdpi/images/progress_fill.png
new file mode 100644
index 0000000..c223871
--- /dev/null
+++ b/res-mdpi/images/progress_fill.png
Binary files differ
diff --git a/res/images/stage_empty.png b/res-mdpi/images/stage_empty.png
similarity index 100%
copy from res/images/stage_empty.png
copy to res-mdpi/images/stage_empty.png
Binary files differ
diff --git a/res/images/stage_fill.png b/res-mdpi/images/stage_fill.png
similarity index 100%
copy from res/images/stage_fill.png
copy to res-mdpi/images/stage_fill.png
Binary files differ
diff --git a/res-xhdpi/images/erasing_text.png b/res-xhdpi/images/erasing_text.png
new file mode 100644
index 0000000..f88e0e6
--- /dev/null
+++ b/res-xhdpi/images/erasing_text.png
Binary files differ
diff --git a/res-xhdpi/images/error_text.png b/res-xhdpi/images/error_text.png
new file mode 100644
index 0000000..c3a4cc6
--- /dev/null
+++ b/res-xhdpi/images/error_text.png
Binary files differ
diff --git a/res/images/icon_error.png b/res-xhdpi/images/icon_error.png
similarity index 100%
copy from res/images/icon_error.png
copy to res-xhdpi/images/icon_error.png
Binary files differ
diff --git a/res/images/icon_installing.png b/res-xhdpi/images/icon_installing.png
similarity index 100%
copy from res/images/icon_installing.png
copy to res-xhdpi/images/icon_installing.png
Binary files differ
diff --git a/res-xhdpi/images/installing_text.png b/res-xhdpi/images/installing_text.png
new file mode 100644
index 0000000..a4dacd0
--- /dev/null
+++ b/res-xhdpi/images/installing_text.png
Binary files differ
diff --git a/res-xhdpi/images/no_command_text.png b/res-xhdpi/images/no_command_text.png
new file mode 100644
index 0000000..eb34e94
--- /dev/null
+++ b/res-xhdpi/images/no_command_text.png
Binary files differ
diff --git a/res-xhdpi/images/progress_empty.png b/res-xhdpi/images/progress_empty.png
new file mode 100644
index 0000000..429797b
--- /dev/null
+++ b/res-xhdpi/images/progress_empty.png
Binary files differ
diff --git a/res-xhdpi/images/progress_fill.png b/res-xhdpi/images/progress_fill.png
new file mode 100644
index 0000000..7af35aa
--- /dev/null
+++ b/res-xhdpi/images/progress_fill.png
Binary files differ
diff --git a/res/images/stage_empty.png b/res-xhdpi/images/stage_empty.png
similarity index 100%
copy from res/images/stage_empty.png
copy to res-xhdpi/images/stage_empty.png
Binary files differ
diff --git a/res/images/stage_fill.png b/res-xhdpi/images/stage_fill.png
similarity index 100%
copy from res/images/stage_fill.png
copy to res-xhdpi/images/stage_fill.png
Binary files differ
diff --git a/res-xxhdpi/images/erasing_text.png b/res-xxhdpi/images/erasing_text.png
new file mode 100644
index 0000000..c87fd52
--- /dev/null
+++ b/res-xxhdpi/images/erasing_text.png
Binary files differ
diff --git a/res-xxhdpi/images/error_text.png b/res-xxhdpi/images/error_text.png
new file mode 100644
index 0000000..486e951
--- /dev/null
+++ b/res-xxhdpi/images/error_text.png
Binary files differ
diff --git a/res/images/icon_error.png b/res-xxhdpi/images/icon_error.png
similarity index 100%
copy from res/images/icon_error.png
copy to res-xxhdpi/images/icon_error.png
Binary files differ
diff --git a/res/images/icon_installing.png b/res-xxhdpi/images/icon_installing.png
similarity index 100%
copy from res/images/icon_installing.png
copy to res-xxhdpi/images/icon_installing.png
Binary files differ
diff --git a/res-xxhdpi/images/installing_text.png b/res-xxhdpi/images/installing_text.png
new file mode 100644
index 0000000..ef6e8f3
--- /dev/null
+++ b/res-xxhdpi/images/installing_text.png
Binary files differ
diff --git a/res-xxhdpi/images/no_command_text.png b/res-xxhdpi/images/no_command_text.png
new file mode 100644
index 0000000..cc98bb1
--- /dev/null
+++ b/res-xxhdpi/images/no_command_text.png
Binary files differ
diff --git a/res-xxhdpi/images/progress_empty.png b/res-xxhdpi/images/progress_empty.png
new file mode 100644
index 0000000..57c8235
--- /dev/null
+++ b/res-xxhdpi/images/progress_empty.png
Binary files differ
diff --git a/res-xxhdpi/images/progress_fill.png b/res-xxhdpi/images/progress_fill.png
new file mode 100644
index 0000000..0f36f48
--- /dev/null
+++ b/res-xxhdpi/images/progress_fill.png
Binary files differ
diff --git a/res/images/stage_empty.png b/res-xxhdpi/images/stage_empty.png
similarity index 100%
copy from res/images/stage_empty.png
copy to res-xxhdpi/images/stage_empty.png
Binary files differ
diff --git a/res/images/stage_fill.png b/res-xxhdpi/images/stage_fill.png
similarity index 100%
copy from res/images/stage_fill.png
copy to res-xxhdpi/images/stage_fill.png
Binary files differ
diff --git a/res-xxxhdpi/images/erasing_text.png b/res-xxxhdpi/images/erasing_text.png
new file mode 100644
index 0000000..612e7a3
--- /dev/null
+++ b/res-xxxhdpi/images/erasing_text.png
Binary files differ
diff --git a/res-xxxhdpi/images/error_text.png b/res-xxxhdpi/images/error_text.png
new file mode 100644
index 0000000..50d2fad
--- /dev/null
+++ b/res-xxxhdpi/images/error_text.png
Binary files differ
diff --git a/res/images/icon_error.png b/res-xxxhdpi/images/icon_error.png
similarity index 100%
copy from res/images/icon_error.png
copy to res-xxxhdpi/images/icon_error.png
Binary files differ
diff --git a/res/images/icon_installing.png b/res-xxxhdpi/images/icon_installing.png
similarity index 100%
copy from res/images/icon_installing.png
copy to res-xxxhdpi/images/icon_installing.png
Binary files differ
diff --git a/res-xxxhdpi/images/installing_text.png b/res-xxxhdpi/images/installing_text.png
new file mode 100644
index 0000000..9bd093b
--- /dev/null
+++ b/res-xxxhdpi/images/installing_text.png
Binary files differ
diff --git a/res-xxxhdpi/images/no_command_text.png b/res-xxxhdpi/images/no_command_text.png
new file mode 100644
index 0000000..6354e6a
--- /dev/null
+++ b/res-xxxhdpi/images/no_command_text.png
Binary files differ
diff --git a/res-xxxhdpi/images/progress_empty.png b/res-xxxhdpi/images/progress_empty.png
new file mode 100644
index 0000000..ba25afb
--- /dev/null
+++ b/res-xxxhdpi/images/progress_empty.png
Binary files differ
diff --git a/res-xxxhdpi/images/progress_fill.png b/res-xxxhdpi/images/progress_fill.png
new file mode 100644
index 0000000..83b72b2
--- /dev/null
+++ b/res-xxxhdpi/images/progress_fill.png
Binary files differ
diff --git a/res/images/stage_empty.png b/res-xxxhdpi/images/stage_empty.png
similarity index 100%
copy from res/images/stage_empty.png
copy to res-xxxhdpi/images/stage_empty.png
Binary files differ
diff --git a/res/images/stage_fill.png b/res-xxxhdpi/images/stage_fill.png
similarity index 100%
copy from res/images/stage_fill.png
copy to res-xxxhdpi/images/stage_fill.png
Binary files differ
diff --git a/res/images/erasing_text.png b/res/images/erasing_text.png
deleted file mode 100644
index 441768a..0000000
--- a/res/images/erasing_text.png
+++ /dev/null
Binary files differ
diff --git a/res/images/error_text.png b/res/images/error_text.png
deleted file mode 100644
index 4ac6391..0000000
--- a/res/images/error_text.png
+++ /dev/null
Binary files differ
diff --git a/res/images/installing_text.png b/res/images/installing_text.png
deleted file mode 100644
index e1ac819..0000000
--- a/res/images/installing_text.png
+++ /dev/null
Binary files differ
diff --git a/res/images/no_command_text.png b/res/images/no_command_text.png
deleted file mode 100644
index a688f09..0000000
--- a/res/images/no_command_text.png
+++ /dev/null
Binary files differ
diff --git a/res/images/progress_empty.png b/res/images/progress_empty.png
deleted file mode 100644
index 7258183..0000000
--- a/res/images/progress_empty.png
+++ /dev/null
Binary files differ
diff --git a/res/images/progress_fill.png b/res/images/progress_fill.png
deleted file mode 100644
index becf87b..0000000
--- a/res/images/progress_fill.png
+++ /dev/null
Binary files differ
diff --git a/roots.cpp b/roots.cpp
index cfe1338..66481a3 100644
--- a/roots.cpp
+++ b/roots.cpp
@@ -21,6 +21,7 @@
 #include <sys/types.h>
 #include <unistd.h>
 #include <ctype.h>
+#include <fcntl.h>
 
 #include <fs_mgr.h>
 #include "mtdutils/mtdutils.h"
@@ -28,6 +29,10 @@
 #include "roots.h"
 #include "common.h"
 #include "make_ext4fs.h"
+extern "C" {
+#include "wipe.h"
+#include "cryptfs.h"
+}
 
 static struct fstab *fstab = NULL;
 
@@ -191,11 +196,31 @@
     }
 
     if (strcmp(v->fs_type, "ext4") == 0) {
-        int result = make_ext4fs(v->blk_device, v->length, volume, sehandle);
+        ssize_t length = 0;
+        if (v->length != 0) {
+            length = v->length;
+        } else if (v->key_loc != NULL && strcmp(v->key_loc, "footer") == 0) {
+            length = -CRYPT_FOOTER_OFFSET;
+        }
+        int result = make_ext4fs(v->blk_device, length, volume, sehandle);
         if (result != 0) {
             LOGE("format_volume: make_extf4fs failed on %s\n", v->blk_device);
             return -1;
         }
+
+        // if there's a key_loc that looks like a path, it should be a
+        // block device for storing encryption metadata.  wipe it too.
+        if (v->key_loc != NULL && v->key_loc[0] == '/') {
+            LOGI("wiping %s\n", v->key_loc);
+            int fd = open(v->key_loc, O_WRONLY | O_CREAT, 0644);
+            if (fd < 0) {
+                LOGE("format_volume: failed to open %s\n", v->key_loc);
+                return -1;
+            }
+            wipe_block_device(fd, get_file_size(fd));
+            close(fd);
+        }
+
         return 0;
     }
 
@@ -213,10 +238,16 @@
 
         if (strcmp(v->mount_point, "/tmp") == 0 ||
             strcmp(v->mount_point, "/cache") == 0) {
-            if (ensure_path_mounted(v->mount_point) != 0) return -1;
+            if (ensure_path_mounted(v->mount_point) != 0) {
+                LOGE("failed to mount %s\n", v->mount_point);
+                return -1;
+            }
 
         } else {
-            if (ensure_path_unmounted(v->mount_point) != 0) return -1;
+            if (ensure_path_unmounted(v->mount_point) != 0) {
+                LOGE("failed to unmount %s\n", v->mount_point);
+                return -1;
+            }
         }
     }
     return 0;
diff --git a/screen_ui.cpp b/screen_ui.cpp
index eddae11..afe856c 100644
--- a/screen_ui.cpp
+++ b/screen_ui.cpp
@@ -74,6 +74,7 @@
     installing_frames(-1),
     stage(-1),
     max_stage(-1) {
+
     for (int i = 0; i < 5; i++)
         backgroundIcon[i] = NULL;
 
@@ -86,7 +87,7 @@
 void ScreenRecoveryUI::draw_background_locked(Icon icon)
 {
     pagesIdentical = false;
-    gr_color(0, 0, 0, 255);
+    gr_color(250, 250, 250, 255);
     gr_clear();
 
     if (icon) {
@@ -111,7 +112,6 @@
         int textY = ((gr_fb_height() - (iconHeight+textHeight+40+sh)) / 2) + iconHeight + 40;
 
         gr_blit(surface, 0, 0, iconWidth, iconHeight, iconX, iconY);
-
         if (stageHeight > 0) {
             int sw = gr_get_width(stageMarkerEmpty);
             int x = (gr_fb_width() - max_stage * gr_get_width(stageMarkerEmpty)) / 2;
@@ -123,7 +123,7 @@
             }
         }
 
-        gr_color(255, 255, 255, 255);
+        gr_color(115, 115, 115, 255);
         gr_texticon(textX, textY, text_surface);
     }
 }
@@ -148,7 +148,7 @@
         int dy = (3*gr_fb_height() + iconHeight - 2*height)/4;
 
         // Erase behind the progress bar (in case this was a progress-only update)
-        gr_color(0, 0, 0, 255);
+        gr_color(250, 250, 250, 255);
         gr_fill(dx, dy, width, height);
 
         if (progressBarType == DETERMINATE) {
@@ -179,20 +179,17 @@
 void ScreenRecoveryUI::SetColor(UIElement e) {
     switch (e) {
         case HEADER:
-            gr_color(247, 0, 6, 255);
+            gr_color(0xff, 0x57, 0x22, 255);  // Quantum "Deep Orange" 500
             break;
         case MENU:
         case MENU_SEL_BG:
-            gr_color(0, 106, 157, 255);
+            gr_color(0x67, 0x3a, 0xb7, 255);  // Quantum "Deep Purple" 500
             break;
         case MENU_SEL_FG:
             gr_color(255, 255, 255, 255);
             break;
         case LOG:
-            gr_color(249, 194, 0, 255);
-            break;
-        case TEXT_FILL:
-            gr_color(0, 0, 0, 160);
+            gr_color(0x3f, 0x51, 0xb5, 255);  // Quantum "Indigo" 500
             break;
         default:
             gr_color(255, 255, 255, 255);
@@ -208,7 +205,7 @@
         draw_background_locked(currentIcon);
         draw_progress_locked();
     } else {
-        gr_color(0, 0, 0, 255);
+        gr_color(250, 250, 250, 255);
         gr_clear();
 
         int y = 0;
@@ -222,13 +219,13 @@
                 if (i == menu_top + menu_sel) {
                     // draw the highlight bar
                     SetColor(MENU_SEL_BG);
-                    gr_fill(0, y-2, gr_fb_width(), y+char_height+2);
+                    gr_fill(0, y-2+kTextYOffset, gr_fb_width(), y+char_height+2+kTextYOffset);
                     // white text of selected item
                     SetColor(MENU_SEL_FG);
-                    if (menu[i][0]) gr_text(4, y, menu[i], 1);
+                    if (menu[i][0]) gr_text(kTextXOffset, y+kTextYOffset, menu[i], 1);
                     SetColor(MENU);
                 } else {
-                    if (menu[i][0]) gr_text(4, y, menu[i], i < menu_top);
+                    if (menu[i][0]) gr_text(kTextXOffset, y+kTextYOffset, menu[i], i < menu_top);
                 }
                 y += char_height+4;
             }
@@ -249,7 +246,7 @@
         for (int ty = gr_fb_height() - char_height, count = 0;
              ty > y+2 && count < text_rows;
              ty -= char_height, ++count) {
-            gr_text(4, ty, text[row], 0);
+            gr_text(kTextXOffset, ty+kTextYOffset, text[row], 0);
             --row;
             if (row < 0) row = text_rows-1;
         }
@@ -350,11 +347,11 @@
     gr_font_size(&char_width, &char_height);
 
     text_col = text_row = 0;
-    text_rows = gr_fb_height() / char_height;
+    text_rows = (gr_fb_height() - 2 * kTextYOffset) / char_height;
     if (text_rows > kMaxRows) text_rows = kMaxRows;
     text_top = 1;
 
-    text_cols = gr_fb_width() / char_width;
+    text_cols = (gr_fb_width() - 2 * kTextXOffset) / char_width;
     if (text_cols > kMaxCols - 1) text_cols = kMaxCols - 1;
 
     backgroundIcon[NONE] = NULL;
diff --git a/screen_ui.h b/screen_ui.h
index 532269f..d5e5892 100644
--- a/screen_ui.h
+++ b/screen_ui.h
@@ -87,6 +87,9 @@
     static const int kMaxCols = 96;
     static const int kMaxRows = 96;
 
+    static const int kTextXOffset = 8;
+    static const int kTextYOffset = 4;
+
     // Log text overlay, displayed when a magic key is pressed
     char text[kMaxRows][kMaxCols];
     int text_cols, text_rows;
@@ -102,6 +105,8 @@
 
     int animation_fps;
     int installing_frames;
+  protected:
+  private:
 
     int iconX, iconY;
 
diff --git a/ui.cpp b/ui.cpp
index 5043ee5..091012f 100644
--- a/ui.cpp
+++ b/ui.cpp
@@ -63,12 +63,12 @@
 }
 
 
-int RecoveryUI::input_callback(int fd, short revents, void* data)
+int RecoveryUI::input_callback(int fd, uint32_t epevents, void* data)
 {
     struct input_event ev;
     int ret;
 
-    ret = ev_get_input(fd, revents, &ev);
+    ret = ev_get_input(fd, epevents, &ev);
     if (ret)
         return -1;
 
@@ -276,7 +276,7 @@
 // - Press power seven times in a row to reboot.
 // - Alternate vol-up and vol-down seven times to mount /system.
 RecoveryUI::KeyAction RecoveryUI::CheckKey(int key) {
-    if (IsKeyPressed(KEY_POWER) && key == KEY_VOLUMEUP) {
+    if ((IsKeyPressed(KEY_POWER) && key == KEY_VOLUMEUP) || key == KEY_HOME) {
         return TOGGLE;
     }
 
diff --git a/ui.h b/ui.h
index 2dfe848..faa0acd 100644
--- a/ui.h
+++ b/ui.h
@@ -136,7 +136,7 @@
     pthread_t input_t;
 
     static void* input_thread(void* cookie);
-    static int input_callback(int fd, short revents, void* data);
+    static int input_callback(int fd, uint32_t epevents, void* data);
     void process_key(int key_code, int updown);
     bool usb_connected();
 
diff --git a/minelf/Android.mk b/uncrypt/Android.mk
similarity index 74%
rename from minelf/Android.mk
rename to uncrypt/Android.mk
index 0f41ff5..756bc96 100644
--- a/minelf/Android.mk
+++ b/uncrypt/Android.mk
@@ -1,4 +1,4 @@
-# Copyright (C) 2009 The Android Open Source Project
+# Copyright (C) 2014 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.
@@ -13,15 +13,16 @@
 # limitations under the License.
 
 LOCAL_PATH := $(call my-dir)
+
 include $(CLEAR_VARS)
 
-LOCAL_SRC_FILES := \
-	Retouch.c
+LOCAL_SRC_FILES := uncrypt.c
 
-LOCAL_C_INCLUDES += bootable/recovery
+LOCAL_MODULE := uncrypt
 
-LOCAL_MODULE := libminelf
+LOCAL_STATIC_LIBRARIES := \
+	libfs_mgr \
+	libcutils \
+	libc
 
-LOCAL_CFLAGS += -Wall
-
-include $(BUILD_STATIC_LIBRARY)
+include $(BUILD_EXECUTABLE)
diff --git a/uncrypt/uncrypt.c b/uncrypt/uncrypt.c
new file mode 100644
index 0000000..24d1ffc
--- /dev/null
+++ b/uncrypt/uncrypt.c
@@ -0,0 +1,382 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+// This program takes a file on an ext4 filesystem and produces a list
+// of the blocks that file occupies, which enables the file contents
+// to be read directly from the block device without mounting the
+// filesystem.
+//
+// If the filesystem is using an encrypted block device, it will also
+// read the file and rewrite it to the same blocks of the underlying
+// (unencrypted) block device, so the file contents can be read
+// without the need for the decryption key.
+//
+// The output of this program is a "block map" which looks like this:
+//
+//     /dev/block/platform/msm_sdcc.1/by-name/userdata     # block device
+//     49652 4096                        # file size in bytes, block size
+//     3                                 # count of block ranges
+//     1000 1008                         # block range 0
+//     2100 2102                         # ... block range 1
+//     30 33                             # ... block range 2
+//
+// Each block range represents a half-open interval; the line "30 33"
+// reprents the blocks [30, 31, 32].
+//
+// Recovery can take this block map file and retrieve the underlying
+// file data to use as an update package.
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <linux/fs.h>
+#include <sys/mman.h>
+
+#include <cutils/properties.h>
+#include <fs_mgr.h>
+
+#define WINDOW_SIZE 5
+#define RECOVERY_COMMAND_FILE "/cache/recovery/command"
+#define RECOVERY_COMMAND_FILE_TMP "/cache/recovery/command.tmp"
+#define CACHE_BLOCK_MAP "/cache/recovery/block.map"
+
+static int write_at_offset(unsigned char* buffer, size_t size,
+                           int wfd, off64_t offset)
+{
+    lseek64(wfd, offset, SEEK_SET);
+    size_t written = 0;
+    while (written < size) {
+        ssize_t wrote = write(wfd, buffer + written, size - written);
+        if (wrote < 0) {
+            fprintf(stderr, "error writing offset %lld: %s\n", offset, strerror(errno));
+            return -1;
+        }
+        written += wrote;
+    }
+    return 0;
+}
+
+void add_block_to_ranges(int** ranges, int* range_alloc, int* range_used, int new_block)
+{
+    // If the current block start is < 0, set the start to the new
+    // block.  (This only happens for the very first block of the very
+    // first range.)
+    if ((*ranges)[*range_used*2-2] < 0) {
+        (*ranges)[*range_used*2-2] = new_block;
+        (*ranges)[*range_used*2-1] = new_block;
+    }
+
+    if (new_block == (*ranges)[*range_used*2-1]) {
+        // If the new block comes immediately after the current range,
+        // all we have to do is extend the current range.
+        ++(*ranges)[*range_used*2-1];
+    } else {
+        // We need to start a new range.
+
+        // If there isn't enough room in the array, we need to expand it.
+        if (*range_used >= *range_alloc) {
+            *range_alloc *= 2;
+            *ranges = realloc(*ranges, *range_alloc * 2 * sizeof(int));
+        }
+
+        ++*range_used;
+        (*ranges)[*range_used*2-2] = new_block;
+        (*ranges)[*range_used*2-1] = new_block+1;
+    }
+}
+
+const char* find_block_device(const char* path, int* encryptable, int* encrypted)
+{
+    // The fstab path is always "/fstab.${ro.hardware}".
+    char fstab_path[PATH_MAX+1] = "/fstab.";
+    if (!property_get("ro.hardware", fstab_path+strlen(fstab_path), "")) {
+        fprintf(stderr, "failed to get ro.hardware\n");
+        return NULL;
+    }
+
+    struct fstab* fstab = fs_mgr_read_fstab(fstab_path);
+    if (!fstab) {
+        fprintf(stderr, "failed to read %s\n", fstab_path);
+        return NULL;
+    }
+
+    // Look for a volume whose mount point is the prefix of path and
+    // return its block device.  Set encrypted if it's currently
+    // encrypted.
+    int i;
+    for (i = 0; i < fstab->num_entries; ++i) {
+        struct fstab_rec* v = &fstab->recs[i];
+        if (!v->mount_point) continue;
+        int len = strlen(v->mount_point);
+        if (strncmp(path, v->mount_point, len) == 0 &&
+            (path[len] == '/' || path[len] == 0)) {
+            *encrypted = 0;
+            *encryptable = 0;
+            if (fs_mgr_is_encryptable(v)) {
+                *encryptable = 1;
+                char buffer[PROPERTY_VALUE_MAX+1];
+                if (property_get("ro.crypto.state", buffer, "") &&
+                    strcmp(buffer, "encrypted") == 0) {
+                    *encrypted = 1;
+                }
+            }
+            return v->blk_device;
+        }
+    }
+
+    return NULL;
+}
+
+char* parse_recovery_command_file()
+{
+    char* fn = NULL;
+    int count = 0;
+    char temp[1024];
+
+
+
+    FILE* f = fopen(RECOVERY_COMMAND_FILE, "r");
+    if (f == NULL) {
+        return NULL;
+    }
+    FILE* fo = fopen(RECOVERY_COMMAND_FILE_TMP, "w");
+
+    while (fgets(temp, sizeof(temp), f)) {
+        printf("read: %s", temp);
+        if (strncmp(temp, "--update_package=", strlen("--update_package=")) == 0) {
+            fn = strdup(temp + strlen("--update_package="));
+            strcpy(temp, "--update_package=@" CACHE_BLOCK_MAP "\n");
+        }
+        fputs(temp, fo);
+    }
+    fclose(f);
+    fclose(fo);
+
+    if (fn) {
+        char* newline = strchr(fn, '\n');
+        if (newline) *newline = 0;
+    }
+    return fn;
+}
+
+int produce_block_map(const char* path, const char* map_file, const char* blk_dev,
+                      int encrypted)
+{
+    struct stat sb;
+    int ret;
+
+    FILE* mapf = fopen(map_file, "w");
+
+    ret = stat(path, &sb);
+    if (ret != 0) {
+        fprintf(stderr, "failed to stat %s\n", path);
+        return -1;
+    }
+
+    printf(" block size: %ld bytes\n", (long)sb.st_blksize);
+
+    int blocks = ((sb.st_size-1) / sb.st_blksize) + 1;
+    printf("  file size: %lld bytes, %d blocks\n", (long long)sb.st_size, blocks);
+
+    int* ranges;
+    int range_alloc = 1;
+    int range_used = 1;
+    ranges = malloc(range_alloc * 2 * sizeof(int));
+    ranges[0] = -1;
+    ranges[1] = -1;
+
+    fprintf(mapf, "%s\n%lld %lu\n", blk_dev, (long long)sb.st_size, (unsigned long)sb.st_blksize);
+
+    unsigned char* buffers[WINDOW_SIZE];
+    int i;
+    if (encrypted) {
+        for (i = 0; i < WINDOW_SIZE; ++i) {
+            buffers[i] = malloc(sb.st_blksize);
+        }
+    }
+    int head_block = 0;
+    int head = 0, tail = 0;
+    size_t pos = 0;
+
+    int fd = open(path, O_RDONLY);
+    if (fd < 0) {
+        fprintf(stderr, "failed to open fd for reading: %s\n", strerror(errno));
+        return -1;
+    }
+    fsync(fd);
+
+    int wfd = -1;
+    if (encrypted) {
+        wfd = open(blk_dev, O_WRONLY);
+        if (wfd < 0) {
+            fprintf(stderr, "failed to open fd for writing: %s\n", strerror(errno));
+            return -1;
+        }
+    }
+
+    while (pos < sb.st_size) {
+        if ((tail+1) % WINDOW_SIZE == head) {
+            // write out head buffer
+            int block = head_block;
+            ret = ioctl(fd, FIBMAP, &block);
+            if (ret != 0) {
+                fprintf(stderr, "failed to find block %d\n", head_block);
+                return -1;
+            }
+            add_block_to_ranges(&ranges, &range_alloc, &range_used, block);
+            if (encrypted) {
+                if (write_at_offset(buffers[head], sb.st_blksize, wfd, (off64_t)sb.st_blksize * block) != 0) {
+                    return -1;
+                }
+            }
+            head = (head + 1) % WINDOW_SIZE;
+            ++head_block;
+        }
+
+        // read next block to tail
+        if (encrypted) {
+            size_t so_far = 0;
+            while (so_far < sb.st_blksize && pos < sb.st_size) {
+                ssize_t this_read = read(fd, buffers[tail] + so_far, sb.st_blksize - so_far);
+                if (this_read < 0) {
+                    fprintf(stderr, "failed to read: %s\n", strerror(errno));
+                    return -1;
+                }
+                so_far += this_read;
+                pos += this_read;
+            }
+        } else {
+            // If we're not encrypting; we don't need to actually read
+            // anything, just skip pos forward as if we'd read a
+            // block.
+            pos += sb.st_blksize;
+        }
+        tail = (tail+1) % WINDOW_SIZE;
+    }
+
+    while (head != tail) {
+        // write out head buffer
+        int block = head_block;
+        ret = ioctl(fd, FIBMAP, &block);
+        if (ret != 0) {
+            fprintf(stderr, "failed to find block %d\n", head_block);
+            return -1;
+        }
+        add_block_to_ranges(&ranges, &range_alloc, &range_used, block);
+        if (encrypted) {
+            if (write_at_offset(buffers[head], sb.st_blksize, wfd, (off64_t)sb.st_blksize * block) != 0) {
+                return -1;
+            }
+        }
+        head = (head + 1) % WINDOW_SIZE;
+        ++head_block;
+    }
+
+    fprintf(mapf, "%d\n", range_used);
+    for (i = 0; i < range_used; ++i) {
+        fprintf(mapf, "%d %d\n", ranges[i*2], ranges[i*2+1]);
+    }
+
+    fclose(mapf);
+    close(fd);
+    if (encrypted) {
+        close(wfd);
+    }
+
+    return 0;
+}
+
+void reboot_to_recovery() {
+    property_set("sys.powerctl", "reboot,recovery");
+    sleep(10);
+}
+
+int main(int argc, char** argv)
+{
+    const char* input_path;
+    const char* map_file;
+    int do_reboot = 1;
+
+    if (argc != 1 && argc != 3) {
+        fprintf(stderr, "usage: %s [<transform_path> <map_file>]\n", argv[0]);
+        return 2;
+    }
+
+    if (argc == 3) {
+        // when command-line args are given this binary is being used
+        // for debugging; don't reboot to recovery at the end.
+        input_path = argv[1];
+        map_file = argv[2];
+        do_reboot = 0;
+    } else {
+        input_path = parse_recovery_command_file();
+        if (input_path == NULL) {
+            // if we're rebooting to recovery without a package (say,
+            // to wipe data), then we don't need to do anything before
+            // going to recovery.
+            fprintf(stderr, "no recovery command file or no update package arg");
+            reboot_to_recovery();
+            return 1;
+        }
+        map_file = CACHE_BLOCK_MAP;
+    }
+
+    // Turn the name of the file we're supposed to convert into an
+    // absolute path, so we can find what filesystem it's on.
+    char path[PATH_MAX+1];
+    if (realpath(input_path, path) == NULL) {
+        fprintf(stderr, "failed to convert %s to absolute path: %s\n", input_path, strerror(errno));
+        return 1;
+    }
+
+    int encryptable;
+    int encrypted;
+    const char* blk_dev = find_block_device(path, &encryptable, &encrypted);
+    if (blk_dev == NULL) {
+        fprintf(stderr, "failed to find block device for %s\n", path);
+        return 1;
+    }
+
+    // If the filesystem it's on isn't encrypted, we only produce the
+    // block map, we don't rewrite the file contents (it would be
+    // pointless to do so).
+    printf("encryptable: %s\n", encryptable ? "yes" : "no");
+    printf("  encrypted: %s\n", encrypted ? "yes" : "no");
+
+    if (!encryptable) {
+        // If the file is on a filesystem that doesn't support
+        // encryption (eg, /cache), then leave it alone.
+        //
+        // TODO: change this to be !encrypted -- if the file is on
+        // /data but /data isn't encrypted, we don't need to use the
+        // block map mechanism.  We do for now so as to get more
+        // testing of it (since most dogfood devices aren't
+        // encrypted).
+
+        unlink(RECOVERY_COMMAND_FILE_TMP);
+    } else {
+        if (produce_block_map(path, map_file, blk_dev, encrypted) != 0) {
+            return 1;
+        }
+    }
+
+    rename(RECOVERY_COMMAND_FILE_TMP, RECOVERY_COMMAND_FILE);
+    reboot_to_recovery();
+    return 0;
+}
diff --git a/updater/Android.mk b/updater/Android.mk
index 67e98ec..99b4890 100644
--- a/updater/Android.mk
+++ b/updater/Android.mk
@@ -20,6 +20,7 @@
 
 ifeq ($(TARGET_USERIMAGES_USE_EXT4), true)
 LOCAL_CFLAGS += -DUSE_EXT4
+LOCAL_CFLAGS += -Wno-unused-parameter
 LOCAL_C_INCLUDES += system/extras/ext4_utils
 LOCAL_STATIC_LIBRARIES += \
     libext4_utils_static \
@@ -30,11 +31,12 @@
 LOCAL_STATIC_LIBRARIES += $(TARGET_RECOVERY_UPDATER_LIBS) $(TARGET_RECOVERY_UPDATER_EXTRA_LIBS)
 LOCAL_STATIC_LIBRARIES += libapplypatch libedify libmtdutils libminzip libz
 LOCAL_STATIC_LIBRARIES += libmincrypt libbz
-LOCAL_STATIC_LIBRARIES += libminelf
 LOCAL_STATIC_LIBRARIES += libcutils liblog libstdc++ libc
 LOCAL_STATIC_LIBRARIES += libselinux
 LOCAL_C_INCLUDES += $(LOCAL_PATH)/..
 
+LOCAL_STATIC_LIBRARIES += libsyspatch libxz libxdelta3
+
 # Each library in TARGET_RECOVERY_UPDATER_LIBS should have a function
 # named "Register_<libname>()".  Here we emit a little C function that
 # gets #included by updater.c.  It calls all those registration
diff --git a/updater/MODULE_LICENSE_GPL b/updater/MODULE_LICENSE_GPL
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/updater/MODULE_LICENSE_GPL
diff --git a/updater/NOTICE b/updater/NOTICE
new file mode 100644
index 0000000..e77696a
--- /dev/null
+++ b/updater/NOTICE
@@ -0,0 +1,339 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                          675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) 19yy  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) 19yy name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/updater/install.c b/updater/install.c
index 872cbf8..53f5e48 100644
--- a/updater/install.c
+++ b/updater/install.c
@@ -45,11 +45,27 @@
 #include "mtdutils/mounts.h"
 #include "mtdutils/mtdutils.h"
 #include "updater.h"
+#include "syspatch.h"
+#include "install.h"
 
 #ifdef USE_EXT4
 #include "make_ext4fs.h"
+#include "wipe.h"
 #endif
 
+// Take a sha-1 digest and return it as a newly-allocated hex string.
+static char* PrintSha1(const uint8_t* digest) {
+    char* buffer = malloc(SHA_DIGEST_SIZE*2 + 1);
+    int i;
+    const char* alphabet = "0123456789abcdef";
+    for (i = 0; i < SHA_DIGEST_SIZE; ++i) {
+        buffer[i*2] = alphabet[(digest[i] >> 4) & 0xf];
+        buffer[i*2+1] = alphabet[digest[i] & 0xf];
+    }
+    buffer[i*2] = '\0';
+    return buffer;
+}
+
 // mount(fs_type, partition_type, location, mount_point)
 //
 //    fs_type="yaffs2" partition_type="MTD"     location=partition
@@ -414,6 +430,54 @@
 }
 
 
+DontCareMap* ReadDontCareMapFromZip(ZipArchive* za, const char* path) {
+    const char* name = "ReadDontCareMapFromZip";
+
+    const ZipEntry* entry = mzFindZipEntry(za, path);
+    if (entry == NULL) {
+        printf("%s: no %s in package\n", name, path);
+        return NULL;
+    }
+
+    size_t map_size = mzGetZipEntryUncompLen(entry);
+    char* map_data = malloc(map_size);
+    if (map_data == NULL) {
+        printf("%s: failed to allocate %zu bytes for %s\n",
+               name, map_size, path);
+        return NULL;
+    }
+
+    if (!mzExtractZipEntryToBuffer(za, entry, (unsigned char*) map_data)) {
+        printf("%s: failed to read %s\n", name, path);
+        return NULL;
+    }
+
+    char* p = map_data;
+    DontCareMap* map = (DontCareMap*) malloc(sizeof(DontCareMap));
+
+    map->block_size = strtoul(p, &p, 0);
+    if (map->block_size != 4096) {
+        printf("%s: unexpected block size %zu\n", name, map->block_size);
+        return NULL;
+    }
+
+    map->region_count = strtoul(p, &p, 0);
+    map->regions = (int*) malloc(map->region_count * sizeof(int));
+
+    int i;
+    for (i = 0; i < map->region_count; ++i) {
+        map->regions[i] = strtoul(p, &p, 0);
+    }
+
+    return map;
+}
+
+bool MapWriter(const unsigned char* data, int dataLen, void* cookie) {
+    return write_with_map(data, dataLen, (MapState*) cookie) == dataLen;
+}
+
+// package_extract_file(package_path, destination_path, map_path)
+//   or
 // package_extract_file(package_path, destination_path)
 //   or
 // package_extract_file(package_path)
@@ -421,19 +485,30 @@
 //   function (the char* returned is actually a FileContents*).
 Value* PackageExtractFileFn(const char* name, State* state,
                            int argc, Expr* argv[]) {
-    if (argc != 1 && argc != 2) {
-        return ErrorAbort(state, "%s() expects 1 or 2 args, got %d",
+    if (argc < 1 || argc > 3) {
+        return ErrorAbort(state, "%s() expects 1 or 2 or 3 args, got %d",
                           name, argc);
     }
     bool success = false;
-    if (argc == 2) {
-        // The two-argument version extracts to a file.
+    if (argc >= 2) {
+        // The two-argument version extracts to a file; the three-arg
+        // version extracts to a file, skipping over regions in a
+        // don't care map.
+
+        ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip;
 
         char* zip_path;
         char* dest_path;
-        if (ReadArgs(state, argv, 2, &zip_path, &dest_path) < 0) return NULL;
+        char* map_path = NULL;
+        DontCareMap* map = NULL;
+        if (argc == 2) {
+            if (ReadArgs(state, argv, 2, &zip_path, &dest_path) < 0) return NULL;
+        } else {
+            if (ReadArgs(state, argv, 3, &zip_path, &dest_path, &map_path) < 0) return NULL;
+            map = ReadDontCareMapFromZip(za, map_path);
+            if (map == NULL) goto done2;
+        }
 
-        ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip;
         const ZipEntry* entry = mzFindZipEntry(za, zip_path);
         if (entry == NULL) {
             printf("%s: no %s in package\n", name, zip_path);
@@ -446,12 +521,26 @@
                     name, dest_path, strerror(errno));
             goto done2;
         }
-        success = mzExtractZipEntryToFile(za, entry, fileno(f));
+        if (map) {
+            MapState state;
+            state.map = map;
+            state.cr = 0;
+            state.so_far = 0;
+            state.f = f;
+            success = mzProcessZipEntryContents(za, entry, MapWriter, &state);
+        } else {
+            success = mzExtractZipEntryToFile(za, entry, fileno(f));
+        }
         fclose(f);
 
       done2:
         free(zip_path);
         free(dest_path);
+        free(map_path);
+        if (map) {
+            free(map->regions);
+            free(map);
+        }
         return StringValue(strdup(success ? "t" : ""));
     } else {
         // The one-argument version returns the contents of the file
@@ -1053,8 +1142,117 @@
     return StringValue(strdup(CacheSizeCheck(bytes) ? "" : "t"));
 }
 
+bool CheckMappedFileSha1(FILE* f, DontCareMap* map, uint8_t* intended_digest) {
+    MapState state;
 
-// apply_patch(srcfile, tgtfile, tgtsha1, tgtsize, sha1_1, patch_1, ...)
+    state.f = f;
+    state.so_far = 0;
+    state.cr = 0;
+    state.map = map;
+
+    SHA_CTX ctx;
+    SHA_init(&ctx);
+
+    unsigned char buffer[32173];
+    size_t bytes_read;
+
+    while ((bytes_read = read_with_map(buffer, sizeof(buffer), &state)) > 0) {
+        SHA_update(&ctx, buffer, bytes_read);
+    }
+    const uint8_t* digest = SHA_final(&ctx);
+
+    return memcmp(digest, intended_digest, SHA_DIGEST_SIZE) == 0;
+}
+
+
+// syspatch(file, tgt_mapfile, tgt_sha1, init_mapfile, init_sha1, patch)
+
+Value* SysPatchFn(const char* name, State* state, int argc, Expr* argv[]) {
+    if (argc != 6) {
+        return ErrorAbort(state, "%s(): expected 6 args, got %d", name, argc);
+    }
+
+    char* filename;
+    char* target_mapfilename;
+    char* target_sha1;
+    char* init_mapfilename;
+    char* init_sha1;
+    char* patch_filename;
+    uint8_t target_digest[SHA_DIGEST_SIZE];
+    uint8_t init_digest[SHA_DIGEST_SIZE];
+
+    if (ReadArgs(state, argv, 6, &filename,
+                 &target_mapfilename, &target_sha1,
+                 &init_mapfilename, &init_sha1, &patch_filename) < 0) {
+        return NULL;
+    }
+
+    if (ParseSha1(target_sha1, target_digest) != 0) {
+        printf("%s(): failed to parse '%s' as target SHA-1", name, target_sha1);
+        memset(target_digest, 0, SHA_DIGEST_SIZE);
+    }
+    if (ParseSha1(init_sha1, init_digest) != 0) {
+        printf("%s(): failed to parse '%s' as init SHA-1", name, init_sha1);
+        memset(init_digest, 0, SHA_DIGEST_SIZE);
+    }
+
+    ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip;
+    FILE* src = fopen(filename, "r");
+
+    DontCareMap* init_map = ReadDontCareMapFromZip(za, init_mapfilename);
+    if (init_map == NULL) return ErrorAbort(state, "%s(): failed to read init map\n", name);
+    DontCareMap* target_map = ReadDontCareMapFromZip(za, target_mapfilename);
+    if (target_map == NULL) return ErrorAbort(state, "%s(): failed to read target map\n", name);
+
+    if (CheckMappedFileSha1(src, init_map, init_digest)) {
+        // If the partition contents match the init_digest, then we need to apply the patch.
+
+        rewind(src);
+
+        const ZipEntry* entry = mzFindZipEntry(za, patch_filename);
+        if (entry == NULL) {
+            return ErrorAbort(state, "%s(): no %s in package\n", name, patch_filename);
+        }
+
+        unsigned char* patch_data;
+        size_t patch_len;
+        if (!mzGetStoredEntry(za, entry, &patch_data, &patch_len)) {
+            return ErrorAbort(state, "%s(): failed to get %s entry\n", name, patch_filename);
+        }
+
+        FILE* tgt = fopen(filename, "r+");
+
+        int ret = syspatch(src, init_map, patch_data, patch_len, tgt, target_map);
+
+        fclose(src);
+        fclose(tgt);
+
+        if (ret != 0) {
+            return ErrorAbort(state, "%s(): patching failed\n", name);
+        }
+    } else {
+        rewind(src);
+        if (CheckMappedFileSha1(src, target_map, target_digest)) {
+            // If the partition contents match the target already, we
+            // don't need to do anything.
+            printf("%s: output is already target\n", name);
+        } else {
+            return ErrorAbort(state, "%s(): %s in unknown state\n", name, filename);
+        }
+    }
+
+  done:
+    free(target_sha1);
+    free(target_mapfilename);
+    free(init_sha1);
+    free(init_mapfilename);
+    free(patch_filename);
+    return StringValue(filename);
+
+}
+
+// apply_patch(file, size, init_sha1, tgt_sha1, patch)
+
 Value* ApplyPatchFn(const char* name, State* state, int argc, Expr* argv[]) {
     if (argc < 6 || (argc % 2) == 1) {
         return ErrorAbort(state, "%s(): expected at least 6 args and an "
@@ -1239,19 +1437,6 @@
     return StringValue(strdup(buffer));
 }
 
-// Take a sha-1 digest and return it as a newly-allocated hex string.
-static char* PrintSha1(uint8_t* digest) {
-    char* buffer = malloc(SHA_DIGEST_SIZE*2 + 1);
-    int i;
-    const char* alphabet = "0123456789abcdef";
-    for (i = 0; i < SHA_DIGEST_SIZE; ++i) {
-        buffer[i*2] = alphabet[(digest[i] >> 4) & 0xf];
-        buffer[i*2+1] = alphabet[digest[i] & 0xf];
-    }
-    buffer[i*2] = '\0';
-    return buffer;
-}
-
 // sha1_check(data)
 //    to return the sha1 of the data (given in the format returned by
 //    read_file).
@@ -1322,7 +1507,7 @@
     v->type = VAL_BLOB;
 
     FileContents fc;
-    if (LoadFileContents(filename, &fc, RETOUCH_DONT_MASK) != 0) {
+    if (LoadFileContents(filename, &fc) != 0) {
         free(filename);
         v->size = -1;
         v->data = NULL;
@@ -1419,7 +1604,7 @@
 // Return the value most recently saved with SetStageFn.  The argument
 // is the block device for the misc partition.
 Value* GetStageFn(const char* name, State* state, int argc, Expr* argv[]) {
-    if (argc != 2) {
+    if (argc != 1) {
         return ErrorAbort(state, "%s() expects 1 arg, got %d", name, argc);
     }
 
@@ -1436,6 +1621,27 @@
     return StringValue(strdup(buffer));
 }
 
+Value* WipeBlockDeviceFn(const char* name, State* state, int argc, Expr* argv[]) {
+    if (argc != 2) {
+        return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc);
+    }
+
+    char* filename;
+    char* len_str;
+    if (ReadArgs(state, argv, 2, &filename, &len_str) < 0) return NULL;
+
+    size_t len = strtoull(len_str, NULL, 0);
+    int fd = open(filename, O_WRONLY, 0644);
+    int success = wipe_block_device(fd, len);
+
+    free(filename);
+    free(len_str);
+
+    close(fd);
+
+    return StringValue(strdup(success ? "t" : ""));
+}
+
 void RegisterInstallFunctions() {
     RegisterFunction("mount", MountFn);
     RegisterFunction("is_mounted", IsMountedFn);
@@ -1469,6 +1675,9 @@
     RegisterFunction("apply_patch_check", ApplyPatchCheckFn);
     RegisterFunction("apply_patch_space", ApplyPatchSpaceFn);
 
+    RegisterFunction("wipe_block_device", WipeBlockDeviceFn);
+    RegisterFunction("syspatch", SysPatchFn);
+
     RegisterFunction("read_file", ReadFileFn);
     RegisterFunction("sha1_check", Sha1CheckFn);
     RegisterFunction("rename", RenameFn);
diff --git a/updater/updater.c b/updater/updater.c
index c7009fe..b7af3e5 100644
--- a/updater/updater.c
+++ b/updater/updater.c
@@ -22,6 +22,7 @@
 #include "updater.h"
 #include "install.h"
 #include "minzip/Zip.h"
+#include "minzip/SysUtil.h"
 
 // Generated by the makefile, this function defines the
 // RegisterDeviceExtensions() function, which calls all the
@@ -65,19 +66,24 @@
 
     // Extract the script from the package.
 
-    char* package_data = argv[3];
+    const char* package_filename = argv[3];
+    MemMapping map;
+    if (sysMapFile(package_filename, &map) != 0) {
+        printf("failed to map package %s\n", argv[3]);
+        return 3;
+    }
     ZipArchive za;
     int err;
-    err = mzOpenZipArchive(package_data, &za);
+    err = mzOpenZipArchive(map.addr, map.length, &za);
     if (err != 0) {
         printf("failed to open package %s: %s\n",
-                package_data, strerror(err));
+               argv[3], strerror(err));
         return 3;
     }
 
     const ZipEntry* script_entry = mzFindZipEntry(&za, SCRIPT_NAME);
     if (script_entry == NULL) {
-        printf("failed to find %s in %s\n", SCRIPT_NAME, package_data);
+        printf("failed to find %s in %s\n", SCRIPT_NAME, package_filename);
         return 4;
     }
 
@@ -99,8 +105,7 @@
 
     Expr* root;
     int error_count = 0;
-    yy_scan_string(script);
-    int error = yyparse(&root, &error_count);
+    int error = parse_string(script, &root, &error_count);
     if (error != 0 || error_count > 0) {
         printf("%d parse errors\n", error_count);
         return 6;
@@ -152,6 +157,7 @@
     if (updater_info.package_zip) {
         mzCloseZipArchive(updater_info.package_zip);
     }
+    sysReleaseMap(&map);
     free(script);
 
     return 0;
diff --git a/verifier.cpp b/verifier.cpp
index 019552b..eeff95a 100644
--- a/verifier.cpp
+++ b/verifier.cpp
@@ -111,15 +111,10 @@
 // Return VERIFY_SUCCESS, VERIFY_FAILURE (if any error is encountered
 // or no key matches the signature).
 
-int verify_file(const char* path, const Certificate* pKeys, unsigned int numKeys) {
+int verify_file(unsigned char* addr, size_t length,
+                const Certificate* pKeys, unsigned int numKeys) {
     ui->SetProgress(0.0);
 
-    FILE* f = fopen(path, "rb");
-    if (f == NULL) {
-        LOGE("failed to open %s (%s)\n", path, strerror(errno));
-        return VERIFY_FAILURE;
-    }
-
     // An archive with a whole-file signature will end in six bytes:
     //
     //   (2-byte signature start) $ff $ff (2-byte comment size)
@@ -131,22 +126,15 @@
 
 #define FOOTER_SIZE 6
 
-    if (fseek(f, -FOOTER_SIZE, SEEK_END) != 0) {
-        LOGE("failed to seek in %s (%s)\n", path, strerror(errno));
-        fclose(f);
+    if (length < FOOTER_SIZE) {
+        LOGE("not big enough to contain footer\n");
         return VERIFY_FAILURE;
     }
 
-    unsigned char footer[FOOTER_SIZE];
-    if (fread(footer, 1, FOOTER_SIZE, f) != FOOTER_SIZE) {
-        LOGE("failed to read footer from %s (%s)\n", path, strerror(errno));
-        fclose(f);
-        return VERIFY_FAILURE;
-    }
+    unsigned char* footer = addr + length - FOOTER_SIZE;
 
     if (footer[2] != 0xff || footer[3] != 0xff) {
         LOGE("footer is wrong\n");
-        fclose(f);
         return VERIFY_FAILURE;
     }
 
@@ -157,7 +145,6 @@
 
     if (signature_start <= FOOTER_SIZE) {
         LOGE("Signature start is in the footer");
-        fclose(f);
         return VERIFY_FAILURE;
     }
 
@@ -167,9 +154,8 @@
     // comment length.
     size_t eocd_size = comment_size + EOCD_HEADER_SIZE;
 
-    if (fseek(f, -eocd_size, SEEK_END) != 0) {
-        LOGE("failed to seek in %s (%s)\n", path, strerror(errno));
-        fclose(f);
+    if (length < eocd_size) {
+        LOGE("not big enough to contain EOCD\n");
         return VERIFY_FAILURE;
     }
 
@@ -177,26 +163,15 @@
     // This is everything except the signature data and length, which
     // includes all of the EOCD except for the comment length field (2
     // bytes) and the comment data.
-    size_t signed_len = ftell(f) + EOCD_HEADER_SIZE - 2;
+    size_t signed_len = length - eocd_size + EOCD_HEADER_SIZE - 2;
 
-    unsigned char* eocd = (unsigned char*)malloc(eocd_size);
-    if (eocd == NULL) {
-        LOGE("malloc for EOCD record failed\n");
-        fclose(f);
-        return VERIFY_FAILURE;
-    }
-    if (fread(eocd, 1, eocd_size, f) != eocd_size) {
-        LOGE("failed to read eocd from %s (%s)\n", path, strerror(errno));
-        fclose(f);
-        return VERIFY_FAILURE;
-    }
+    unsigned char* eocd = addr + length - eocd_size;
 
     // If this is really is the EOCD record, it will begin with the
     // magic number $50 $4b $05 $06.
     if (eocd[0] != 0x50 || eocd[1] != 0x4b ||
         eocd[2] != 0x05 || eocd[3] != 0x06) {
         LOGE("signature length doesn't match EOCD marker\n");
-        fclose(f);
         return VERIFY_FAILURE;
     }
 
@@ -209,7 +184,6 @@
             // which could be exploitable.  Fail verification if
             // this sequence occurs anywhere after the real one.
             LOGE("EOCD marker occurs after start of EOCD\n");
-            fclose(f);
             return VERIFY_FAILURE;
         }
     }
@@ -229,35 +203,23 @@
     SHA256_CTX sha256_ctx;
     SHA_init(&sha1_ctx);
     SHA256_init(&sha256_ctx);
-    unsigned char* buffer = (unsigned char*)malloc(BUFFER_SIZE);
-    if (buffer == NULL) {
-        LOGE("failed to alloc memory for sha1 buffer\n");
-        fclose(f);
-        return VERIFY_FAILURE;
-    }
 
     double frac = -1.0;
     size_t so_far = 0;
-    fseek(f, 0, SEEK_SET);
     while (so_far < signed_len) {
-        size_t size = BUFFER_SIZE;
-        if (signed_len - so_far < size) size = signed_len - so_far;
-        if (fread(buffer, 1, size, f) != size) {
-            LOGE("failed to read data from %s (%s)\n", path, strerror(errno));
-            fclose(f);
-            return VERIFY_FAILURE;
-        }
-        if (need_sha1) SHA_update(&sha1_ctx, buffer, size);
-        if (need_sha256) SHA256_update(&sha256_ctx, buffer, size);
+        size_t size = signed_len - so_far;
+        if (size > BUFFER_SIZE) size = BUFFER_SIZE;
+
+        if (need_sha1) SHA_update(&sha1_ctx, addr + so_far, size);
+        if (need_sha256) SHA256_update(&sha256_ctx, addr + so_far, size);
         so_far += size;
+
         double f = so_far / (double)signed_len;
         if (f > frac + 0.02 || size == so_far) {
             ui->SetProgress(f);
             frac = f;
         }
     }
-    fclose(f);
-    free(buffer);
 
     const uint8_t* sha1 = SHA_final(&sha1_ctx);
     const uint8_t* sha256 = SHA256_final(&sha256_ctx);
@@ -269,10 +231,8 @@
     if (!read_pkcs7(eocd + eocd_size - signature_start, signature_size, &sig_der,
             &sig_der_length)) {
         LOGE("Could not find signature DER block\n");
-        free(eocd);
         return VERIFY_FAILURE;
     }
-    free(eocd);
 
     /*
      * Check to make sure at least one of the keys matches the signature. Since
diff --git a/verifier.h b/verifier.h
index 023d3bf..15f8d98 100644
--- a/verifier.h
+++ b/verifier.h
@@ -37,10 +37,13 @@
     ECPublicKey* ec;
 } Certificate;
 
-/* Look in the file for a signature footer, and verify that it
- * matches one of the given keys.  Return one of the constants below.
+/* addr and length define a an update package file that has been
+ * loaded (or mmap'ed, or whatever) into memory.  Verify that the file
+ * is signed and the signature matches one of the given keys.  Return
+ * one of the constants below.
  */
-int verify_file(const char* path, const Certificate *pKeys, unsigned int numKeys);
+int verify_file(unsigned char* addr, size_t length,
+                const Certificate *pKeys, unsigned int numKeys);
 
 Certificate* load_keys(const char* filename, int* numKeys);
 
diff --git a/verifier_test.cpp b/verifier_test.cpp
index 88fcad4..10a5dda 100644
--- a/verifier_test.cpp
+++ b/verifier_test.cpp
@@ -17,12 +17,16 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdarg.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
 
 #include "common.h"
 #include "verifier.h"
 #include "ui.h"
 #include "mincrypt/sha.h"
 #include "mincrypt/sha256.h"
+#include "minzip/SysUtil.h"
 
 // This is build/target/product/security/testkey.x509.pem after being
 // dumped out by dumpkey.jar.
@@ -227,7 +231,13 @@
 
     ui = new FakeUI();
 
-    int result = verify_file(argv[argn], certs, num_keys);
+    MemMapping map;
+    if (sysMapFile(argv[argn], &map) != 0) {
+        fprintf(stderr, "failed to mmap %s: %s\n", argv[argn], strerror(errno));
+        return 4;
+    }
+
+    int result = verify_file(map.addr, map.length, certs, num_keys);
     if (result == VERIFY_SUCCESS) {
         printf("VERIFIED\n");
         return 0;