Fix integer overflows in recovery procedure. am: 1273956e69 am: 5978a71d29 am: 877dcba47a
am: f4ee1720f3

* commit 'f4ee1720f33a01518f79a2bf8df4664dab3564d4':
  Fix integer overflows in recovery procedure.
diff --git a/adb_install.cpp b/adb_install.cpp
index ebd4cac..e3b94ea 100644
--- a/adb_install.cpp
+++ b/adb_install.cpp
@@ -42,7 +42,7 @@
         ui->Print("failed to open driver control: %s\n", strerror(errno));
         return;
     }
-    if (write(fd, enabled ? "1" : "0", 1) < 0) {
+    if (TEMP_FAILURE_RETRY(write(fd, enabled ? "1" : "0", 1)) == -1) {
         ui->Print("failed to set driver control: %s\n", strerror(errno));
     }
     if (close(fd) < 0) {
diff --git a/applypatch/applypatch.c b/applypatch/applypatch.c
index 2c86e09..2358d42 100644
--- a/applypatch/applypatch.c
+++ b/applypatch/applypatch.c
@@ -422,20 +422,19 @@
             int attempt;
 
             for (attempt = 0; attempt < 2; ++attempt) {
-                lseek(fd, start, SEEK_SET);
+                if (TEMP_FAILURE_RETRY(lseek(fd, start, SEEK_SET)) == -1) {
+                    printf("failed seek on %s: %s\n",
+                           partition, strerror(errno));
+                    return -1;
+                }
                 while (start < len) {
                     size_t to_write = len - start;
                     if (to_write > 1<<20) to_write = 1<<20;
 
-                    ssize_t written = write(fd, data+start, to_write);
-                    if (written < 0) {
-                        if (errno == EINTR) {
-                            written = 0;
-                        } else {
-                            printf("failed write writing to %s (%s)\n",
-                                   partition, strerror(errno));
-                            return -1;
-                        }
+                    ssize_t written = TEMP_FAILURE_RETRY(write(fd, data+start, to_write));
+                    if (written == -1) {
+                        printf("failed write writing to %s: %s\n", partition, strerror(errno));
+                        return -1;
                     }
                     start += written;
                 }
@@ -460,13 +459,20 @@
                 // won't just be reading the cache.
                 sync();
                 int dc = open("/proc/sys/vm/drop_caches", O_WRONLY);
-                write(dc, "3\n", 2);
+                if (TEMP_FAILURE_RETRY(write(dc, "3\n", 2)) == -1) {
+                    printf("write to /proc/sys/vm/drop_caches failed: %s\n", strerror(errno));
+                } else {
+                    printf("  caches dropped\n");
+                }
                 close(dc);
                 sleep(1);
-                printf("  caches dropped\n");
 
                 // verify
-                lseek(fd, 0, SEEK_SET);
+                if (TEMP_FAILURE_RETRY(lseek(fd, 0, SEEK_SET)) == -1) {
+                    printf("failed to seek back to beginning of %s: %s\n",
+                           partition, strerror(errno));
+                    return -1;
+                }
                 unsigned char buffer[4096];
                 start = len;
                 size_t p;
@@ -476,15 +482,12 @@
 
                     size_t so_far = 0;
                     while (so_far < to_read) {
-                        ssize_t read_count = read(fd, buffer+so_far, to_read-so_far);
-                        if (read_count < 0) {
-                            if (errno == EINTR) {
-                                read_count = 0;
-                            } else {
-                                printf("verify read error %s at %zu: %s\n",
-                                       partition, p, strerror(errno));
-                                return -1;
-                            }
+                        ssize_t read_count =
+                                TEMP_FAILURE_RETRY(read(fd, buffer+so_far, to_read-so_far));
+                        if (read_count == -1) {
+                            printf("verify read error %s at %zu: %s\n",
+                                   partition, p, strerror(errno));
+                            return -1;
                         }
                         if ((size_t)read_count < to_read) {
                             printf("short verify read %s at %zu: %zd %zu %s\n",
@@ -625,8 +628,8 @@
     ssize_t done = 0;
     ssize_t wrote;
     while (done < (ssize_t) len) {
-        wrote = write(fd, data+done, len-done);
-        if (wrote <= 0) {
+        wrote = TEMP_FAILURE_RETRY(write(fd, data+done, len-done));
+        if (wrote == -1) {
             printf("error writing %d bytes: %s\n", (int)(len-done), strerror(errno));
             return done;
         }
@@ -659,7 +662,7 @@
         printf("failed to statfs %s: %s\n", filename, strerror(errno));
         return -1;
     }
-    return sf.f_bsize * sf.f_bfree;
+    return sf.f_bsize * sf.f_bavail;
 }
 
 int CacheSizeCheck(size_t bytes) {
diff --git a/device.h b/device.h
index dad8ccd..f74b6b0 100644
--- a/device.h
+++ b/device.h
@@ -91,13 +91,16 @@
     static const int kHighlightDown = -3;
     static const int kInvokeItem = -4;
 
-    // Called when we do a wipe data/factory reset operation (either via a
-    // reboot from the main system with the --wipe_data flag, or when the
-    // user boots into recovery manually and selects the option from the
-    // menu.)  Can perform whatever device-specific wiping actions are
-    // needed.  Return 0 on success.  The userdata and cache partitions
-    // are erased AFTER this returns (whether it returns success or not).
-    virtual int WipeData() { return 0; }
+    // Called before and after we do a wipe data/factory reset operation,
+    // either via a reboot from the main system with the --wipe_data flag,
+    // or when the user boots into recovery image manually and selects the
+    // option from the menu, to perform whatever device-specific wiping
+    // actions are needed.
+    // Return true on success; returning false from PreWipeData will prevent
+    // the regular wipe, and returning false from PostWipeData will cause
+    // the wipe to be considered a failure.
+    virtual bool PreWipeData() { return true; }
+    virtual bool PostWipeData() { return true; }
 
   private:
     RecoveryUI* ui_;
diff --git a/fuse_sdcard_provider.c b/fuse_sdcard_provider.c
index ca8c914..4565c7b 100644
--- a/fuse_sdcard_provider.c
+++ b/fuse_sdcard_provider.c
@@ -36,19 +36,17 @@
 static int read_block_file(void* cookie, uint32_t block, uint8_t* buffer, uint32_t fetch_size) {
     struct file_data* fd = (struct file_data*)cookie;
 
-    if (lseek(fd->fd, block * fd->block_size, SEEK_SET) < 0) {
-        printf("seek on sdcard failed: %s\n", strerror(errno));
+    off64_t offset = ((off64_t) block) * fd->block_size;
+    if (TEMP_FAILURE_RETRY(lseek64(fd->fd, offset, SEEK_SET)) == -1) {
+        fprintf(stderr, "seek on sdcard failed: %s\n", strerror(errno));
         return -EIO;
     }
 
     while (fetch_size > 0) {
-        ssize_t r = read(fd->fd, buffer, fetch_size);
-        if (r < 0) {
-            if (r != -EINTR) {
-                printf("read on sdcard failed: %s\n", strerror(errno));
-                return -EIO;
-            }
-            r = 0;
+        ssize_t r = TEMP_FAILURE_RETRY(read(fd->fd, buffer, fetch_size));
+        if (r == -1) {
+            fprintf(stderr, "read on sdcard failed: %s\n", strerror(errno));
+            return -EIO;
         }
         fetch_size -= r;
         buffer += r;
diff --git a/fuse_sideload.c b/fuse_sideload.c
index 1dd84e9..48e6cc5 100644
--- a/fuse_sideload.c
+++ b/fuse_sideload.c
@@ -442,14 +442,12 @@
     }
     uint8_t request_buffer[sizeof(struct fuse_in_header) + PATH_MAX*8];
     for (;;) {
-        ssize_t len = read(fd.ffd, request_buffer, sizeof(request_buffer));
-        if (len < 0) {
-            if (errno != EINTR) {
-                perror("read request");
-                if (errno == ENODEV) {
-                    result = -1;
-                    break;
-                }
+        ssize_t len = TEMP_FAILURE_RETRY(read(fd.ffd, request_buffer, sizeof(request_buffer)));
+        if (len == -1) {
+            perror("read request");
+            if (errno == ENODEV) {
+                result = -1;
+                break;
             }
             continue;
         }
@@ -508,7 +506,7 @@
             outhdr.len = sizeof(outhdr);
             outhdr.error = result;
             outhdr.unique = hdr->unique;
-            write(fd.ffd, &outhdr, sizeof(outhdr));
+            TEMP_FAILURE_RETRY(write(fd.ffd, &outhdr, sizeof(outhdr)));
         }
     }
 
diff --git a/minadbd/Android.mk b/minadbd/Android.mk
index cbfd76e..a7a3e08 100644
--- a/minadbd/Android.mk
+++ b/minadbd/Android.mk
@@ -20,6 +20,7 @@
 LOCAL_CONLY_FLAGS := -Wimplicit-function-declaration
 LOCAL_C_INCLUDES := bootable/recovery system/core/adb
 LOCAL_WHOLE_STATIC_LIBRARIES := libadbd
+LOCAL_STATIC_LIBRARIES := libbase
 
 include $(BUILD_STATIC_LIBRARY)
 
@@ -31,6 +32,6 @@
 LOCAL_CFLAGS := $(minadbd_cflags)
 LOCAL_C_INCLUDES := $(LOCAL_PATH) system/core/adb
 LOCAL_STATIC_LIBRARIES := libminadbd
-LOCAL_SHARED_LIBRARIES := liblog
+LOCAL_SHARED_LIBRARIES := liblog libbase libcutils
 
 include $(BUILD_NATIVE_TEST)
diff --git a/minadbd/adb_main.cpp b/minadbd/adb_main.cpp
index f6e2401..7fae99a 100644
--- a/minadbd/adb_main.cpp
+++ b/minadbd/adb_main.cpp
@@ -19,11 +19,12 @@
 #include <stdio.h>
 #include <stdlib.h>
 
-#define  TRACE_TAG   TRACE_ADB
+#define TRACE_TAG TRACE_ADB
 
 #include "sysdeps.h"
 
 #include "adb.h"
+#include "adb_auth.h"
 #include "transport.h"
 
 int adb_main(int is_daemon, int server_port)
@@ -35,6 +36,9 @@
     // No SIGCHLD. Let the service subproc handle its children.
     signal(SIGPIPE, SIG_IGN);
 
+    // We can't require authentication for sideloading. http://b/22025550.
+    auth_required = false;
+
     init_transport_registration();
     usb_init();
 
diff --git a/minadbd/fuse_adb_provider.cpp b/minadbd/fuse_adb_provider.cpp
index 5da7fd7..d71807d 100644
--- a/minadbd/fuse_adb_provider.cpp
+++ b/minadbd/fuse_adb_provider.cpp
@@ -26,13 +26,10 @@
 #include "fuse_adb_provider.h"
 #include "fuse_sideload.h"
 
-int read_block_adb(void* cookie, uint32_t block, uint8_t* buffer,
-                   uint32_t fetch_size) {
-    struct adb_data* ad = (struct adb_data*)cookie;
+int read_block_adb(void* data, uint32_t block, uint8_t* buffer, uint32_t fetch_size) {
+    adb_data* ad = reinterpret_cast<adb_data*>(data);
 
-    char buf[10];
-    snprintf(buf, sizeof(buf), "%08u", block);
-    if (!WriteStringFully(ad->sfd, buf)) {
+    if (!WriteFdFmt(ad->sfd, "%08u", block)) {
         fprintf(stderr, "failed to write to adb host: %s\n", strerror(errno));
         return -EIO;
     }
@@ -45,20 +42,18 @@
     return 0;
 }
 
-static void close_adb(void* cookie) {
-    struct adb_data* ad = (struct adb_data*)cookie;
-
-    WriteStringFully(ad->sfd, "DONEDONE");
+static void close_adb(void* data) {
+    adb_data* ad = reinterpret_cast<adb_data*>(data);
+    WriteFdExactly(ad->sfd, "DONEDONE");
 }
 
 int run_adb_fuse(int sfd, uint64_t file_size, uint32_t block_size) {
-    struct adb_data ad;
-    struct provider_vtab vtab;
-
+    adb_data ad;
     ad.sfd = sfd;
     ad.file_size = file_size;
     ad.block_size = block_size;
 
+    provider_vtab vtab;
     vtab.read_block = read_block_adb;
     vtab.close = close_adb;
 
diff --git a/minadbd/services.cpp b/minadbd/services.cpp
index a832567..dd1fd7c 100644
--- a/minadbd/services.cpp
+++ b/minadbd/services.cpp
@@ -43,15 +43,16 @@
     return 0;
 }
 
-static void sideload_host_service(int sfd, void* cookie) {
-    char* saveptr;
-    const char* s = adb_strtok_r(reinterpret_cast<char*>(cookie), ":", &saveptr);
-    uint64_t file_size = strtoull(s, NULL, 10);
-    s = adb_strtok_r(NULL, ":", &saveptr);
-    uint32_t block_size = strtoul(s, NULL, 10);
+static void sideload_host_service(int sfd, void* data) {
+    const char* args = reinterpret_cast<const char*>(data);
+    int file_size;
+    int block_size;
+    if (sscanf(args, "%d:%d", &file_size, &block_size) != 2) {
+        printf("bad sideload-host arguments: %s\n", args);
+        exit(1);
+    }
 
-    printf("sideload-host file size %" PRIu64 " block size %" PRIu32 "\n",
-           file_size, block_size);
+    printf("sideload-host file size %d block size %d\n", file_size, block_size);
 
     int result = run_adb_fuse(sfd, file_size, block_size);
 
diff --git a/minui/Android.mk b/minui/Android.mk
index 52f0662..97724fb 100644
--- a/minui/Android.mk
+++ b/minui/Android.mk
@@ -5,10 +5,12 @@
     events.cpp \
     graphics.cpp \
     graphics_adf.cpp \
+    graphics_drm.cpp \
     graphics_fbdev.cpp \
     resources.cpp \
 
 LOCAL_WHOLE_STATIC_LIBRARIES += libadf
+LOCAL_WHOLE_STATIC_LIBRARIES += libdrm
 LOCAL_STATIC_LIBRARIES += libpng
 
 LOCAL_MODULE := libminui
diff --git a/minui/events.cpp b/minui/events.cpp
index 2d47a58..3b2262a 100644
--- a/minui/events.cpp
+++ b/minui/events.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <dirent.h>
+#include <errno.h>
 #include <fcntl.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -165,7 +166,7 @@
 
 int ev_get_input(int fd, uint32_t epevents, input_event* ev) {
     if (epevents & EPOLLIN) {
-        ssize_t r = read(fd, ev, sizeof(*ev));
+        ssize_t r = TEMP_FAILURE_RETRY(read(fd, ev, sizeof(*ev)));
         if (r == sizeof(*ev)) {
             return 0;
         }
diff --git a/minui/graphics.cpp b/minui/graphics.cpp
index f09f1c6..c0eea9e 100644
--- a/minui/graphics.cpp
+++ b/minui/graphics.cpp
@@ -369,6 +369,11 @@
     }
 
     if (!gr_draw) {
+        gr_backend = open_drm();
+        gr_draw = gr_backend->init(gr_backend);
+    }
+
+    if (!gr_draw) {
         gr_backend = open_fbdev();
         gr_draw = gr_backend->init(gr_backend);
         if (gr_draw == NULL) {
diff --git a/minui/graphics.h b/minui/graphics.h
index 81a9233..52968eb 100644
--- a/minui/graphics.h
+++ b/minui/graphics.h
@@ -38,5 +38,6 @@
 
 minui_backend* open_fbdev();
 minui_backend* open_adf();
+minui_backend* open_drm();
 
 #endif
diff --git a/minui/graphics_drm.cpp b/minui/graphics_drm.cpp
new file mode 100644
index 0000000..03e33b7
--- /dev/null
+++ b/minui/graphics_drm.cpp
@@ -0,0 +1,476 @@
+/*
+ * Copyright (C) 2015 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 <drm_fourcc.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/cdefs.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+
+#include "minui.h"
+#include "graphics.h"
+
+#define ARRAY_SIZE(A) (sizeof(A)/sizeof(*(A)))
+
+struct drm_surface {
+    GRSurface base;
+    uint32_t fb_id;
+    uint32_t handle;
+};
+
+static drm_surface *drm_surfaces[2];
+static int current_buffer;
+
+static drmModeCrtc *main_monitor_crtc;
+static drmModeConnector *main_monitor_connector;
+
+static int drm_fd = -1;
+
+static void drm_disable_crtc(int drm_fd, drmModeCrtc *crtc) {
+    if (crtc) {
+        drmModeSetCrtc(drm_fd, crtc->crtc_id,
+                       0, // fb_id
+                       0, 0,  // x,y
+                       NULL,  // connectors
+                       0,     // connector_count
+                       NULL); // mode
+    }
+}
+
+static void drm_enable_crtc(int drm_fd, drmModeCrtc *crtc,
+                            struct drm_surface *surface) {
+    int32_t ret;
+
+    ret = drmModeSetCrtc(drm_fd, crtc->crtc_id,
+                         surface->fb_id,
+                         0, 0,  // x,y
+                         &main_monitor_connector->connector_id,
+                         1,  // connector_count
+                         &main_monitor_crtc->mode);
+
+    if (ret)
+        printf("drmModeSetCrtc failed ret=%d\n", ret);
+}
+
+static void drm_blank(minui_backend* backend __unused, bool blank) {
+    if (blank)
+        drm_disable_crtc(drm_fd, main_monitor_crtc);
+    else
+        drm_enable_crtc(drm_fd, main_monitor_crtc,
+                        drm_surfaces[current_buffer]);
+}
+
+static void drm_destroy_surface(struct drm_surface *surface) {
+    struct drm_gem_close gem_close;
+    int ret;
+
+    if(!surface)
+        return;
+
+    if (surface->base.data)
+        munmap(surface->base.data,
+               surface->base.row_bytes * surface->base.height);
+
+    if (surface->fb_id) {
+        ret = drmModeRmFB(drm_fd, surface->fb_id);
+        if (ret)
+            printf("drmModeRmFB failed ret=%d\n", ret);
+    }
+
+    if (surface->handle) {
+        memset(&gem_close, 0, sizeof(gem_close));
+        gem_close.handle = surface->handle;
+
+        ret = drmIoctl(drm_fd, DRM_IOCTL_GEM_CLOSE, &gem_close);
+        if (ret)
+            printf("DRM_IOCTL_GEM_CLOSE failed ret=%d\n", ret);
+    }
+
+    free(surface);
+}
+
+static int drm_format_to_bpp(uint32_t format) {
+    switch(format) {
+        case DRM_FORMAT_ABGR8888:
+        case DRM_FORMAT_BGRA8888:
+        case DRM_FORMAT_RGBX8888:
+        case DRM_FORMAT_BGRX8888:
+        case DRM_FORMAT_XBGR8888:
+        case DRM_FORMAT_XRGB8888:
+            return 32;
+        case DRM_FORMAT_RGB565:
+            return 16;
+        default:
+            printf("Unknown format %d\n", format);
+            return 32;
+    }
+}
+
+static drm_surface *drm_create_surface(int width, int height) {
+    struct drm_surface *surface;
+    struct drm_mode_create_dumb create_dumb;
+    uint32_t format;
+    int ret;
+
+    surface = (struct drm_surface*)calloc(1, sizeof(*surface));
+    if (!surface) {
+        printf("Can't allocate memory\n");
+        return NULL;
+    }
+
+#if defined(RECOVERY_ABGR)
+    format = DRM_FORMAT_RGBA8888;
+#elif defined(RECOVERY_BGRA)
+    format = DRM_FORMAT_ARGB8888;
+#elif defined(RECOVERY_RGBX)
+    format = DRM_FORMAT_XBGR8888;
+#else
+    format = DRM_FORMAT_RGB565;
+#endif
+
+    memset(&create_dumb, 0, sizeof(create_dumb));
+    create_dumb.height = height;
+    create_dumb.width = width;
+    create_dumb.bpp = drm_format_to_bpp(format);
+    create_dumb.flags = 0;
+
+    ret = drmIoctl(drm_fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_dumb);
+    if (ret) {
+        printf("DRM_IOCTL_MODE_CREATE_DUMB failed ret=%d\n",ret);
+        drm_destroy_surface(surface);
+        return NULL;
+    }
+    surface->handle = create_dumb.handle;
+
+    uint32_t handles[4], pitches[4], offsets[4];
+
+    handles[0] = surface->handle;
+    pitches[0] = create_dumb.pitch;
+    offsets[0] = 0;
+
+    ret = drmModeAddFB2(drm_fd, width, height,
+            format, handles, pitches, offsets,
+            &(surface->fb_id), 0);
+    if (ret) {
+        printf("drmModeAddFB2 failed ret=%d\n", ret);
+        drm_destroy_surface(surface);
+        return NULL;
+    }
+
+    struct drm_mode_map_dumb map_dumb;
+    memset(&map_dumb, 0, sizeof(map_dumb));
+    map_dumb.handle = create_dumb.handle;
+    ret = drmIoctl(drm_fd, DRM_IOCTL_MODE_MAP_DUMB, &map_dumb);
+    if (ret) {
+        printf("DRM_IOCTL_MODE_MAP_DUMB failed ret=%d\n",ret);
+        drm_destroy_surface(surface);
+        return NULL;;
+    }
+
+    surface->base.height = height;
+    surface->base.width = width;
+    surface->base.row_bytes = create_dumb.pitch;
+    surface->base.pixel_bytes = create_dumb.bpp / 8;
+    surface->base.data = (unsigned char*)
+                         mmap(NULL,
+                              surface->base.height * surface->base.row_bytes,
+                              PROT_READ | PROT_WRITE, MAP_SHARED,
+                              drm_fd, map_dumb.offset);
+    if (surface->base.data == MAP_FAILED) {
+        perror("mmap() failed");
+        drm_destroy_surface(surface);
+        return NULL;
+    }
+
+    return surface;
+}
+
+static drmModeCrtc *find_crtc_for_connector(int fd,
+                            drmModeRes *resources,
+                            drmModeConnector *connector) {
+    int i, j;
+    drmModeEncoder *encoder;
+    int32_t crtc;
+
+    /*
+     * Find the encoder. If we already have one, just use it.
+     */
+    if (connector->encoder_id)
+        encoder = drmModeGetEncoder(fd, connector->encoder_id);
+    else
+        encoder = NULL;
+
+    if (encoder && encoder->crtc_id) {
+        crtc = encoder->crtc_id;
+        drmModeFreeEncoder(encoder);
+        return drmModeGetCrtc(fd, crtc);
+    }
+
+    /*
+     * Didn't find anything, try to find a crtc and encoder combo.
+     */
+    crtc = -1;
+    for (i = 0; i < connector->count_encoders; i++) {
+        encoder = drmModeGetEncoder(fd, connector->encoders[i]);
+
+        if (encoder) {
+            for (j = 0; j < resources->count_crtcs; j++) {
+                if (!(encoder->possible_crtcs & (1 << j)))
+                    continue;
+                crtc = resources->crtcs[j];
+                break;
+            }
+            if (crtc >= 0) {
+                drmModeFreeEncoder(encoder);
+                return drmModeGetCrtc(fd, crtc);
+            }
+        }
+    }
+
+    return NULL;
+}
+
+static drmModeConnector *find_used_connector_by_type(int fd,
+                                 drmModeRes *resources,
+                                 unsigned type) {
+    int i;
+    for (i = 0; i < resources->count_connectors; i++) {
+        drmModeConnector *connector;
+
+        connector = drmModeGetConnector(fd, resources->connectors[i]);
+        if (connector) {
+            if ((connector->connector_type == type) &&
+                    (connector->connection == DRM_MODE_CONNECTED) &&
+                    (connector->count_modes > 0))
+                return connector;
+
+            drmModeFreeConnector(connector);
+        }
+    }
+    return NULL;
+}
+
+static drmModeConnector *find_first_connected_connector(int fd,
+                             drmModeRes *resources) {
+    int i;
+    for (i = 0; i < resources->count_connectors; i++) {
+        drmModeConnector *connector;
+
+        connector = drmModeGetConnector(fd, resources->connectors[i]);
+        if (connector) {
+            if ((connector->count_modes > 0) &&
+                    (connector->connection == DRM_MODE_CONNECTED))
+                return connector;
+
+            drmModeFreeConnector(connector);
+        }
+    }
+    return NULL;
+}
+
+static drmModeConnector *find_main_monitor(int fd, drmModeRes *resources,
+        uint32_t *mode_index) {
+    unsigned i = 0;
+    int modes;
+    /* Look for LVDS/eDP/DSI connectors. Those are the main screens. */
+    unsigned kConnectorPriority[] = {
+        DRM_MODE_CONNECTOR_LVDS,
+        DRM_MODE_CONNECTOR_eDP,
+        DRM_MODE_CONNECTOR_DSI,
+    };
+
+    drmModeConnector *main_monitor_connector = NULL;
+    do {
+        main_monitor_connector = find_used_connector_by_type(fd,
+                                         resources,
+                                         kConnectorPriority[i]);
+        i++;
+    } while (!main_monitor_connector && i < ARRAY_SIZE(kConnectorPriority));
+
+    /* If we didn't find a connector, grab the first one that is connected. */
+    if (!main_monitor_connector)
+        main_monitor_connector =
+                find_first_connected_connector(fd, resources);
+
+    /* If we still didn't find a connector, give up and return. */
+    if (!main_monitor_connector)
+        return NULL;
+
+    *mode_index = 0;
+    for (modes = 0; modes < main_monitor_connector->count_modes; modes++) {
+        if (main_monitor_connector->modes[modes].type &
+                DRM_MODE_TYPE_PREFERRED) {
+            *mode_index = modes;
+            break;
+        }
+    }
+
+    return main_monitor_connector;
+}
+
+static void disable_non_main_crtcs(int fd,
+                    drmModeRes *resources,
+                    drmModeCrtc* main_crtc) {
+    int i;
+    drmModeCrtc* crtc;
+
+    for (i = 0; i < resources->count_connectors; i++) {
+        drmModeConnector *connector;
+
+        connector = drmModeGetConnector(fd, resources->connectors[i]);
+        crtc = find_crtc_for_connector(fd, resources, connector);
+        if (crtc->crtc_id != main_crtc->crtc_id)
+            drm_disable_crtc(fd, crtc);
+        drmModeFreeCrtc(crtc);
+    }
+}
+
+static GRSurface* drm_init(minui_backend* backend __unused) {
+    drmModeRes *res = NULL;
+    uint32_t selected_mode;
+    char *dev_name;
+    int width, height;
+    int ret, i;
+
+    /* Consider DRM devices in order. */
+    for (i = 0; i < DRM_MAX_MINOR; i++) {
+        uint64_t cap = 0;
+
+        ret = asprintf(&dev_name, DRM_DEV_NAME, DRM_DIR_NAME, i);
+        if (ret < 0)
+            continue;
+
+        drm_fd = open(dev_name, O_RDWR, 0);
+        free(dev_name);
+        if (drm_fd < 0)
+            continue;
+
+        /* We need dumb buffers. */
+        ret = drmGetCap(drm_fd, DRM_CAP_DUMB_BUFFER, &cap);
+        if (ret || cap == 0) {
+            close(drm_fd);
+            continue;
+        }
+
+        res = drmModeGetResources(drm_fd);
+        if (!res) {
+            close(drm_fd);
+            continue;
+        }
+
+        /* Use this device if it has at least one connected monitor. */
+        if (res->count_crtcs > 0 && res->count_connectors > 0)
+            if (find_first_connected_connector(drm_fd, res))
+                break;
+
+        drmModeFreeResources(res);
+        close(drm_fd);
+        res = NULL;
+    }
+
+    if (drm_fd < 0 || res == NULL) {
+        perror("cannot find/open a drm device");
+        return NULL;
+    }
+
+    main_monitor_connector = find_main_monitor(drm_fd,
+            res, &selected_mode);
+
+    if (!main_monitor_connector) {
+        printf("main_monitor_connector not found\n");
+        drmModeFreeResources(res);
+        close(drm_fd);
+        return NULL;
+    }
+
+    main_monitor_crtc = find_crtc_for_connector(drm_fd, res,
+                                                main_monitor_connector);
+
+    if (!main_monitor_crtc) {
+        printf("main_monitor_crtc not found\n");
+        drmModeFreeResources(res);
+        close(drm_fd);
+        return NULL;
+    }
+
+    disable_non_main_crtcs(drm_fd,
+                           res, main_monitor_crtc);
+
+    main_monitor_crtc->mode = main_monitor_connector->modes[selected_mode];
+
+    width = main_monitor_crtc->mode.hdisplay;
+    height = main_monitor_crtc->mode.vdisplay;
+
+    drmModeFreeResources(res);
+
+    drm_surfaces[0] = drm_create_surface(width, height);
+    drm_surfaces[1] = drm_create_surface(width, height);
+    if (!drm_surfaces[0] || !drm_surfaces[1]) {
+        drm_destroy_surface(drm_surfaces[0]);
+        drm_destroy_surface(drm_surfaces[1]);
+        drmModeFreeResources(res);
+        close(drm_fd);
+        return NULL;
+    }
+
+    current_buffer = 0;
+
+    drm_enable_crtc(drm_fd, main_monitor_crtc, drm_surfaces[1]);
+
+    return &(drm_surfaces[0]->base);
+}
+
+static GRSurface* drm_flip(minui_backend* backend __unused) {
+    int ret;
+
+    ret = drmModePageFlip(drm_fd, main_monitor_crtc->crtc_id,
+                          drm_surfaces[current_buffer]->fb_id, 0, NULL);
+    if (ret < 0) {
+        printf("drmModePageFlip failed ret=%d\n", ret);
+        return NULL;
+    }
+    current_buffer = 1 - current_buffer;
+    return &(drm_surfaces[current_buffer]->base);
+}
+
+static void drm_exit(minui_backend* backend __unused) {
+    drm_disable_crtc(drm_fd, main_monitor_crtc);
+    drm_destroy_surface(drm_surfaces[0]);
+    drm_destroy_surface(drm_surfaces[1]);
+    drmModeFreeCrtc(main_monitor_crtc);
+    drmModeFreeConnector(main_monitor_connector);
+    close(drm_fd);
+    drm_fd = -1;
+}
+
+static minui_backend drm_backend = {
+    .init = drm_init,
+    .flip = drm_flip,
+    .blank = drm_blank,
+    .exit = drm_exit,
+};
+
+minui_backend* open_drm() {
+    return &drm_backend;
+}
diff --git a/minzip/SysUtil.c b/minzip/SysUtil.c
index 8601591..b1fb455 100644
--- a/minzip/SysUtil.c
+++ b/minzip/SysUtil.c
@@ -29,11 +29,13 @@
     assert(start_ != NULL);
     assert(length_ != NULL);
 
-    start = lseek(fd, 0L, SEEK_CUR);
-    end = lseek(fd, 0L, SEEK_END);
-    (void) lseek(fd, start, SEEK_SET);
+    // TODO: isn't start always 0 for the single call site? just use fstat instead?
 
-    if (start == (off_t) -1 || end == (off_t) -1) {
+    start = TEMP_FAILURE_RETRY(lseek(fd, 0L, SEEK_CUR));
+    end = TEMP_FAILURE_RETRY(lseek(fd, 0L, SEEK_END));
+
+    if (TEMP_FAILURE_RETRY(lseek(fd, start, SEEK_SET)) == -1 ||
+                start == (off_t) -1 || end == (off_t) -1) {
         LOGE("could not determine length of file\n");
         return -1;
     }
diff --git a/minzip/Zip.c b/minzip/Zip.c
index d3ff79b..40712e0 100644
--- a/minzip/Zip.c
+++ b/minzip/Zip.c
@@ -675,13 +675,11 @@
     }
     ssize_t soFar = 0;
     while (true) {
-        ssize_t n = write(fd, data+soFar, dataLen-soFar);
+        ssize_t n = TEMP_FAILURE_RETRY(write(fd, data+soFar, dataLen-soFar));
         if (n <= 0) {
             LOGE("Error writing %zd bytes from zip file from %p: %s\n",
                  dataLen-soFar, data+soFar, strerror(errno));
-            if (errno != EINTR) {
-              return false;
-            }
+            return false;
         } else if (n > 0) {
             soFar += n;
             if (soFar == dataLen) return true;
diff --git a/mtdutils/flash_image.c b/mtdutils/flash_image.c
index 5657dfc..36ffa13 100644
--- a/mtdutils/flash_image.c
+++ b/mtdutils/flash_image.c
@@ -72,7 +72,7 @@
     if (fd < 0) die("error opening %s", argv[2]);
 
     char header[HEADER_SIZE];
-    int headerlen = read(fd, header, sizeof(header));
+    int headerlen = TEMP_FAILURE_RETRY(read(fd, header, sizeof(header)));
     if (headerlen <= 0) die("error reading %s header", argv[2]);
 
     MtdReadContext *in = mtd_read_partition(partition);
@@ -104,7 +104,7 @@
     if (wrote != headerlen) die("error writing %s", argv[1]);
 
     int len;
-    while ((len = read(fd, buf, sizeof(buf))) > 0) {
+    while ((len = TEMP_FAILURE_RETRY(read(fd, buf, sizeof(buf)))) > 0) {
         wrote = mtd_write_data(out, buf, len);
         if (wrote != len) die("error writing %s", argv[1]);
     }
@@ -125,13 +125,13 @@
     if (mtd_partition_info(partition, NULL, &block_size, NULL))
         die("error getting %s block size", argv[1]);
 
-    if (lseek(fd, headerlen, SEEK_SET) != headerlen)
+    if (TEMP_FAILURE_RETRY(lseek(fd, headerlen, SEEK_SET)) != headerlen)
         die("error rewinding %s", argv[2]);
 
     int left = block_size - headerlen;
     while (left < 0) left += block_size;
     while (left > 0) {
-        len = read(fd, buf, left > (int)sizeof(buf) ? (int)sizeof(buf) : left);
+        len = TEMP_FAILURE_RETRY(read(fd, buf, left > (int)sizeof(buf) ? (int)sizeof(buf) : left));
         if (len <= 0) die("error reading %s", argv[2]);
         if (mtd_write_data(out, buf, len) != len)
             die("error writing %s", argv[1]);
diff --git a/mtdutils/mtdutils.c b/mtdutils/mtdutils.c
index 9a17e38..cc30334 100644
--- a/mtdutils/mtdutils.c
+++ b/mtdutils/mtdutils.c
@@ -108,7 +108,7 @@
     if (fd < 0) {
         goto bail;
     }
-    nbytes = read(fd, buf, sizeof(buf) - 1);
+    nbytes = TEMP_FAILURE_RETRY(read(fd, buf, sizeof(buf) - 1));
     close(fd);
     if (nbytes < 0) {
         goto bail;
@@ -279,12 +279,6 @@
     return ctx;
 }
 
-// Seeks to a location in the partition.  Don't mix with reads of
-// anything other than whole blocks; unpredictable things will result.
-void mtd_read_skip_to(const MtdReadContext* ctx, size_t offset) {
-    lseek64(ctx->fd, offset, SEEK_SET);
-}
-
 static int read_block(const MtdPartition *partition, int fd, char *data)
 {
     struct mtd_ecc_stats before, after;
@@ -293,13 +287,18 @@
         return -1;
     }
 
-    loff_t pos = lseek64(fd, 0, SEEK_CUR);
+    loff_t pos = TEMP_FAILURE_RETRY(lseek64(fd, 0, SEEK_CUR));
+    if (pos == -1) {
+        printf("mtd: read_block: couldn't SEEK_CUR: %s\n", strerror(errno));
+        return -1;
+    }
 
     ssize_t size = partition->erase_size;
     int mgbb;
 
     while (pos + size <= (int) partition->size) {
-        if (lseek64(fd, pos, SEEK_SET) != pos || read(fd, data, size) != size) {
+        if (TEMP_FAILURE_RETRY(lseek64(fd, pos, SEEK_SET)) != pos ||
+                    TEMP_FAILURE_RETRY(read(fd, data, size)) != size) {
             printf("mtd: read error at 0x%08llx (%s)\n",
                     pos, strerror(errno));
         } else if (ioctl(fd, ECCGETSTATS, &after)) {
@@ -409,8 +408,11 @@
     const MtdPartition *partition = ctx->partition;
     int fd = ctx->fd;
 
-    off_t pos = lseek(fd, 0, SEEK_CUR);
-    if (pos == (off_t) -1) return 1;
+    off_t pos = TEMP_FAILURE_RETRY(lseek(fd, 0, SEEK_CUR));
+    if (pos == (off_t) -1) {
+        printf("mtd: write_block: couldn't SEEK_CUR: %s\n", strerror(errno));
+        return -1;
+    }
 
     ssize_t size = partition->erase_size;
     while (pos + size <= (int) partition->size) {
@@ -435,15 +437,15 @@
                         pos, strerror(errno));
                 continue;
             }
-            if (lseek(fd, pos, SEEK_SET) != pos ||
-                write(fd, data, size) != size) {
+            if (TEMP_FAILURE_RETRY(lseek(fd, pos, SEEK_SET)) != pos ||
+                TEMP_FAILURE_RETRY(write(fd, data, size)) != size) {
                 printf("mtd: write error at 0x%08lx (%s)\n",
                         pos, strerror(errno));
             }
 
             char verify[size];
-            if (lseek(fd, pos, SEEK_SET) != pos ||
-                read(fd, verify, size) != size) {
+            if (TEMP_FAILURE_RETRY(lseek(fd, pos, SEEK_SET)) != pos ||
+                TEMP_FAILURE_RETRY(read(fd, verify, size)) != size) {
                 printf("mtd: re-read error at 0x%08lx (%s)\n",
                         pos, strerror(errno));
                 continue;
@@ -512,8 +514,11 @@
         ctx->stored = 0;
     }
 
-    off_t pos = lseek(ctx->fd, 0, SEEK_CUR);
-    if ((off_t) pos == (off_t) -1) return pos;
+    off_t pos = TEMP_FAILURE_RETRY(lseek(ctx->fd, 0, SEEK_CUR));
+    if ((off_t) pos == (off_t) -1) {
+        printf("mtd_erase_blocks: couldn't SEEK_CUR: %s\n", strerror(errno));
+        return -1;
+    }
 
     const int total = (ctx->partition->size - pos) / ctx->partition->erase_size;
     if (blocks < 0) blocks = total;
@@ -554,18 +559,3 @@
     free(ctx);
     return r;
 }
-
-/* Return the offset of the first good block at or after pos (which
- * might be pos itself).
- */
-off_t mtd_find_write_start(MtdWriteContext *ctx, off_t pos) {
-    int i;
-    for (i = 0; i < ctx->bad_block_count; ++i) {
-        if (ctx->bad_block_offsets[i] == pos) {
-            pos += ctx->partition->erase_size;
-        } else if (ctx->bad_block_offsets[i] > pos) {
-            return pos;
-        }
-    }
-    return pos;
-}
diff --git a/mtdutils/mtdutils.h b/mtdutils/mtdutils.h
index 2708c43..8059d6a 100644
--- a/mtdutils/mtdutils.h
+++ b/mtdutils/mtdutils.h
@@ -49,12 +49,10 @@
 MtdReadContext *mtd_read_partition(const MtdPartition *);
 ssize_t mtd_read_data(MtdReadContext *, char *data, size_t data_len);
 void mtd_read_close(MtdReadContext *);
-void mtd_read_skip_to(const MtdReadContext *, size_t offset);
 
 MtdWriteContext *mtd_write_partition(const MtdPartition *);
 ssize_t mtd_write_data(MtdWriteContext *, const char *data, size_t data_len);
 off_t mtd_erase_blocks(MtdWriteContext *, int blocks);  /* 0 ok, -1 for all */
-off_t mtd_find_write_start(MtdWriteContext *ctx, off_t pos);
 int mtd_write_close(MtdWriteContext *);
 
 #ifdef __cplusplus
diff --git a/recovery.cpp b/recovery.cpp
index 4dd8279..b7a5458 100644
--- a/recovery.cpp
+++ b/recovery.cpp
@@ -31,6 +31,9 @@
 #include <time.h>
 #include <unistd.h>
 
+#include <base/file.h>
+#include <base/stringprintf.h>
+
 #include "bootloader.h"
 #include "common.h"
 #include "cutils/properties.h"
@@ -65,8 +68,6 @@
   { NULL, 0, NULL, 0 },
 };
 
-#define LAST_LOG_FILE "/cache/recovery/last_log"
-
 static const char *CACHE_LOG_DIR = "/cache/recovery";
 static const char *COMMAND_FILE = "/cache/recovery/command";
 static const char *INTENT_FILE = "/cache/recovery/intent";
@@ -78,9 +79,8 @@
 static const char *TEMPORARY_LOG_FILE = "/tmp/recovery.log";
 static const char *TEMPORARY_INSTALL_FILE = "/tmp/last_install";
 static const char *LAST_KMSG_FILE = "/cache/recovery/last_kmsg";
-#define KLOG_DEFAULT_LEN (64 * 1024)
-
-#define KEEP_LOG_COUNT 10
+static const char *LAST_LOG_FILE = "/cache/recovery/last_log";
+static const int KEEP_LOG_COUNT = 10;
 
 RecoveryUI* ui = NULL;
 char* locale = NULL;
@@ -267,72 +267,55 @@
     set_bootloader_message(&boot);
 }
 
-// read from kernel log into buffer and write out to file
-static void
-save_kernel_log(const char *destination) {
-    int n;
-    char *buffer;
-    int klog_buf_len;
-    FILE *log;
-
-    klog_buf_len = klogctl(KLOG_SIZE_BUFFER, 0, 0);
+// Read from kernel log into buffer and write out to file.
+static void save_kernel_log(const char* destination) {
+    int klog_buf_len = klogctl(KLOG_SIZE_BUFFER, 0, 0);
     if (klog_buf_len <= 0) {
-        LOGE("Error getting klog size (%s), using default\n", strerror(errno));
-        klog_buf_len = KLOG_DEFAULT_LEN;
-    }
-
-    buffer = (char *)malloc(klog_buf_len);
-    if (!buffer) {
-        LOGE("Can't alloc %d bytes for klog buffer\n", klog_buf_len);
+        LOGE("Error getting klog size: %s\n", strerror(errno));
         return;
     }
 
-    n = klogctl(KLOG_READ_ALL, buffer, klog_buf_len);
-    if (n < 0) {
-        LOGE("Error in reading klog (%s)\n", strerror(errno));
-        free(buffer);
+    std::string buffer(klog_buf_len, 0);
+    int n = klogctl(KLOG_READ_ALL, &buffer[0], klog_buf_len);
+    if (n == -1) {
+        LOGE("Error in reading klog: %s\n", strerror(errno));
         return;
     }
-
-    log = fopen_path(destination, "w");
-    if (log == NULL) {
-        LOGE("Can't open %s\n", destination);
-        free(buffer);
-        return;
-    }
-    fwrite(buffer, n, 1, log);
-    check_and_fclose(log, destination);
-    free(buffer);
+    buffer.resize(n);
+    android::base::WriteStringToFile(buffer, destination);
 }
 
 // How much of the temp log we have copied to the copy in cache.
 static long tmplog_offset = 0;
 
-static void
-copy_log_file(const char* source, const char* destination, int append) {
-    FILE *log = fopen_path(destination, append ? "a" : "w");
-    if (log == NULL) {
+static void copy_log_file(const char* source, const char* destination, bool append) {
+    FILE* dest_fp = fopen_path(destination, append ? "a" : "w");
+    if (dest_fp == nullptr) {
         LOGE("Can't open %s\n", destination);
     } else {
-        FILE *tmplog = fopen(source, "r");
-        if (tmplog != NULL) {
+        FILE* source_fp = fopen(source, "r");
+        if (source_fp != nullptr) {
             if (append) {
-                fseek(tmplog, tmplog_offset, SEEK_SET);  // Since last write
+                fseek(source_fp, tmplog_offset, SEEK_SET);  // Since last write
             }
             char buf[4096];
-            while (fgets(buf, sizeof(buf), tmplog)) fputs(buf, log);
-            if (append) {
-                tmplog_offset = ftell(tmplog);
+            size_t bytes;
+            while ((bytes = fread(buf, 1, sizeof(buf), source_fp)) != 0) {
+                fwrite(buf, 1, bytes, dest_fp);
             }
-            check_and_fclose(tmplog, source);
+            if (append) {
+                tmplog_offset = ftell(source_fp);
+            }
+            check_and_fclose(source_fp, source);
         }
-        check_and_fclose(log, destination);
+        check_and_fclose(dest_fp, destination);
     }
 }
 
-// Rename last_log -> last_log.1 -> last_log.2 -> ... -> last_log.$max
-// Overwrites any existing last_log.$max.
-static void rotate_last_logs(int max) {
+// Rename last_log -> last_log.1 -> last_log.2 -> ... -> last_log.$max.
+// Similarly rename last_kmsg -> last_kmsg.1 -> ... -> last_kmsg.$max.
+// Overwrite any existing last_log.$max and last_kmsg.$max.
+static void rotate_logs(int max) {
     // Logs should only be rotated once.
     static bool rotated = false;
     if (rotated) {
@@ -340,14 +323,19 @@
     }
     rotated = true;
     ensure_path_mounted(LAST_LOG_FILE);
+    ensure_path_mounted(LAST_KMSG_FILE);
 
-    char oldfn[256];
-    char newfn[256];
     for (int i = max-1; i >= 0; --i) {
-        snprintf(oldfn, sizeof(oldfn), (i==0) ? LAST_LOG_FILE : (LAST_LOG_FILE ".%d"), i);
-        snprintf(newfn, sizeof(newfn), LAST_LOG_FILE ".%d", i+1);
-        // ignore errors
-        rename(oldfn, newfn);
+        std::string old_log = android::base::StringPrintf((i == 0) ? "%s" : "%s.%d",
+                LAST_LOG_FILE, i);
+        std::string new_log = android::base::StringPrintf("%s.%d", LAST_LOG_FILE, i+1);
+        // Ignore errors if old_log doesn't exist.
+        rename(old_log.c_str(), new_log.c_str());
+
+        std::string old_kmsg = android::base::StringPrintf((i == 0) ? "%s" : "%s.%d",
+                LAST_KMSG_FILE, i);
+        std::string new_kmsg = android::base::StringPrintf("%s.%d", LAST_KMSG_FILE, i+1);
+        rename(old_kmsg.c_str(), new_kmsg.c_str());
     }
 }
 
@@ -360,7 +348,7 @@
         return;
     }
 
-    rotate_last_logs(KEEP_LOG_COUNT);
+    rotate_logs(KEEP_LOG_COUNT);
 
     // Copy logs to cache so the system can find out what happened.
     copy_log_file(TEMPORARY_LOG_FILE, LOG_FILE, true);
@@ -429,8 +417,7 @@
     struct _saved_log_file* next;
 } saved_log_file;
 
-static int
-erase_volume(const char *volume) {
+static bool erase_volume(const char* volume) {
     bool is_cache = (strcmp(volume, CACHE_ROOT) == 0);
 
     ui->SetBackground(RecoveryUI::ERASING);
@@ -439,9 +426,10 @@
     saved_log_file* head = NULL;
 
     if (is_cache) {
-        // If we're reformatting /cache, we load any
-        // "/cache/recovery/last*" files into memory, so we can restore
-        // them after the reformat.
+        // If we're reformatting /cache, we load any past logs
+        // (i.e. "/cache/recovery/last_*") and the current log
+        // ("/cache/recovery/log") into memory, so we can restore them after
+        // the reformat.
 
         ensure_path_mounted(volume);
 
@@ -454,7 +442,7 @@
             strcat(path, "/");
             int path_len = strlen(path);
             while ((de = readdir(d)) != NULL) {
-                if (strncmp(de->d_name, "last", 4) == 0) {
+                if (strncmp(de->d_name, "last_", 5) == 0 || strcmp(de->d_name, "log") == 0) {
                     saved_log_file* p = (saved_log_file*) malloc(sizeof(saved_log_file));
                     strcpy(path+path_len, de->d_name);
                     p->name = strdup(path);
@@ -510,7 +498,7 @@
         copy_logs();
     }
 
-    return result;
+    return (result == 0);
 }
 
 static int
@@ -684,13 +672,13 @@
     modified_flash = true;
 
     ui->Print("\n-- Wiping data...\n");
-    if (device->WipeData() == 0 && erase_volume("/data") == 0 && erase_volume("/cache") == 0) {
-        ui->Print("Data wipe complete.\n");
-        return true;
-    } else {
-        ui->Print("Data wipe failed.\n");
-        return false;
-    }
+    bool success =
+        device->PreWipeData() &&
+        erase_volume("/data") &&
+        erase_volume("/cache") &&
+        device->PostWipeData();
+    ui->Print("Data wipe %s.\n", success ? "complete" : "failed");
+    return success;
 }
 
 // Return true on success.
@@ -702,52 +690,51 @@
     modified_flash = true;
 
     ui->Print("\n-- Wiping cache...\n");
-    if (erase_volume("/cache") == 0) {
-        ui->Print("Cache wipe complete.\n");
-        return true;
-    } else {
-        ui->Print("Cache wipe failed.\n");
-        return false;
-    }
+    bool success = erase_volume("/cache");
+    ui->Print("Cache wipe %s.\n", success ? "complete" : "failed");
+    return success;
 }
 
 static void choose_recovery_file(Device* device) {
-    unsigned int i;
-    unsigned int n;
-    static const char** title_headers = NULL;
-    char *filename;
-    // "Go back" + LAST_KMSG_FILE + KEEP_LOG_COUNT + terminating NULL entry
-    char* entries[KEEP_LOG_COUNT + 3];
+    // "Back" + KEEP_LOG_COUNT * 2 + terminating nullptr entry
+    char* entries[1 + KEEP_LOG_COUNT * 2 + 1];
     memset(entries, 0, sizeof(entries));
 
-    n = 0;
-    entries[n++] = strdup("Go back");
-
-    // Add kernel kmsg file if available
-    if ((ensure_path_mounted(LAST_KMSG_FILE) == 0) && (access(LAST_KMSG_FILE, R_OK) == 0)) {
-        entries[n++] = strdup(LAST_KMSG_FILE);
-    }
+    unsigned int n = 0;
 
     // Add LAST_LOG_FILE + LAST_LOG_FILE.x
-    for (i = 0; i < KEEP_LOG_COUNT; i++) {
-        char *filename;
-        if (asprintf(&filename, (i==0) ? LAST_LOG_FILE : (LAST_LOG_FILE ".%d"), i) == -1) {
+    // Add LAST_KMSG_FILE + LAST_KMSG_FILE.x
+    for (int i = 0; i < KEEP_LOG_COUNT; i++) {
+        char* log_file;
+        if (asprintf(&log_file, (i == 0) ? "%s" : "%s.%d", LAST_LOG_FILE, i) == -1) {
             // memory allocation failure - return early. Should never happen.
             return;
         }
-        if ((ensure_path_mounted(filename) != 0) || (access(filename, R_OK) == -1)) {
-            free(filename);
-            entries[n++] = NULL;
-            break;
+        if ((ensure_path_mounted(log_file) != 0) || (access(log_file, R_OK) == -1)) {
+            free(log_file);
+        } else {
+            entries[n++] = log_file;
         }
-        entries[n++] = filename;
+
+        char* kmsg_file;
+        if (asprintf(&kmsg_file, (i == 0) ? "%s" : "%s.%d", LAST_KMSG_FILE, i) == -1) {
+            // memory allocation failure - return early. Should never happen.
+            return;
+        }
+        if ((ensure_path_mounted(kmsg_file) != 0) || (access(kmsg_file, R_OK) == -1)) {
+            free(kmsg_file);
+        } else {
+            entries[n++] = kmsg_file;
+        }
     }
 
-    const char* headers[] = { "Select file to view", NULL };
+    entries[n++] = strdup("Back");
+
+    const char* headers[] = { "Select file to view", nullptr };
 
     while (true) {
         int chosen_item = get_menu_selection(headers, entries, 1, 0, device);
-        if (chosen_item == 0) break;
+        if (strcmp(entries[chosen_item], "Back") == 0) break;
 
         // TODO: do we need to redirect? ShowFile could just avoid writing to stdio.
         redirect_stdio("/dev/null");
@@ -755,7 +742,7 @@
         redirect_stdio(TEMPORARY_LOG_FILE);
     }
 
-    for (i = 0; i < (sizeof(entries) / sizeof(*entries)); i++) {
+    for (size_t i = 0; i < (sizeof(entries) / sizeof(*entries)); i++) {
         free(entries[i]);
     }
 }
@@ -1086,6 +1073,13 @@
     } else if (!just_exit) {
         status = INSTALL_NONE;  // No command specified
         ui->SetBackground(RecoveryUI::NO_COMMAND);
+
+        // http://b/17489952
+        // If this is an eng or userdebug build, automatically turn on the
+        // text display if no command is specified.
+        if (is_ro_debuggable()) {
+            ui->ShowText(true);
+        }
     }
 
     if (!sideload_auto_reboot && (status == INSTALL_ERROR || status == INSTALL_CORRUPT)) {
diff --git a/screen_ui.cpp b/screen_ui.cpp
index 5e73d37..ff95915 100644
--- a/screen_ui.cpp
+++ b/screen_ui.cpp
@@ -58,18 +58,19 @@
     progressScopeSize(0),
     progress(0),
     pagesIdentical(false),
-    text(nullptr),
-    text_cols(0),
-    text_rows(0),
-    text_col(0),
-    text_row(0),
-    text_top(0),
+    text_cols_(0),
+    text_rows_(0),
+    text_(nullptr),
+    text_col_(0),
+    text_row_(0),
+    text_top_(0),
     show_text(false),
     show_text_ever(false),
-    menu(nullptr),
+    menu_(nullptr),
     show_menu(false),
     menu_items(0),
     menu_sel(0),
+    file_viewer_text_(nullptr),
     animation_fps(20),
     installing_frames(-1),
     stage(-1),
@@ -255,7 +256,7 @@
             DrawTextLines(&y, HasThreeButtons() ? REGULAR_HELP : LONG_PRESS_HELP);
 
             SetColor(HEADER);
-            DrawTextLines(&y, menu_headers);
+            DrawTextLines(&y, menu_headers_);
 
             SetColor(MENU);
             DrawHorizontalRule(&y);
@@ -267,10 +268,10 @@
                     gr_fill(0, y - 2, gr_fb_width(), y + char_height + 2);
                     // Bold white text for the selected item.
                     SetColor(MENU_SEL_FG);
-                    gr_text(4, y, menu[i], true);
+                    gr_text(4, y, menu_[i], true);
                     SetColor(MENU);
                 } else {
-                    gr_text(4, y, menu[i], false);
+                    gr_text(4, y, menu_[i], false);
                 }
                 y += char_height + 4;
             }
@@ -281,14 +282,14 @@
         // screen, the bottom of the menu, or we've displayed the
         // entire text buffer.
         SetColor(LOG);
-        int row = (text_top+text_rows-1) % text_rows;
+        int row = (text_top_ + text_rows_ - 1) % text_rows_;
         size_t count = 0;
         for (int ty = gr_fb_height() - char_height;
-             ty >= y && count < text_rows;
+             ty >= y && count < text_rows_;
              ty -= char_height, ++count) {
-            gr_text(0, ty, text[row], false);
+            gr_text(0, ty, text_[row], false);
             --row;
-            if (row < 0) row = text_rows-1;
+            if (row < 0) row = text_rows_ - 1;
         }
     }
 }
@@ -391,14 +392,15 @@
     gr_init();
 
     gr_font_size(&char_width, &char_height);
-    text_rows = gr_fb_height() / char_height;
-    text_cols = gr_fb_width() / char_width;
+    text_rows_ = gr_fb_height() / char_height;
+    text_cols_ = gr_fb_width() / char_width;
 
-    text = Alloc2d(text_rows, text_cols + 1);
-    menu = Alloc2d(text_rows, text_cols + 1);
+    text_ = Alloc2d(text_rows_, text_cols_ + 1);
+    file_viewer_text_ = Alloc2d(text_rows_, text_cols_ + 1);
+    menu_ = Alloc2d(text_rows_, text_cols_ + 1);
 
-    text_col = text_row = 0;
-    text_top = 1;
+    text_col_ = text_row_ = 0;
+    text_top_ = 1;
 
     backgroundIcon[NONE] = nullptr;
     LoadBitmapArray("icon_installing", &installing_frames, &installation);
@@ -514,17 +516,17 @@
     fputs(buf, stdout);
 
     pthread_mutex_lock(&updateMutex);
-    if (text_rows > 0 && text_cols > 0) {
+    if (text_rows_ > 0 && text_cols_ > 0) {
         for (const char* ptr = buf; *ptr != '\0'; ++ptr) {
-            if (*ptr == '\n' || text_col >= text_cols) {
-                text[text_row][text_col] = '\0';
-                text_col = 0;
-                text_row = (text_row + 1) % text_rows;
-                if (text_row == text_top) text_top = (text_top + 1) % text_rows;
+            if (*ptr == '\n' || text_col_ >= text_cols_) {
+                text_[text_row_][text_col_] = '\0';
+                text_col_ = 0;
+                text_row_ = (text_row_ + 1) % text_rows_;
+                if (text_row_ == text_top_) text_top_ = (text_top_ + 1) % text_rows_;
             }
-            if (*ptr != '\n') text[text_row][text_col++] = *ptr;
+            if (*ptr != '\n') text_[text_row_][text_col_++] = *ptr;
         }
-        text[text_row][text_col] = '\0';
+        text_[text_row_][text_col_] = '\0';
         update_screen_locked();
     }
     pthread_mutex_unlock(&updateMutex);
@@ -532,21 +534,23 @@
 
 void ScreenRecoveryUI::PutChar(char ch) {
     pthread_mutex_lock(&updateMutex);
-    if (ch != '\n') text[text_row][text_col++] = ch;
-    if (ch == '\n' || text_col >= text_cols) {
-        text_col = 0;
-        ++text_row;
+    if (ch != '\n') text_[text_row_][text_col_++] = ch;
+    if (ch == '\n' || text_col_ >= text_cols_) {
+        text_col_ = 0;
+        ++text_row_;
+
+        if (text_row_ == text_top_) text_top_ = (text_top_ + 1) % text_rows_;
     }
     pthread_mutex_unlock(&updateMutex);
 }
 
 void ScreenRecoveryUI::ClearText() {
     pthread_mutex_lock(&updateMutex);
-    text_col = 0;
-    text_row = 0;
-    text_top = 1;
-    for (size_t i = 0; i < text_rows; ++i) {
-        memset(text[i], 0, text_cols + 1);
+    text_col_ = 0;
+    text_row_ = 0;
+    text_top_ = 1;
+    for (size_t i = 0; i < text_rows_; ++i) {
+        memset(text_[i], 0, text_cols_ + 1);
     }
     pthread_mutex_unlock(&updateMutex);
 }
@@ -590,12 +594,11 @@
 
         int ch = getc(fp);
         if (ch == EOF) {
-            text_row = text_top = text_rows - 2;
+            while (text_row_ < text_rows_ - 1) PutChar('\n');
             show_prompt = true;
         } else {
             PutChar(ch);
-            if (text_col == 0 && text_row >= text_rows - 2) {
-                text_top = text_row;
+            if (text_col_ == 0 && text_row_ >= text_rows_ - 1) {
                 show_prompt = true;
             }
         }
@@ -608,19 +611,34 @@
         Print("  Unable to open %s: %s\n", filename, strerror(errno));
         return;
     }
+
+    char** old_text = text_;
+    size_t old_text_col = text_col_;
+    size_t old_text_row = text_row_;
+    size_t old_text_top = text_top_;
+
+    // Swap in the alternate screen and clear it.
+    text_ = file_viewer_text_;
+    ClearText();
+
     ShowFile(fp);
     fclose(fp);
+
+    text_ = old_text;
+    text_col_ = old_text_col;
+    text_row_ = old_text_row;
+    text_top_ = old_text_top;
 }
 
 void ScreenRecoveryUI::StartMenu(const char* const * headers, const char* const * items,
                                  int initial_selection) {
     pthread_mutex_lock(&updateMutex);
-    if (text_rows > 0 && text_cols > 0) {
-        menu_headers = headers;
+    if (text_rows_ > 0 && text_cols_ > 0) {
+        menu_headers_ = headers;
         size_t i = 0;
-        for (; i < text_rows && items[i] != nullptr; ++i) {
-            strncpy(menu[i], items[i], text_cols-1);
-            menu[i][text_cols-1] = '\0';
+        for (; i < text_rows_ && items[i] != nullptr; ++i) {
+            strncpy(menu_[i], items[i], text_cols_ - 1);
+            menu_[i][text_cols_ - 1] = '\0';
         }
         menu_items = i;
         show_menu = true;
@@ -649,7 +667,7 @@
 
 void ScreenRecoveryUI::EndMenu() {
     pthread_mutex_lock(&updateMutex);
-    if (show_menu && text_rows > 0 && text_cols > 0) {
+    if (show_menu && text_rows_ > 0 && text_cols_ > 0) {
         show_menu = false;
         update_screen_locked();
     }
diff --git a/screen_ui.h b/screen_ui.h
index 46165d9..ea05bf1 100644
--- a/screen_ui.h
+++ b/screen_ui.h
@@ -89,18 +89,23 @@
     // true when both graphics pages are the same (except for the progress bar).
     bool pagesIdentical;
 
+    size_t text_cols_, text_rows_;
+
     // Log text overlay, displayed when a magic key is pressed.
-    char** text;
-    size_t text_cols, text_rows;
-    size_t text_col, text_row, text_top;
+    char** text_;
+    size_t text_col_, text_row_, text_top_;
+
     bool show_text;
     bool show_text_ever;   // has show_text ever been true?
 
-    char** menu;
-    const char* const* menu_headers;
+    char** menu_;
+    const char* const* menu_headers_;
     bool show_menu;
     int menu_items, menu_sel;
 
+    // An alternate text screen, swapped with 'text_' when we're viewing a log file.
+    char** file_viewer_text_;
+
     pthread_t progress_thread_;
 
     int animation_fps;
diff --git a/tools/ota/check-lost+found.c b/tools/ota/check-lost+found.c
index cbf7926..8ce12d3 100644
--- a/tools/ota/check-lost+found.c
+++ b/tools/ota/check-lost+found.c
@@ -78,7 +78,7 @@
                 snprintf(fn, sizeof(fn), "%s/%s", kPartitions[i], "dirty");
                 fd = open(fn, O_WRONLY|O_CREAT, 0444);
                 if (fd >= 0) {  // Don't sweat it if we can't write the file.
-                    write(fd, fn, sizeof(fn));  // write, you know, some data
+                    TEMP_FAILURE_RETRY(write(fd, fn, sizeof(fn)));  // write, you know, some data
                     close(fd);
                     unlink(fn);
                 }
diff --git a/ui.cpp b/ui.cpp
index dca325f..1a0b079 100644
--- a/ui.cpp
+++ b/ui.cpp
@@ -251,7 +251,7 @@
 
     char buf;
     // USB is connected if android_usb state is CONNECTED or CONFIGURED.
-    int connected = (read(fd, &buf, 1) == 1) && (buf == 'C');
+    int connected = (TEMP_FAILURE_RETRY(read(fd, &buf, 1)) == 1) && (buf == 'C');
     if (close(fd) < 0) {
         printf("failed to close /sys/class/android_usb/android0/state: %s\n",
                strerror(errno));
diff --git a/uncrypt/Android.mk b/uncrypt/Android.mk
index 878d275..c7d4d37 100644
--- a/uncrypt/Android.mk
+++ b/uncrypt/Android.mk
@@ -16,10 +16,10 @@
 
 include $(CLEAR_VARS)
 
-LOCAL_SRC_FILES := uncrypt.c
+LOCAL_SRC_FILES := uncrypt.cpp
 
 LOCAL_MODULE := uncrypt
 
-LOCAL_STATIC_LIBRARIES := libfs_mgr liblog libcutils
+LOCAL_STATIC_LIBRARIES := libbase liblog libfs_mgr libcutils
 
 include $(BUILD_EXECUTABLE)
diff --git a/uncrypt/uncrypt.c b/uncrypt/uncrypt.cpp
similarity index 63%
rename from uncrypt/uncrypt.c
rename to uncrypt/uncrypt.cpp
index aa75210..1db3013 100644
--- a/uncrypt/uncrypt.c
+++ b/uncrypt/uncrypt.cpp
@@ -40,37 +40,42 @@
 // file data to use as an update package.
 
 #include <errno.h>
+#include <fcntl.h>
+#include <linux/fs.h>
+#include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <stdarg.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <linux/fs.h>
 #include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
 
-#define LOG_TAG "uncrypt"
-#include <log/log.h>
+#include <base/file.h>
+#include <base/strings.h>
 #include <cutils/properties.h>
 #include <fs_mgr.h>
+#define LOG_TAG "uncrypt"
+#include <log/log.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 const std::string cache_block_map = "/cache/recovery/block.map";
+static const std::string status_file = "/cache/recovery/uncrypt_status";
+static const std::string uncrypt_file = "/cache/recovery/uncrypt_file";
 
 static struct fstab* fstab = NULL;
 
-static int write_at_offset(unsigned char* buffer, size_t size,
-                           int wfd, off64_t offset)
-{
-    lseek64(wfd, offset, SEEK_SET);
+static int write_at_offset(unsigned char* buffer, size_t size, int wfd, off64_t offset) {
+    if (TEMP_FAILURE_RETRY(lseek64(wfd, offset, SEEK_SET)) == -1) {
+        ALOGE("error seeking to offset %lld: %s\n", offset, strerror(errno));
+        return -1;
+    }
     size_t written = 0;
     while (written < size) {
-        ssize_t wrote = write(wfd, buffer + written, size - written);
-        if (wrote < 0) {
-            ALOGE("error writing offset %lld: %s\n", offset, strerror(errno));
+        ssize_t wrote = TEMP_FAILURE_RETRY(write(wfd, buffer + written, size - written));
+        if (wrote == -1) {
+            ALOGE("error writing offset %lld: %s\n", (offset + written), strerror(errno));
             return -1;
         }
         written += wrote;
@@ -78,8 +83,7 @@
     return 0;
 }
 
-void add_block_to_ranges(int** ranges, int* range_alloc, int* range_used, int new_block)
-{
+static 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.)
@@ -98,7 +102,7 @@
         // 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));
+            *ranges = reinterpret_cast<int*>(realloc(*ranges, *range_alloc * 2 * sizeof(int)));
         }
 
         ++*range_used;
@@ -107,8 +111,7 @@
     }
 }
 
-static struct fstab* read_fstab()
-{
+static struct fstab* read_fstab() {
     fstab = NULL;
 
     // The fstab path is always "/fstab.${ro.hardware}".
@@ -127,26 +130,26 @@
     return fstab;
 }
 
-const char* find_block_device(const char* path, int* encryptable, int* encrypted)
-{
+static const char* find_block_device(const char* path, bool* encryptable, bool* encrypted) {
     // 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) {
+    for (int i = 0; i < fstab->num_entries; ++i) {
         struct fstab_rec* v = &fstab->recs[i];
-        if (!v->mount_point) continue;
+        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;
+            *encrypted = false;
+            *encryptable = false;
             if (fs_mgr_is_encryptable(v)) {
-                *encryptable = 1;
+                *encryptable = true;
                 char buffer[PROPERTY_VALUE_MAX+1];
                 if (property_get("ro.crypto.state", buffer, "") &&
                     strcmp(buffer, "encrypted") == 0) {
-                    *encrypted = 1;
+                    *encrypted = true;
                 }
             }
             return v->blk_device;
@@ -156,56 +159,37 @@
     return NULL;
 }
 
-char* parse_recovery_command_file()
+// Parse uncrypt_file to find the update package name.
+static bool find_uncrypt_package(std::string& package_name)
 {
-    char* fn = NULL;
-    int count = 0;
-    char temp[1024];
+    if (!android::base::ReadFileToString(uncrypt_file, &package_name)) {
+        ALOGE("failed to open \"%s\": %s\n", uncrypt_file.c_str(), strerror(errno));
+        return false;
+    }
 
-    FILE* f = fopen(RECOVERY_COMMAND_FILE, "r");
-    if (f == NULL) {
-        return NULL;
-    }
-    int fd = open(RECOVERY_COMMAND_FILE_TMP, O_WRONLY | O_CREAT | O_SYNC, S_IRUSR | S_IWUSR);
-    if (fd < 0) {
-        ALOGE("failed to open %s\n", RECOVERY_COMMAND_FILE_TMP);
-        return NULL;
-    }
-    FILE* fo = fdopen(fd, "w");
+    // Remove the trailing '\n' if present.
+    package_name = android::base::Trim(package_name);
 
-    while (fgets(temp, sizeof(temp), f)) {
-        printf("read: %s", temp);
-        if (strncmp(temp, "--update_package=/data/", strlen("--update_package=/data/")) == 0) {
-            fn = strdup(temp + strlen("--update_package="));
-            strcpy(temp, "--update_package=@" CACHE_BLOCK_MAP "\n");
-        }
-        fputs(temp, fo);
-    }
-    fclose(f);
-    fsync(fd);
-    fclose(fo);
-
-    if (fn) {
-        char* newline = strchr(fn, '\n');
-        if (newline) *newline = 0;
-    }
-    return fn;
+    return true;
 }
 
-int produce_block_map(const char* path, const char* map_file, const char* blk_dev,
-                      int encrypted)
-{
-    struct stat sb;
-    int ret;
-
+static int produce_block_map(const char* path, const char* map_file, const char* blk_dev,
+                             bool encrypted, int status_fd) {
     int mapfd = open(map_file, O_WRONLY | O_CREAT | O_SYNC, S_IRUSR | S_IWUSR);
-    if (mapfd < 0) {
+    if (mapfd == -1) {
         ALOGE("failed to open %s\n", map_file);
         return -1;
     }
     FILE* mapf = fdopen(mapfd, "w");
 
-    ret = stat(path, &sb);
+    // Make sure we can write to the status_file.
+    if (!android::base::WriteStringToFd("0\n", status_fd)) {
+        ALOGE("failed to update \"%s\"\n", status_file.c_str());
+        return -1;
+    }
+
+    struct stat sb;
+    int ret = stat(path, &sb);
     if (ret != 0) {
         ALOGE("failed to stat %s\n", path);
         return -1;
@@ -216,20 +200,18 @@
     int blocks = ((sb.st_size-1) / sb.st_blksize) + 1;
     ALOGI("  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));
+    int* ranges = reinterpret_cast<int*>(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);
+        for (size_t i = 0; i < WINDOW_SIZE; ++i) {
+            buffers[i] = reinterpret_cast<unsigned char*>(malloc(sb.st_blksize));
         }
     }
     int head_block = 0;
@@ -241,7 +223,6 @@
         ALOGE("failed to open fd for reading: %s\n", strerror(errno));
         return -1;
     }
-    fsync(fd);
 
     int wfd = -1;
     if (encrypted) {
@@ -252,7 +233,15 @@
         }
     }
 
+    int last_progress = 0;
     while (pos < sb.st_size) {
+        // Update the status file, progress must be between [0, 99].
+        int progress = static_cast<int>(100 * (double(pos) / double(sb.st_size)));
+        if (progress > last_progress) {
+          last_progress = progress;
+          android::base::WriteStringToFd(std::to_string(progress) + "\n", status_fd);
+        }
+
         if ((tail+1) % WINDOW_SIZE == head) {
             // write out head buffer
             int block = head_block;
@@ -263,7 +252,8 @@
             }
             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) {
+                if (write_at_offset(buffers[head], sb.st_blksize, wfd,
+                        (off64_t)sb.st_blksize * block) != 0) {
                     return -1;
                 }
             }
@@ -275,8 +265,9 @@
         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) {
+                ssize_t this_read =
+                        TEMP_FAILURE_RETRY(read(fd, buffers[tail] + so_far, sb.st_blksize - so_far));
+                if (this_read == -1) {
                     ALOGE("failed to read: %s\n", strerror(errno));
                     return -1;
                 }
@@ -302,7 +293,8 @@
         }
         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) {
+            if (write_at_offset(buffers[head], sb.st_blksize, wfd,
+                    (off64_t)sb.st_blksize * block) != 0) {
                 return -1;
             }
         }
@@ -311,25 +303,30 @@
     }
 
     fprintf(mapf, "%d\n", range_used);
-    for (i = 0; i < range_used; ++i) {
+    for (int i = 0; i < range_used; ++i) {
         fprintf(mapf, "%d %d\n", ranges[i*2], ranges[i*2+1]);
     }
 
-    fsync(mapfd);
+    if (fsync(mapfd) == -1) {
+        ALOGE("failed to fsync \"%s\": %s\n", map_file, strerror(errno));
+        return -1;
+    }
     fclose(mapf);
     close(fd);
     if (encrypted) {
-        fsync(wfd);
+        if (fsync(wfd) == -1) {
+            ALOGE("failed to fsync \"%s\": %s\n", blk_dev, strerror(errno));
+            return -1;
+        }
         close(wfd);
     }
 
     return 0;
 }
 
-void wipe_misc() {
+static void wipe_misc() {
     ALOGI("removing old commands from misc");
-    int i;
-    for (i = 0; i < fstab->num_entries; ++i) {
+    for (int i = 0; i < fstab->num_entries; ++i) {
         struct fstab_rec* v = &fstab->recs[i];
         if (!v->mount_point) continue;
         if (strcmp(v->mount_point, "/misc") == 0) {
@@ -340,72 +337,49 @@
             size_t written = 0;
             size_t size = sizeof(zeroes);
             while (written < size) {
-                ssize_t w = write(fd, zeroes, size-written);
-                if (w < 0 && errno != EINTR) {
+                ssize_t w = TEMP_FAILURE_RETRY(write(fd, zeroes, size-written));
+                if (w == -1) {
                     ALOGE("zero write failed: %s\n", strerror(errno));
                     return;
                 } else {
                     written += w;
                 }
             }
-            fsync(fd);
+            if (fsync(fd) == -1) {
+                ALOGE("failed to fsync \"%s\": %s\n", v->blk_device, strerror(errno));
+                close(fd);
+                return;
+            }
             close(fd);
         }
     }
 }
 
-void reboot_to_recovery() {
+static void reboot_to_recovery() {
     ALOGI("rebooting to recovery");
     property_set("sys.powerctl", "reboot,recovery");
     sleep(10);
     ALOGE("reboot didn't succeed?");
 }
 
-int main(int argc, char** argv)
-{
-    const char* input_path;
-    const char* map_file;
-    int do_reboot = 1;
+int uncrypt(const char* input_path, const char* map_file, int status_fd) {
 
-    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.
-            ALOGI("no recovery command file or no update package arg");
-            reboot_to_recovery();
-            return 1;
-        }
-        map_file = CACHE_BLOCK_MAP;
-    }
-
-    ALOGI("update package is %s", input_path);
+    ALOGI("update package is \"%s\"", input_path);
 
     // 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) {
-        ALOGE("failed to convert %s to absolute path: %s", input_path, strerror(errno));
+        ALOGE("failed to convert \"%s\" to absolute path: %s", input_path, strerror(errno));
         return 1;
     }
 
-    int encryptable;
-    int encrypted;
     if (read_fstab() == NULL) {
         return 1;
     }
+
+    bool encryptable;
+    bool encrypted;
     const char* blk_dev = find_block_device(path, &encryptable, &encrypted);
     if (blk_dev == NULL) {
         ALOGE("failed to find block device for %s", path);
@@ -425,18 +399,67 @@
     // On /data we want to convert the file to a block map so that we
     // can read the package without mounting the partition.  On /cache
     // and /sdcard we leave the file alone.
-    if (strncmp(path, "/data/", 6) != 0) {
-        // path does not start with "/data/"; leave it alone.
-        unlink(RECOVERY_COMMAND_FILE_TMP);
-    } else {
+    if (strncmp(path, "/data/", 6) == 0) {
         ALOGI("writing block map %s", map_file);
-        if (produce_block_map(path, map_file, blk_dev, encrypted) != 0) {
+        if (produce_block_map(path, map_file, blk_dev, encrypted, status_fd) != 0) {
             return 1;
         }
     }
 
-    wipe_misc();
-    rename(RECOVERY_COMMAND_FILE_TMP, RECOVERY_COMMAND_FILE);
-    if (do_reboot) reboot_to_recovery();
+    return 0;
+}
+
+int main(int argc, char** argv) {
+    const char* input_path;
+    const char* map_file;
+
+    if (argc != 3 && argc != 1 && (argc == 2 && strcmp(argv[1], "--reboot") != 0)) {
+        fprintf(stderr, "usage: %s [--reboot] [<transform_path> <map_file>]\n", argv[0]);
+        return 2;
+    }
+
+    // When uncrypt is started with "--reboot", it wipes misc and reboots.
+    // Otherwise it uncrypts the package and writes the block map.
+    if (argc == 2) {
+        if (read_fstab() == NULL) {
+            return 1;
+        }
+        wipe_misc();
+        reboot_to_recovery();
+    } else {
+        // The pipe has been created by the system server.
+        int status_fd = open(status_file.c_str(), O_WRONLY | O_CREAT | O_SYNC, S_IRUSR | S_IWUSR);
+        if (status_fd == -1) {
+            ALOGE("failed to open pipe \"%s\": %s\n", status_file.c_str(), strerror(errno));
+            return 1;
+        }
+
+        if (argc == 3) {
+            // when command-line args are given this binary is being used
+            // for debugging.
+            input_path = argv[1];
+            map_file = argv[2];
+        } else {
+            std::string package;
+            if (!find_uncrypt_package(package)) {
+                android::base::WriteStringToFd("-1\n", status_fd);
+                close(status_fd);
+                return 1;
+            }
+            input_path = package.c_str();
+            map_file = cache_block_map.c_str();
+        }
+
+        int status = uncrypt(input_path, map_file, status_fd);
+        if (status != 0) {
+            android::base::WriteStringToFd("-1\n", status_fd);
+            close(status_fd);
+            return 1;
+        }
+
+        android::base::WriteStringToFd("100\n", status_fd);
+        close(status_fd);
+    }
+
     return 0;
 }
diff --git a/updater/blockimg.c b/updater/blockimg.c
index d5344f9..b006d10 100644
--- a/updater/blockimg.c
+++ b/updater/blockimg.c
@@ -19,6 +19,7 @@
 #include <dirent.h>
 #include <fcntl.h>
 #include <inttypes.h>
+#include <libgen.h>
 #include <pthread.h>
 #include <stdarg.h>
 #include <stdio.h>
@@ -101,7 +102,7 @@
             r2_0 = r2->pos[j * 2];
             r2_1 = r2->pos[j * 2 + 1];
 
-            if (!(r2_0 > r1_1 || r1_0 > r2_1)) {
+            if (!(r2_0 >= r1_1 || r1_0 >= r2_1)) {
                 return 1;
             }
         }
@@ -113,13 +114,12 @@
 static int read_all(int fd, uint8_t* data, size_t size) {
     size_t so_far = 0;
     while (so_far < size) {
-        ssize_t r = read(fd, data+so_far, size-so_far);
-        if (r < 0 && errno != EINTR) {
+        ssize_t r = TEMP_FAILURE_RETRY(read(fd, data+so_far, size-so_far));
+        if (r == -1) {
             fprintf(stderr, "read failed: %s\n", strerror(errno));
             return -1;
-        } else {
-            so_far += r;
         }
+        so_far += r;
     }
     return 0;
 }
@@ -127,13 +127,12 @@
 static int write_all(int fd, const uint8_t* data, size_t size) {
     size_t written = 0;
     while (written < size) {
-        ssize_t w = write(fd, data+written, size-written);
-        if (w < 0 && errno != EINTR) {
+        ssize_t w = TEMP_FAILURE_RETRY(write(fd, data+written, size-written));
+        if (w == -1) {
             fprintf(stderr, "write failed: %s\n", strerror(errno));
             return -1;
-        } else {
-            written += w;
         }
+        written += w;
     }
 
     if (fsync(fd) == -1) {
@@ -144,19 +143,13 @@
     return 0;
 }
 
-static int check_lseek(int fd, off64_t offset, int whence) {
-    while (true) {
-        off64_t ret = lseek64(fd, offset, whence);
-        if (ret < 0) {
-            if (errno != EINTR) {
-                fprintf(stderr, "lseek64 failed: %s\n", strerror(errno));
-                return -1;
-            }
-        } else {
-            break;
-        }
+static bool check_lseek(int fd, off64_t offset, int whence) {
+    off64_t rc = TEMP_FAILURE_RETRY(lseek64(fd, offset, whence));
+    if (rc == -1) {
+        fprintf(stderr, "lseek64 failed: %s\n", strerror(errno));
+        return false;
     }
-    return 0;
+    return true;
 }
 
 static void allocate(size_t size, uint8_t** buffer, size_t* buffer_alloc) {
@@ -213,8 +206,8 @@
                 rss->p_remain = (rss->tgt->pos[rss->p_block * 2 + 1] -
                                  rss->tgt->pos[rss->p_block * 2]) * BLOCKSIZE;
 
-                if (check_lseek(rss->fd, (off64_t)rss->tgt->pos[rss->p_block*2] * BLOCKSIZE,
-                        SEEK_SET) == -1) {
+                if (!check_lseek(rss->fd, (off64_t)rss->tgt->pos[rss->p_block*2] * BLOCKSIZE,
+                                 SEEK_SET)) {
                     break;
                 }
             } else {
@@ -306,7 +299,7 @@
     }
 
     for (i = 0; i < src->count; ++i) {
-        if (check_lseek(fd, (off64_t) src->pos[i * 2] * BLOCKSIZE, SEEK_SET) == -1) {
+        if (!check_lseek(fd, (off64_t) src->pos[i * 2] * BLOCKSIZE, SEEK_SET)) {
             return -1;
         }
 
@@ -332,7 +325,7 @@
     }
 
     for (i = 0; i < tgt->count; ++i) {
-        if (check_lseek(fd, (off64_t) tgt->pos[i * 2] * BLOCKSIZE, SEEK_SET) == -1) {
+        if (!check_lseek(fd, (off64_t) tgt->pos[i * 2] * BLOCKSIZE, SEEK_SET)) {
             return -1;
         }
 
@@ -624,7 +617,7 @@
 
 lsout:
     if (fd != -1) {
-        TEMP_FAILURE_RETRY(close(fd));
+        close(fd);
     }
 
     if (fn) {
@@ -640,6 +633,7 @@
     char *cn = NULL;
     int fd = -1;
     int rc = -1;
+    int dfd = -1;
     int res;
     struct stat st;
 
@@ -698,11 +692,29 @@
         goto wsout;
     }
 
+    const char* dname;
+    dname = dirname(cn);
+    dfd = TEMP_FAILURE_RETRY(open(dname, O_RDONLY | O_DIRECTORY));
+
+    if (dfd == -1) {
+        fprintf(stderr, "failed to open \"%s\" failed: %s\n", dname, strerror(errno));
+        goto wsout;
+    }
+
+    if (fsync(dfd) == -1) {
+        fprintf(stderr, "fsync \"%s\" failed: %s\n", dname, strerror(errno));
+        goto wsout;
+    }
+
     rc = 0;
 
 wsout:
     if (fd != -1) {
-        TEMP_FAILURE_RETRY(close(fd));
+        close(fd);
+    }
+
+    if (dfd != -1) {
+        close(dfd);
     }
 
     if (fn) {
@@ -1217,7 +1229,7 @@
 
     if (params->canwrite) {
         for (i = 0; i < tgt->count; ++i) {
-            if (check_lseek(params->fd, (off64_t) tgt->pos[i * 2] * BLOCKSIZE, SEEK_SET) == -1) {
+            if (!check_lseek(params->fd, (off64_t) tgt->pos[i * 2] * BLOCKSIZE, SEEK_SET)) {
                 goto pczout;
             }
 
@@ -1271,7 +1283,7 @@
         rss.p_block = 0;
         rss.p_remain = (tgt->pos[1] - tgt->pos[0]) * BLOCKSIZE;
 
-        if (check_lseek(params->fd, (off64_t) tgt->pos[0] * BLOCKSIZE, SEEK_SET) == -1) {
+        if (!check_lseek(params->fd, (off64_t) tgt->pos[0] * BLOCKSIZE, SEEK_SET)) {
             goto pcnout;
         }
 
@@ -1367,7 +1379,7 @@
             rss.p_block = 0;
             rss.p_remain = (tgt->pos[1] - tgt->pos[0]) * BLOCKSIZE;
 
-            if (check_lseek(params->fd, (off64_t) tgt->pos[0] * BLOCKSIZE, SEEK_SET) == -1) {
+            if (!check_lseek(params->fd, (off64_t) tgt->pos[0] * BLOCKSIZE, SEEK_SET)) {
                 goto pcdout;
             }
 
@@ -1432,7 +1444,6 @@
 
     if (!S_ISBLK(st.st_mode)) {
         fprintf(stderr, "not a block device; skipping erase\n");
-        rc = 0;
         goto pceout;
     }
 
@@ -1456,7 +1467,7 @@
 
             if (ioctl(params->fd, BLKDISCARD, &blocks) == -1) {
                 fprintf(stderr, "BLKDISCARD ioctl failed: %s\n", strerror(errno));
-                // Continue anyway, nothing we can do
+                goto pceout;
             }
         }
     }
@@ -1740,7 +1751,7 @@
         if (fsync(params.fd) == -1) {
             fprintf(stderr, "fsync failed: %s\n", strerror(errno));
         }
-        TEMP_FAILURE_RETRY(close(params.fd));
+        close(params.fd);
     }
 
     if (logcmd) {
@@ -1906,7 +1917,7 @@
 
     int i, j;
     for (i = 0; i < rs->count; ++i) {
-        if (check_lseek(fd, (off64_t)rs->pos[i*2] * BLOCKSIZE, SEEK_SET) == -1) {
+        if (!check_lseek(fd, (off64_t)rs->pos[i*2] * BLOCKSIZE, SEEK_SET)) {
             ErrorAbort(state, "failed to seek %s: %s", blockdev_filename->data,
                 strerror(errno));
             goto done;