merge in lmp-mr1-release history after reset to fe8563f4f2a6efe1eb00bb41a5037c88917becfc
diff --git a/default_device.cpp b/default_device.cpp
index a25f05f..97806ac 100644
--- a/default_device.cpp
+++ b/default_device.cpp
@@ -31,6 +31,7 @@
                                "wipe cache partition",
                                "reboot to bootloader",
                                "power down",
+                               "view recovery logs",
                                NULL };
 
 class DefaultDevice : public Device {
@@ -69,6 +70,7 @@
           case 3: return WIPE_CACHE;
           case 4: return REBOOT_BOOTLOADER;
           case 5: return SHUTDOWN;
+          case 6: return READ_RECOVERY_LASTLOG;
           default: return NO_ACTION;
         }
     }
diff --git a/device.h b/device.h
index 57ec3fc..8ff4ec0 100644
--- a/device.h
+++ b/device.h
@@ -68,7 +68,7 @@
     enum BuiltinAction { NO_ACTION, REBOOT, APPLY_EXT,
                          APPLY_CACHE,   // APPLY_CACHE is deprecated; has no effect
                          APPLY_ADB_SIDELOAD, WIPE_DATA, WIPE_CACHE,
-                         REBOOT_BOOTLOADER, SHUTDOWN };
+                         REBOOT_BOOTLOADER, SHUTDOWN, READ_RECOVERY_LASTLOG };
 
     // Perform a recovery action selected from the menu.
     // 'menu_position' will be the item number of the selected menu
diff --git a/recovery.cpp b/recovery.cpp
index 7f17b16..e1a2a96 100644
--- a/recovery.cpp
+++ b/recovery.cpp
@@ -77,6 +77,8 @@
 static const char *TEMPORARY_LOG_FILE = "/tmp/recovery.log";
 static const char *TEMPORARY_INSTALL_FILE = "/tmp/last_install";
 
+#define KEEP_LOG_COUNT 10
+
 RecoveryUI* ui = NULL;
 char* locale = NULL;
 char recovery_version[PROPERTY_VALUE_MAX+1];
@@ -161,6 +163,12 @@
     return fp;
 }
 
+static void redirect_stdio(const char* filename) {
+    // If these fail, there's not really anywhere to complain...
+    freopen(filename, "a", stdout); setbuf(stdout, NULL);
+    freopen(filename, "a", stderr); setbuf(stderr, NULL);
+}
+
 // close a file, log an error if the error indicator is set
 static void
 check_and_fclose(FILE *fp, const char *name) {
@@ -665,6 +673,65 @@
     ui->Print("Data wipe complete.\n");
 }
 
+static void file_to_ui(const char* fn) {
+    FILE *fp = fopen_path(fn, "re");
+    if (fp == NULL) {
+        ui->Print("  Unable to open %s: %s\n", fn, strerror(errno));
+        return;
+    }
+    char line[1024];
+    int ct = 0;
+    redirect_stdio("/dev/null");
+    while(fgets(line, sizeof(line), fp) != NULL) {
+        ui->Print("%s", line);
+        ct++;
+        if (ct % 30 == 0) {
+            // give the user time to glance at the entries
+            ui->WaitKey();
+        }
+    }
+    redirect_stdio(TEMPORARY_LOG_FILE);
+    fclose(fp);
+}
+
+static void choose_recovery_file(Device* device) {
+    int i;
+    static const char** title_headers = NULL;
+    char *filename;
+    const char* headers[] = { "Select file to view",
+                              "",
+                              NULL };
+    char* entries[KEEP_LOG_COUNT + 2];
+    memset(entries, 0, sizeof(entries));
+
+    for (i = 0; i < KEEP_LOG_COUNT; i++) {
+        char *filename;
+        if (asprintf(&filename, (i==0) ? LAST_LOG_FILE : (LAST_LOG_FILE ".%d"), 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[i+1] = NULL;
+            break;
+        }
+        entries[i+1] = filename;
+    }
+
+    entries[0] = strdup("Go back");
+    title_headers = prepend_title((const char**)headers);
+
+    while(1) {
+        int chosen_item = get_menu_selection(title_headers, entries, 1, 0, device);
+        if (chosen_item == 0) break;
+        file_to_ui(entries[chosen_item]);
+    }
+
+    for (i = 0; i < KEEP_LOG_COUNT + 1; i++) {
+        free(entries[i]);
+    }
+}
+
 // 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.
@@ -760,6 +827,10 @@
                 ui->Print("\nAPPLY_CACHE is deprecated.\n");
                 break;
 
+            case Device::READ_RECOVERY_LASTLOG:
+                choose_recovery_file(device);
+                break;
+
             case Device::APPLY_ADB_SIDELOAD:
                 status = apply_from_adb(ui, &wipe_cache, TEMPORARY_INSTALL_FILE);
                 if (status >= 0) {
@@ -824,9 +895,7 @@
 main(int argc, char **argv) {
     time_t start = time(NULL);
 
-    // If these fail, there's not really anywhere to complain...
-    freopen(TEMPORARY_LOG_FILE, "a", stdout); setbuf(stdout, NULL);
-    freopen(TEMPORARY_LOG_FILE, "a", stderr); setbuf(stderr, NULL);
+    redirect_stdio(TEMPORARY_LOG_FILE);
 
     // If this binary is started with the single argument "--adbd",
     // instead of being the normal recovery binary, it turns into kind
@@ -844,7 +913,7 @@
 
     load_volume_table();
     ensure_path_mounted(LAST_LOG_FILE);
-    rotate_last_logs(10);
+    rotate_last_logs(KEEP_LOG_COUNT);
     get_args(&argc, &argv);
 
     const char *send_intent = NULL;
diff --git a/updater/install.c b/updater/install.c
index dad0d08..ff7de47 100644
--- a/updater/install.c
+++ b/updater/install.c
@@ -52,6 +52,26 @@
 #include "wipe.h"
 #endif
 
+void uiPrint(State* state, char* buffer) {
+    char* line = strtok(buffer, "\n");
+    UpdaterInfo* ui = (UpdaterInfo*)(state->cookie);
+    while (line) {
+        fprintf(ui->cmd_pipe, "ui_print %s\n", line);
+        line = strtok(NULL, "\n");
+    }
+    fprintf(ui->cmd_pipe, "ui_print\n");
+}
+
+__attribute__((__format__(printf, 2, 3))) __nonnull((2))
+void uiPrintf(State* state, const char* format, ...) {
+    char error_msg[1024];
+    va_list ap;
+    va_start(ap, format);
+    vsnprintf(error_msg, sizeof(error_msg), format, ap);
+    va_end(ap);
+    uiPrint(state, error_msg);
+}
+
 // Take a sha-1 digest and return it as a newly-allocated hex string.
 char* PrintSha1(const uint8_t* digest) {
     char* buffer = malloc(SHA_DIGEST_SIZE*2 + 1);
@@ -71,16 +91,27 @@
 //    fs_type="ext4"   partition_type="EMMC"    location=device
 Value* MountFn(const char* name, State* state, int argc, Expr* argv[]) {
     char* result = NULL;
-    if (argc != 4) {
-        return ErrorAbort(state, "%s() expects 4 args, got %d", name, argc);
+    if (argc != 4 && argc != 5) {
+        return ErrorAbort(state, "%s() expects 4-5 args, got %d", name, argc);
     }
     char* fs_type;
     char* partition_type;
     char* location;
     char* mount_point;
-    if (ReadArgs(state, argv, 4, &fs_type, &partition_type,
+    char* mount_options;
+    bool has_mount_options;
+    if (argc == 5) {
+        has_mount_options = true;
+        if (ReadArgs(state, argv, 5, &fs_type, &partition_type,
+                 &location, &mount_point, &mount_options) < 0) {
+            return NULL;
+        }
+    } else {
+        has_mount_options = false;
+        if (ReadArgs(state, argv, 4, &fs_type, &partition_type,
                  &location, &mount_point) < 0) {
-        return NULL;
+            return NULL;
+        }
     }
 
     if (strlen(fs_type) == 0) {
@@ -120,13 +151,13 @@
         const MtdPartition* mtd;
         mtd = mtd_find_partition_by_name(location);
         if (mtd == NULL) {
-            printf("%s: no mtd partition named \"%s\"",
+            uiPrintf(state, "%s: no mtd partition named \"%s\"",
                     name, location);
             result = strdup("");
             goto done;
         }
         if (mtd_mount_partition(mtd, mount_point, fs_type, 0 /* rw */) != 0) {
-            printf("mtd mount of %s failed: %s\n",
+            uiPrintf(state, "mtd mount of %s failed: %s\n",
                     location, strerror(errno));
             result = strdup("");
             goto done;
@@ -134,8 +165,9 @@
         result = mount_point;
     } else {
         if (mount(location, mount_point, fs_type,
-                  MS_NOATIME | MS_NODEV | MS_NODIRATIME, "") < 0) {
-            printf("%s: failed to mount %s at %s: %s\n",
+                  MS_NOATIME | MS_NODEV | MS_NODIRATIME,
+                  has_mount_options ? mount_options : "") < 0) {
+            uiPrintf(state, "%s: failed to mount %s at %s: %s\n",
                     name, location, mount_point, strerror(errno));
             result = strdup("");
         } else {
@@ -148,6 +180,7 @@
     free(partition_type);
     free(location);
     if (result != mount_point) free(mount_point);
+    if (has_mount_options) free(mount_options);
     return StringValue(result);
 }
 
@@ -198,10 +231,14 @@
     scan_mounted_volumes();
     const MountedVolume* vol = find_mounted_volume_by_mount_point(mount_point);
     if (vol == NULL) {
-        printf("unmount of %s failed; no such volume\n", mount_point);
+        uiPrintf(state, "unmount of %s failed; no such volume\n", mount_point);
         result = strdup("");
     } else {
-        unmount_mounted_volume(vol);
+        int ret = unmount_mounted_volume(vol);
+        if (ret != 0) {
+           uiPrintf(state, "unmount of %s failed (%d): %s\n",
+                    mount_point, ret, strerror(errno));
+        }
         result = mount_point;
     }
 
@@ -358,6 +395,9 @@
     if (make_parents(dst_name) != 0) {
         ErrorAbort(state, "Creating parent of %s failed, error %s",
           dst_name, strerror(errno));
+    } else if (access(dst_name, F_OK) == 0 && access(src_name, F_OK) != 0) {
+        // File was already moved
+        result = dst_name;
     } else if (rename(src_name, dst_name) != 0) {
         ErrorAbort(state, "Rename of %s to %s failed, error %s",
           src_name, dst_name, strerror(errno));
@@ -630,7 +670,7 @@
     uint64_t capabilities;
 };
 
-static struct perm_parsed_args ParsePermArgs(int argc, char** args) {
+static struct perm_parsed_args ParsePermArgs(State * state, int argc, char** args) {
     int i;
     struct perm_parsed_args parsed;
     int bad = 0;
@@ -645,7 +685,7 @@
                 parsed.uid = uid;
                 parsed.has_uid = true;
             } else {
-                printf("ParsePermArgs: invalid UID \"%s\"\n", args[i + 1]);
+                uiPrintf(state, "ParsePermArgs: invalid UID \"%s\"\n", args[i + 1]);
                 bad++;
             }
             continue;
@@ -656,7 +696,7 @@
                 parsed.gid = gid;
                 parsed.has_gid = true;
             } else {
-                printf("ParsePermArgs: invalid GID \"%s\"\n", args[i + 1]);
+                uiPrintf(state, "ParsePermArgs: invalid GID \"%s\"\n", args[i + 1]);
                 bad++;
             }
             continue;
@@ -667,7 +707,7 @@
                 parsed.mode = mode;
                 parsed.has_mode = true;
             } else {
-                printf("ParsePermArgs: invalid mode \"%s\"\n", args[i + 1]);
+                uiPrintf(state, "ParsePermArgs: invalid mode \"%s\"\n", args[i + 1]);
                 bad++;
             }
             continue;
@@ -678,7 +718,7 @@
                 parsed.dmode = mode;
                 parsed.has_dmode = true;
             } else {
-                printf("ParsePermArgs: invalid dmode \"%s\"\n", args[i + 1]);
+                uiPrintf(state, "ParsePermArgs: invalid dmode \"%s\"\n", args[i + 1]);
                 bad++;
             }
             continue;
@@ -689,7 +729,7 @@
                 parsed.fmode = mode;
                 parsed.has_fmode = true;
             } else {
-                printf("ParsePermArgs: invalid fmode \"%s\"\n", args[i + 1]);
+                uiPrintf(state, "ParsePermArgs: invalid fmode \"%s\"\n", args[i + 1]);
                 bad++;
             }
             continue;
@@ -700,7 +740,7 @@
                 parsed.capabilities = capabilities;
                 parsed.has_capabilities = true;
             } else {
-                printf("ParsePermArgs: invalid capabilities \"%s\"\n", args[i + 1]);
+                uiPrintf(state, "ParsePermArgs: invalid capabilities \"%s\"\n", args[i + 1]);
                 bad++;
             }
             continue;
@@ -710,7 +750,7 @@
                 parsed.selabel = args[i+1];
                 parsed.has_selabel = true;
             } else {
-                printf("ParsePermArgs: invalid selabel \"%s\"\n", args[i + 1]);
+                uiPrintf(state, "ParsePermArgs: invalid selabel \"%s\"\n", args[i + 1]);
                 bad++;
             }
             continue;
@@ -727,71 +767,71 @@
 }
 
 static int ApplyParsedPerms(
+        State * state,
         const char* filename,
         const struct stat *statptr,
         struct perm_parsed_args parsed)
 {
     int bad = 0;
 
+    if (parsed.has_selabel) {
+        if (lsetfilecon(filename, parsed.selabel) != 0) {
+            uiPrintf(state, "ApplyParsedPerms: lsetfilecon of %s to %s failed: %s\n",
+                    filename, parsed.selabel, strerror(errno));
+            bad++;
+        }
+    }
+
     /* ignore symlinks */
     if (S_ISLNK(statptr->st_mode)) {
-        return 0;
+        return bad;
     }
 
     if (parsed.has_uid) {
         if (chown(filename, parsed.uid, -1) < 0) {
-            printf("ApplyParsedPerms: chown of %s to %d failed: %s\n",
-                   filename, parsed.uid, strerror(errno));
+            uiPrintf(state, "ApplyParsedPerms: chown of %s to %d failed: %s\n",
+                    filename, parsed.uid, strerror(errno));
             bad++;
         }
     }
 
     if (parsed.has_gid) {
         if (chown(filename, -1, parsed.gid) < 0) {
-            printf("ApplyParsedPerms: chgrp of %s to %d failed: %s\n",
-                   filename, parsed.gid, strerror(errno));
+            uiPrintf(state, "ApplyParsedPerms: chgrp of %s to %d failed: %s\n",
+                    filename, parsed.gid, strerror(errno));
             bad++;
         }
     }
 
     if (parsed.has_mode) {
         if (chmod(filename, parsed.mode) < 0) {
-            printf("ApplyParsedPerms: chmod of %s to %d failed: %s\n",
-                   filename, parsed.mode, strerror(errno));
+            uiPrintf(state, "ApplyParsedPerms: chmod of %s to %d failed: %s\n",
+                    filename, parsed.mode, strerror(errno));
             bad++;
         }
     }
 
     if (parsed.has_dmode && S_ISDIR(statptr->st_mode)) {
         if (chmod(filename, parsed.dmode) < 0) {
-            printf("ApplyParsedPerms: chmod of %s to %d failed: %s\n",
-                   filename, parsed.dmode, strerror(errno));
+            uiPrintf(state, "ApplyParsedPerms: chmod of %s to %d failed: %s\n",
+                    filename, parsed.dmode, strerror(errno));
             bad++;
         }
     }
 
     if (parsed.has_fmode && S_ISREG(statptr->st_mode)) {
         if (chmod(filename, parsed.fmode) < 0) {
-            printf("ApplyParsedPerms: chmod of %s to %d failed: %s\n",
+            uiPrintf(state, "ApplyParsedPerms: chmod of %s to %d failed: %s\n",
                    filename, parsed.fmode, strerror(errno));
             bad++;
         }
     }
 
-    if (parsed.has_selabel) {
-        // TODO: Don't silently ignore ENOTSUP
-        if (lsetfilecon(filename, parsed.selabel) && (errno != ENOTSUP)) {
-            printf("ApplyParsedPerms: lsetfilecon of %s to %s failed: %s\n",
-                   filename, parsed.selabel, strerror(errno));
-            bad++;
-        }
-    }
-
     if (parsed.has_capabilities && S_ISREG(statptr->st_mode)) {
         if (parsed.capabilities == 0) {
             if ((removexattr(filename, XATTR_NAME_CAPS) == -1) && (errno != ENODATA)) {
                 // Report failure unless it's ENODATA (attribute not set)
-                printf("ApplyParsedPerms: removexattr of %s to %" PRIx64 " failed: %s\n",
+                uiPrintf(state, "ApplyParsedPerms: removexattr of %s to %" PRIx64 " failed: %s\n",
                        filename, parsed.capabilities, strerror(errno));
                 bad++;
             }
@@ -804,8 +844,8 @@
             cap_data.data[1].permitted = (uint32_t) (parsed.capabilities >> 32);
             cap_data.data[1].inheritable = 0;
             if (setxattr(filename, XATTR_NAME_CAPS, &cap_data, sizeof(cap_data), 0) < 0) {
-                printf("ApplyParsedPerms: setcap of %s to %" PRIx64 " failed: %s\n",
-                       filename, parsed.capabilities, strerror(errno));
+                uiPrintf(state, "ApplyParsedPerms: setcap of %s to %" PRIx64 " failed: %s\n",
+                        filename, parsed.capabilities, strerror(errno));
                 bad++;
             }
         }
@@ -817,10 +857,11 @@
 // nftw doesn't allow us to pass along context, so we need to use
 // global variables.  *sigh*
 static struct perm_parsed_args recursive_parsed_args;
+static State* recursive_state;
 
 static int do_SetMetadataRecursive(const char* filename, const struct stat *statptr,
         int fileflags, struct FTW *pfwt) {
-    return ApplyParsedPerms(filename, statptr, recursive_parsed_args);
+    return ApplyParsedPerms(recursive_state, filename, statptr, recursive_parsed_args);
 }
 
 static Value* SetMetadataFn(const char* name, State* state, int argc, Expr* argv[]) {
@@ -845,14 +886,16 @@
         goto done;
     }
 
-    struct perm_parsed_args parsed = ParsePermArgs(argc, args);
+    struct perm_parsed_args parsed = ParsePermArgs(state, argc, args);
 
     if (recursive) {
         recursive_parsed_args = parsed;
+        recursive_state = state;
         bad += nftw(args[0], do_SetMetadataRecursive, 30, FTW_CHDIR | FTW_DEPTH | FTW_PHYS);
         memset(&recursive_parsed_args, 0, sizeof(recursive_parsed_args));
+        recursive_state = NULL;
     } else {
-        bad += ApplyParsedPerms(args[0], &sb, parsed);
+        bad += ApplyParsedPerms(state, args[0], &sb, parsed);
     }
 
 done:
@@ -1224,15 +1267,7 @@
     }
     free(args);
     buffer[size] = '\0';
-
-    char* line = strtok(buffer, "\n");
-    while (line) {
-        fprintf(((UpdaterInfo*)(state->cookie))->cmd_pipe,
-                "ui_print %s\n", line);
-        line = strtok(NULL, "\n");
-    }
-    fprintf(((UpdaterInfo*)(state->cookie))->cmd_pipe, "ui_print\n");
-
+    uiPrint(state, buffer);
     return StringValue(buffer);
 }