update.zip somewhat working now...
diff --git a/Android.mk b/Android.mk
index 6a64d86..66c30cc 100644
--- a/Android.mk
+++ b/Android.mk
@@ -5,9 +5,12 @@
include $(CLEAR_VARS)
commands_recovery_local_path := $(LOCAL_PATH)
+# LOCAL_CPP_EXTENSION := .c
LOCAL_SRC_FILES := \
+ extendedcommands.c \
legacy.c \
+ commands.c \
recovery.c \
bootloader.c \
firmware.c \
diff --git a/amend/register.c b/amend/register.c
index 167dd32..e45a973 100644
--- a/amend/register.c
+++ b/amend/register.c
@@ -125,6 +125,19 @@
return -1;
}
+/* delete <srcdir> <dstdir>
+ */
+static int
+cmd_delete(const char *name, void *cookie, int argc, const char *argv[],
+ PermissionRequestList *permissions)
+{
+ UNUSED(name);
+ UNUSED(cookie);
+ CHECK_WORDS();
+//xxx
+ return -1;
+}
+
/* mark <resource> dirty|clean
*/
static int
@@ -165,6 +178,9 @@
ret = registerCommand("copy_dir", CMD_ARGS_WORDS, cmd_copy_dir, NULL);
if (ret < 0) return ret;
+ ret = registerCommand("delete", CMD_ARGS_WORDS, cmd_delete, NULL);
+ if (ret < 0) return ret;
+
ret = registerCommand("format", CMD_ARGS_WORDS, cmd_format, NULL);
if (ret < 0) return ret;
diff --git a/commands.c b/commands.c
new file mode 100644
index 0000000..e6fb9be
--- /dev/null
+++ b/commands.c
@@ -0,0 +1,1154 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef NDEBUG
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <unistd.h>
+
+#include "amend/commands.h"
+#include "commands.h"
+#include "common.h"
+#include "cutils/misc.h"
+#include "cutils/properties.h"
+#include "firmware.h"
+#include "minzip/DirUtil.h"
+#include "minzip/Zip.h"
+#include "roots.h"
+
+#include "extendedcommands.h"
+
+static int gDidShowProgress = 0;
+
+#define UNUSED(p) ((void)(p))
+
+#define CHECK_BOOL() \
+ do { \
+ assert(argv == NULL); \
+ if (argv != NULL) return -1; \
+ assert(argc == true || argc == false); \
+ if (argc != true && argc != false) return -1; \
+ } while (false)
+
+#define CHECK_WORDS() \
+ do { \
+ assert(argc >= 0); \
+ if (argc < 0) return -1; \
+ assert(argc == 0 || argv != NULL); \
+ if (argc != 0 && argv == NULL) return -1; \
+ if (permissions != NULL) { \
+ int CW_I_; \
+ for (CW_I_ = 0; CW_I_ < argc; CW_I_++) { \
+ assert(argv[CW_I_] != NULL); \
+ if (argv[CW_I_] == NULL) return -1; \
+ } \
+ } \
+ } while (false)
+
+#define CHECK_FN() \
+ do { \
+ CHECK_WORDS(); \
+ if (permissions != NULL) { \
+ assert(result == NULL); \
+ if (result != NULL) return -1; \
+ } else { \
+ assert(result != NULL); \
+ if (result == NULL) return -1; \
+ } \
+ } while (false)
+
+#define NO_PERMS(perms) \
+ do { \
+ PermissionRequestList *NP_PRL_ = (perms); \
+ if (NP_PRL_ != NULL) { \
+ int NP_RET_ = addPermissionRequestToList(NP_PRL_, \
+ "", false, PERM_NONE); \
+ if (NP_RET_ < 0) { \
+ /* Returns from the calling function. \
+ */ \
+ return NP_RET_; \
+ } \
+ } \
+ } while (false)
+
+/*
+ * Command definitions
+ */
+
+/* assert <boolexpr>
+ */
+static int
+cmd_assert(const char *name, void *cookie, int argc, const char *argv[],
+ PermissionRequestList *permissions)
+{
+ UNUSED(name);
+ UNUSED(cookie);
+ CHECK_BOOL();
+ NO_PERMS(permissions);
+
+ if (!script_assert_enabled) {
+ return 0;
+ }
+
+ /* If our argument is false, return non-zero (failure)
+ * If our argument is true, return zero (success)
+ */
+ if (argc) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+/* format <root>
+ */
+static int
+cmd_format(const char *name, void *cookie, int argc, const char *argv[],
+ PermissionRequestList *permissions)
+{
+ UNUSED(name);
+ UNUSED(cookie);
+ CHECK_WORDS();
+
+ if (argc != 1) {
+ LOGE("Command %s requires exactly one argument\n", name);
+ return 1;
+ }
+ const char *root = argv[0];
+ ui_print("Formatting %s...\n", root);
+
+ int ret = format_root_device(root);
+ if (ret != 0) {
+ LOGE("Can't format %s\n", root);
+ return 1;
+ }
+
+ return 0;
+}
+
+/* delete <file1> [<fileN> ...]
+ * delete_recursive <file-or-dir1> [<file-or-dirN> ...]
+ *
+ * Like "rm -f", will try to delete every named file/dir, even if
+ * earlier ones fail. Recursive deletes that fail halfway through
+ * give up early.
+ */
+static int
+cmd_delete(const char *name, void *cookie, int argc, const char *argv[],
+ PermissionRequestList *permissions)
+{
+ UNUSED(cookie);
+ CHECK_WORDS();
+ int nerr = 0;
+ bool recurse;
+
+ if (argc < 1) {
+ LOGE("Command %s requires at least one argument\n", name);
+ return 1;
+ }
+
+ recurse = (strcmp(name, "delete_recursive") == 0);
+ ui_print("Deleting files...\n");
+//xxx permissions
+
+ int i;
+ for (i = 0; i < argc; i++) {
+ const char *root_path = argv[i];
+ char pathbuf[PATH_MAX];
+ const char *path;
+
+ /* This guarantees that all paths use "SYSTEM:"-style roots;
+ * plain paths won't make it through translate_root_path().
+ */
+ path = translate_root_path(root_path, pathbuf, sizeof(pathbuf));
+ if (path != NULL) {
+ int ret = ensure_root_path_mounted(root_path);
+ if (ret < 0) {
+ LOGW("Can't mount volume to delete \"%s\"\n", root_path);
+ nerr++;
+ continue;
+ }
+ if (recurse) {
+ ret = dirUnlinkHierarchy(path);
+ } else {
+ ret = unlink(path);
+ }
+ if (ret != 0 && errno != ENOENT) {
+ LOGW("Can't delete %s\n(%s)\n", path, strerror(errno));
+ nerr++;
+ }
+ } else {
+ nerr++;
+ }
+ }
+//TODO: add a way to fail if a delete didn't work
+
+ return 0;
+}
+
+typedef struct {
+ int num_done;
+ int num_total;
+} ExtractContext;
+
+static void extract_count_cb(const char *fn, void *cookie)
+{
+ ++((ExtractContext*) cookie)->num_total;
+}
+
+static void extract_cb(const char *fn, void *cookie)
+{
+ // minzip writes the filename to the log, so we don't need to
+ ExtractContext *ctx = (ExtractContext*) cookie;
+ ui_set_progress((float) ++ctx->num_done / ctx->num_total);
+}
+
+/* copy_dir <src-dir> <dst-dir> [<timestamp>]
+ *
+ * The contents of <src-dir> will become the contents of <dst-dir>.
+ * The original contents of <dst-dir> are preserved unless something
+ * in <src-dir> overwrote them.
+ *
+ * e.g., for "copy_dir PKG:system SYSTEM:", the file "PKG:system/a"
+ * would be copied to "SYSTEM:a".
+ *
+ * The specified timestamp (in decimal seconds since 1970) will be used,
+ * or a fixed default timestamp will be supplied otherwise.
+ */
+static int
+cmd_copy_dir(const char *name, void *cookie, int argc, const char *argv[],
+ PermissionRequestList *permissions)
+{
+ UNUSED(name);
+ UNUSED(cookie);
+ CHECK_WORDS();
+//xxx permissions
+
+ // To create a consistent system image, never use the clock for timestamps.
+ struct utimbuf timestamp = { 1217592000, 1217592000 }; // 8/1/2008 default
+ if (argc == 3) {
+ char *end;
+ time_t value = strtoul(argv[2], &end, 0);
+ if (value == 0 || end[0] != '\0') {
+ LOGE("Command %s: invalid timestamp \"%s\"\n", name, argv[2]);
+ return 1;
+ } else if (value < timestamp.modtime) {
+ LOGE("Command %s: timestamp \"%s\" too early\n", name, argv[2]);
+ return 1;
+ }
+ timestamp.modtime = timestamp.actime = value;
+ } else if (argc != 2) {
+ LOGE("Command %s requires exactly two arguments\n", name);
+ return 1;
+ }
+
+ // Use 40% of the progress bar (80% post-verification) by default
+ ui_print("Copying files...\n");
+ if (!gDidShowProgress) ui_show_progress(DEFAULT_FILES_PROGRESS_FRACTION, 0);
+
+ /* Mount the destination volume if it isn't already.
+ */
+ const char *dst_root_path = argv[1];
+ int ret = ensure_root_path_mounted(dst_root_path);
+ if (ret < 0) {
+ LOGE("Can't mount %s\n", dst_root_path);
+ return 1;
+ }
+
+ /* Get the real target path.
+ */
+ char dstpathbuf[PATH_MAX];
+ const char *dst_path;
+ dst_path = translate_root_path(dst_root_path,
+ dstpathbuf, sizeof(dstpathbuf));
+ if (dst_path == NULL) {
+ LOGE("Command %s: bad destination path \"%s\"\n", name, dst_root_path);
+ return 1;
+ }
+
+ /* Try to copy the directory. The source may be inside a package.
+ */
+ const char *src_root_path = argv[0];
+ char srcpathbuf[PATH_MAX];
+ const char *src_path;
+ if (is_package_root_path(src_root_path)) {
+ const ZipArchive *package;
+ src_path = translate_package_root_path(src_root_path,
+ srcpathbuf, sizeof(srcpathbuf), &package);
+ if (src_path == NULL) {
+ LOGE("Command %s: bad source path \"%s\"\n", name, src_root_path);
+ return 1;
+ }
+
+ /* Extract the files. Set MZ_EXTRACT_FILES_ONLY, because only files
+ * are validated by the signature. Do a dry run first to count how
+ * many there are (and find some errors early).
+ */
+ ExtractContext ctx;
+ ctx.num_done = 0;
+ ctx.num_total = 0;
+
+ if (!mzExtractRecursive(package, src_path, dst_path,
+ MZ_EXTRACT_FILES_ONLY | MZ_EXTRACT_DRY_RUN,
+ ×tamp, extract_count_cb, (void *) &ctx) ||
+ !mzExtractRecursive(package, src_path, dst_path,
+ MZ_EXTRACT_FILES_ONLY,
+ ×tamp, extract_cb, (void *) &ctx)) {
+ LOGW("Command %s: couldn't extract \"%s\" to \"%s\"\n",
+ name, src_root_path, dst_root_path);
+ return 1;
+ }
+ } else {
+ LOGE("Command %s: non-package source path \"%s\" not yet supported\n",
+ name, src_root_path);
+//xxx mount the src volume
+//xxx
+ return 255;
+ }
+
+ return 0;
+}
+
+/* run_program <program-file> [<args> ...]
+ *
+ * Run an external program included in the update package.
+ */
+static int
+cmd_run_program(const char *name, void *cookie, int argc, const char *argv[],
+ PermissionRequestList *permissions)
+{
+ UNUSED(cookie);
+ CHECK_WORDS();
+
+ if (argc < 1) {
+ LOGE("Command %s requires at least one argument\n", name);
+ return 1;
+ }
+
+ // Copy the program file to temporary storage.
+ if (!is_package_root_path(argv[0])) {
+ LOGE("Command %s: non-package program file \"%s\" not supported\n",
+ name, argv[0]);
+ return 1;
+ }
+
+ char path[PATH_MAX];
+ const ZipArchive *package;
+ if (!translate_package_root_path(argv[0], path, sizeof(path), &package)) {
+ LOGE("Command %s: bad source path \"%s\"\n", name, argv[0]);
+ return 1;
+ }
+
+ const ZipEntry *entry = mzFindZipEntry(package, path);
+ if (entry == NULL) {
+ LOGE("Can't find %s\n", path);
+ return 1;
+ }
+
+ static const char *binary = "/tmp/run_program_binary";
+ unlink(binary); // just to be sure
+ int fd = creat(binary, 0755);
+ if (fd < 0) {
+ LOGE("Can't make %s\n", binary);
+ return 1;
+ }
+ bool ok = mzExtractZipEntryToFile(package, entry, fd);
+ close(fd);
+
+ if (!ok) {
+ LOGE("Can't copy %s\n", path);
+ return 1;
+ }
+
+ // Create a copy of argv to NULL-terminate it, as execv requires
+ char **args = (char **) malloc(sizeof(char*) * (argc + 1));
+ memcpy(args, argv, sizeof(char*) * argc);
+ args[argc] = NULL;
+
+ pid_t pid = fork();
+ if (pid == 0) {
+ execv(binary, args);
+ fprintf(stderr, "E:Can't run %s\n(%s)\n", binary, strerror(errno));
+ _exit(-1);
+ }
+
+ int status;
+ waitpid(pid, &status, 0);
+ if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
+ return 0;
+ } else {
+ LOGE("Error in %s\n(Status %d)\n", path, status);
+ return 1;
+ }
+}
+
+/* set_perm <uid> <gid> <mode> <path> [... <pathN>]
+ * set_perm_recursive <uid> <gid> <dir-mode> <file-mode> <path> [... <pathN>]
+ *
+ * Like "chmod", "chown" and "chgrp" all in one, set ownership and permissions
+ * of single files or entire directory trees. Any error causes failure.
+ * User, group, and modes must all be integer values (hex or octal OK).
+ */
+static int
+cmd_set_perm(const char *name, void *cookie, int argc, const char *argv[],
+ PermissionRequestList *permissions)
+{
+ UNUSED(cookie);
+ CHECK_WORDS();
+ bool recurse = !strcmp(name, "set_perm_recursive");
+
+ int min_args = 4 + (recurse ? 1 : 0);
+ if (argc < min_args) {
+ LOGE("Command %s requires at least %d args\n", name, min_args);
+ return 1;
+ }
+
+ // All the arguments except the path(s) are numeric.
+ int i, n[min_args - 1];
+ for (i = 0; i < min_args - 1; ++i) {
+ char *end;
+ n[i] = strtoul(argv[i], &end, 0);
+ if (end[0] != '\0' || argv[i][0] == '\0') {
+ LOGE("Command %s: invalid argument \"%s\"\n", name, argv[i]);
+ return 1;
+ }
+ }
+
+ for (i = min_args - 1; i < min_args; ++i) {
+ char path[PATH_MAX];
+ if (translate_root_path(argv[i], path, sizeof(path)) == NULL) {
+ LOGE("Command %s: bad path \"%s\"\n", name, argv[i]);
+ return 1;
+ }
+
+ if (ensure_root_path_mounted(argv[i])) {
+ LOGE("Can't mount %s\n", argv[i]);
+ return 1;
+ }
+
+ if (recurse
+ ? dirSetHierarchyPermissions(path, n[0], n[1], n[2], n[3])
+ : (chown(path, n[0], n[1]) || chmod(path, n[2]))) {
+ LOGE("Can't chown/mod %s\n(%s)\n", path, strerror(errno));
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/* show_progress <fraction> <duration>
+ *
+ * Use <fraction> of the on-screen progress meter for the next operation,
+ * automatically advancing the meter over <duration> seconds (or more rapidly
+ * if the actual rate of progress can be determined).
+ */
+static int
+cmd_show_progress(const char *name, void *cookie, int argc, const char *argv[],
+ PermissionRequestList *permissions)
+{
+ UNUSED(cookie);
+ CHECK_WORDS();
+
+ if (argc != 2) {
+ LOGE("Command %s requires exactly two arguments\n", name);
+ return 1;
+ }
+
+ char *end;
+ double fraction = strtod(argv[0], &end);
+ if (end[0] != '\0' || argv[0][0] == '\0' || fraction < 0 || fraction > 1) {
+ LOGE("Command %s: invalid fraction \"%s\"\n", name, argv[0]);
+ return 1;
+ }
+
+ int duration = strtoul(argv[1], &end, 0);
+ if (end[0] != '\0' || argv[0][0] == '\0') {
+ LOGE("Command %s: invalid duration \"%s\"\n", name, argv[1]);
+ return 1;
+ }
+
+ // Half of the progress bar is taken by verification,
+ // so everything that happens during installation is scaled.
+ ui_show_progress(fraction * (1 - VERIFICATION_PROGRESS_FRACTION), duration);
+ gDidShowProgress = 1;
+ return 0;
+}
+
+/* symlink <link-target> <link-path>
+ *
+ * Create a symlink, like "ln -s". The link path must not exist already.
+ * Note that <link-path> is in root:path format, but <link-target> is
+ * for the target filesystem (and may be relative).
+ */
+static int
+cmd_symlink(const char *name, void *cookie, int argc, const char *argv[],
+ PermissionRequestList *permissions)
+{
+ UNUSED(cookie);
+ CHECK_WORDS();
+
+ if (argc != 2) {
+ LOGE("Command %s requires exactly two arguments\n", name);
+ return 1;
+ }
+
+ char path[PATH_MAX];
+ if (translate_root_path(argv[1], path, sizeof(path)) == NULL) {
+ LOGE("Command %s: bad path \"%s\"\n", name, argv[1]);
+ return 1;
+ }
+
+ if (ensure_root_path_mounted(argv[1])) {
+ LOGE("Can't mount %s\n", argv[1]);
+ return 1;
+ }
+
+ if (symlink(argv[0], path)) {
+ LOGE("Can't symlink %s\n", path);
+ return 1;
+ }
+
+ return 0;
+}
+
+struct FirmwareContext {
+ size_t total_bytes, done_bytes;
+ char *data;
+};
+
+static bool firmware_fn(const unsigned char *data, int data_len, void *cookie)
+{
+ struct FirmwareContext *context = (struct FirmwareContext*) cookie;
+ if (context->done_bytes + data_len > context->total_bytes) {
+ LOGE("Data overrun in firmware\n");
+ return false; // Should not happen, but let's be safe.
+ }
+
+ memcpy(context->data + context->done_bytes, data, data_len);
+ context->done_bytes += data_len;
+ ui_set_progress(context->done_bytes * 1.0 / context->total_bytes);
+ return true;
+}
+
+/* write_radio_image <src-image>
+ * write_hboot_image <src-image>
+ * Doesn't actually take effect until the rest of installation finishes.
+ */
+static int
+cmd_write_firmware_image(const char *name, void *cookie,
+ int argc, const char *argv[], PermissionRequestList *permissions)
+{
+ UNUSED(cookie);
+ CHECK_WORDS();
+
+ if (argc != 1) {
+ LOGE("Command %s requires exactly one argument\n", name);
+ return 1;
+ }
+
+ const char *type;
+ if (!strcmp(name, "write_radio_image")) {
+ type = "radio";
+ } else if (!strcmp(name, "write_hboot_image")) {
+ type = "hboot";
+ } else {
+ LOGE("Unknown firmware update command %s\n", name);
+ return 1;
+ }
+
+ if (!is_package_root_path(argv[0])) {
+ LOGE("Command %s: non-package image file \"%s\" not supported\n",
+ name, argv[0]);
+ return 1;
+ }
+
+ ui_print("Extracting %s image...\n", type);
+ char path[PATH_MAX];
+ const ZipArchive *package;
+ if (!translate_package_root_path(argv[0], path, sizeof(path), &package)) {
+ LOGE("Command %s: bad source path \"%s\"\n", name, argv[0]);
+ return 1;
+ }
+
+ const ZipEntry *entry = mzFindZipEntry(package, path);
+ if (entry == NULL) {
+ LOGE("Can't find %s\n", path);
+ return 1;
+ }
+
+ // Load the update image into RAM.
+ struct FirmwareContext context;
+ context.total_bytes = mzGetZipEntryUncompLen(entry);
+ context.done_bytes = 0;
+ context.data = malloc(context.total_bytes);
+ if (context.data == NULL) {
+ LOGE("Can't allocate %d bytes for %s\n", context.total_bytes, argv[0]);
+ return 1;
+ }
+
+ if (!mzProcessZipEntryContents(package, entry, firmware_fn, &context) ||
+ context.done_bytes != context.total_bytes) {
+ LOGE("Can't read %s\n", argv[0]);
+ free(context.data);
+ return 1;
+ }
+
+ if (remember_firmware_update(type, context.data, context.total_bytes)) {
+ LOGE("Can't store %s image\n", type);
+ free(context.data);
+ return 1;
+ }
+
+ return 0;
+}
+
+static bool write_raw_image_process_fn(
+ const unsigned char *data,
+ int data_len, void *ctx)
+{
+ int r = mtd_write_data((MtdWriteContext*)ctx, (const char *)data, data_len);
+ if (r == data_len) return true;
+ LOGE("%s\n", strerror(errno));
+ return false;
+}
+
+/* write_raw_image <src-image> <dest-root>
+ */
+static int
+cmd_write_raw_image(const char *name, void *cookie,
+ int argc, const char *argv[], PermissionRequestList *permissions)
+{
+ UNUSED(cookie);
+ CHECK_WORDS();
+//xxx permissions
+
+ if (argc != 2) {
+ LOGE("Command %s requires exactly two arguments\n", name);
+ return 1;
+ }
+
+ // Use 10% of the progress bar (20% post-verification) by default
+ const char *src_root_path = argv[0];
+ const char *dst_root_path = argv[1];
+ ui_print("Writing %s...\n", dst_root_path);
+ if (!gDidShowProgress) ui_show_progress(DEFAULT_IMAGE_PROGRESS_FRACTION, 0);
+
+ /* Find the source image, which is probably in a package.
+ */
+ if (!is_package_root_path(src_root_path)) {
+ LOGE("Command %s: non-package source path \"%s\" not yet supported\n",
+ name, src_root_path);
+ return 255;
+ }
+
+ /* Get the package.
+ */
+ char srcpathbuf[PATH_MAX];
+ const char *src_path;
+ const ZipArchive *package;
+ src_path = translate_package_root_path(src_root_path,
+ srcpathbuf, sizeof(srcpathbuf), &package);
+ if (src_path == NULL) {
+ LOGE("Command %s: bad source path \"%s\"\n", name, src_root_path);
+ return 1;
+ }
+
+ /* Get the entry.
+ */
+ const ZipEntry *entry = mzFindZipEntry(package, src_path);
+ if (entry == NULL) {
+ LOGE("Missing file %s\n", src_path);
+ return 1;
+ }
+
+ /* Unmount the destination root if it isn't already.
+ */
+ int ret = ensure_root_path_unmounted(dst_root_path);
+ if (ret < 0) {
+ LOGE("Can't unmount %s\n", dst_root_path);
+ return 1;
+ }
+
+ /* Open the partition for writing.
+ */
+ const MtdPartition *partition = get_root_mtd_partition(dst_root_path);
+ if (partition == NULL) {
+ LOGE("Can't find %s\n", dst_root_path);
+ return 1;
+ }
+ MtdWriteContext *context = mtd_write_partition(partition);
+ if (context == NULL) {
+ LOGE("Can't open %s\n", dst_root_path);
+ return 1;
+ }
+
+ /* Extract and write the image.
+ */
+ bool ok = mzProcessZipEntryContents(package, entry,
+ write_raw_image_process_fn, context);
+ if (!ok) {
+ LOGE("Error writing %s\n", dst_root_path);
+ mtd_write_close(context);
+ return 1;
+ }
+
+ if (mtd_erase_blocks(context, -1) == (off_t) -1) {
+ LOGE("Error finishing %s\n", dst_root_path);
+ mtd_write_close(context);
+ return -1;
+ }
+
+ if (mtd_write_close(context)) {
+ LOGE("Error closing %s\n", dst_root_path);
+ return -1;
+ }
+ return 0;
+}
+
+/* mark <resource> dirty|clean
+ */
+static int
+cmd_mark(const char *name, void *cookie, int argc, const char *argv[],
+ PermissionRequestList *permissions)
+{
+ UNUSED(name);
+ UNUSED(cookie);
+ CHECK_WORDS();
+//xxx when marking, save the top-level hash at the mark point
+// so we can retry on failure. Otherwise the hashes won't match,
+// or someone could intentionally dirty the FS to force a downgrade
+//xxx
+ return -1;
+}
+
+/* done
+ */
+static int
+cmd_done(const char *name, void *cookie, int argc, const char *argv[],
+ PermissionRequestList *permissions)
+{
+ UNUSED(name);
+ UNUSED(cookie);
+ CHECK_WORDS();
+//xxx
+ return -1;
+}
+
+
+/*
+ * Function definitions
+ */
+
+/* compatible_with(<version>)
+ *
+ * Returns "true" if this version of the script parser and command
+ * set supports the named version.
+ */
+static int
+fn_compatible_with(const char *name, void *cookie, int argc, const char *argv[],
+ char **result, size_t *resultLen,
+ PermissionRequestList *permissions)
+{
+ UNUSED(name);
+ UNUSED(cookie);
+ CHECK_FN();
+ NO_PERMS(permissions);
+
+ if (argc != 1) {
+ fprintf(stderr, "%s: wrong number of arguments (%d)\n",
+ name, argc);
+ return 1;
+ }
+
+ if (!strcmp(argv[0], "0.1") || !strcmp(argv[0], "0.2")) {
+ *result = strdup("true");
+ } else {
+ *result = strdup("");
+ }
+ if (resultLen != NULL) {
+ *resultLen = strlen(*result);
+ }
+ return 0;
+}
+
+/* update_forced()
+ *
+ * Returns "true" if some system setting has determined that
+ * the update should happen no matter what.
+ */
+static int
+fn_update_forced(const char *name, void *cookie, int argc, const char *argv[],
+ char **result, size_t *resultLen,
+ PermissionRequestList *permissions)
+{
+ UNUSED(name);
+ UNUSED(cookie);
+ CHECK_FN();
+ NO_PERMS(permissions);
+
+ if (argc != 0) {
+ fprintf(stderr, "%s: wrong number of arguments (%d)\n",
+ name, argc);
+ return 1;
+ }
+
+ //xxx check some global or property
+ bool force = true;
+ if (force) {
+ *result = strdup("true");
+ } else {
+ *result = strdup("");
+ }
+ if (resultLen != NULL) {
+ *resultLen = strlen(*result);
+ }
+
+ return 0;
+}
+
+/* get_mark(<resource>)
+ *
+ * Returns the current mark associated with the provided resource.
+ */
+static int
+fn_get_mark(const char *name, void *cookie, int argc, const char *argv[],
+ char **result, size_t *resultLen,
+ PermissionRequestList *permissions)
+{
+ UNUSED(name);
+ UNUSED(cookie);
+ CHECK_FN();
+ NO_PERMS(permissions);
+
+ if (argc != 1) {
+ fprintf(stderr, "%s: wrong number of arguments (%d)\n",
+ name, argc);
+ return 1;
+ }
+
+ //xxx look up the value
+ *result = strdup("");
+ if (resultLen != NULL) {
+ *resultLen = strlen(*result);
+ }
+
+ return 0;
+}
+
+/* hash_dir(<path-to-directory>)
+ */
+static int
+fn_hash_dir(const char *name, void *cookie, int argc, const char *argv[],
+ char **result, size_t *resultLen,
+ PermissionRequestList *permissions)
+{
+ int ret = -1;
+
+ UNUSED(name);
+ UNUSED(cookie);
+ CHECK_FN();
+
+ const char *dir;
+ if (argc != 1) {
+ fprintf(stderr, "%s: wrong number of arguments (%d)\n",
+ name, argc);
+ return 1;
+ } else {
+ dir = argv[0];
+ }
+
+ if (permissions != NULL) {
+ if (dir == NULL) {
+ /* The argument is the result of another function.
+ * Assume the worst case, where the function returns
+ * the root.
+ */
+ dir = "/";
+ }
+ ret = addPermissionRequestToList(permissions, dir, true, PERM_READ);
+ } else {
+//xxx build and return the string
+ *result = strdup("hashvalue");
+ if (resultLen != NULL) {
+ *resultLen = strlen(*result);
+ }
+ ret = 0;
+ }
+
+ return ret;
+}
+
+/* matches(<str>, <str1> [, <strN>...])
+ * If <str> matches (strcmp) any of <str1>...<strN>, returns <str>,
+ * otherwise returns "".
+ *
+ * E.g., assert matches(hash_dir("/path"), "hash1", "hash2")
+ */
+static int
+fn_matches(const char *name, void *cookie, int argc, const char *argv[],
+ char **result, size_t *resultLen,
+ PermissionRequestList *permissions)
+{
+ UNUSED(name);
+ UNUSED(cookie);
+ CHECK_FN();
+ NO_PERMS(permissions);
+
+ if (argc < 2) {
+ fprintf(stderr, "%s: not enough arguments (%d < 2)\n",
+ name, argc);
+ return 1;
+ }
+
+ int i;
+ for (i = 1; i < argc; i++) {
+ if (strcmp(argv[0], argv[i]) == 0) {
+ *result = strdup(argv[0]);
+ if (resultLen != NULL) {
+ *resultLen = strlen(*result);
+ }
+ return 0;
+ }
+ }
+
+ *result = strdup("");
+ if (resultLen != NULL) {
+ *resultLen = 1;
+ }
+ return 0;
+}
+
+/* concat(<str>, <str1> [, <strN>...])
+ * Returns the concatenation of all strings.
+ */
+static int
+fn_concat(const char *name, void *cookie, int argc, const char *argv[],
+ char **result, size_t *resultLen,
+ PermissionRequestList *permissions)
+{
+ UNUSED(name);
+ UNUSED(cookie);
+ CHECK_FN();
+ NO_PERMS(permissions);
+
+ size_t totalLen = 0;
+ int i;
+ for (i = 0; i < argc; i++) {
+ totalLen += strlen(argv[i]);
+ }
+
+ char *s = (char *)malloc(totalLen + 1);
+ if (s == NULL) {
+ return -1;
+ }
+ s[totalLen] = '\0';
+ for (i = 0; i < argc; i++) {
+ //TODO: keep track of the end to avoid walking the string each time
+ strcat(s, argv[i]);
+ }
+ *result = s;
+ if (resultLen != NULL) {
+ *resultLen = strlen(s);
+ }
+
+ return 0;
+}
+
+/* getprop(<property>)
+ * Returns the named Android system property value, or "" if not set.
+ */
+static int
+fn_getprop(const char *name, void *cookie, int argc, const char *argv[],
+ char **result, size_t *resultLen,
+ PermissionRequestList *permissions)
+{
+ UNUSED(cookie);
+ CHECK_FN();
+ NO_PERMS(permissions);
+
+ if (argc != 1) {
+ LOGE("Command %s requires exactly one argument\n", name);
+ return 1;
+ }
+
+ char value[PROPERTY_VALUE_MAX];
+ property_get(argv[0], value, "");
+
+ *result = strdup(value);
+ if (resultLen != NULL) {
+ *resultLen = strlen(*result);
+ }
+
+ return 0;
+}
+
+/* file_contains(<filename>, <substring>)
+ * Returns "true" if the file exists and contains the specified substring.
+ */
+static int
+fn_file_contains(const char *name, void *cookie, int argc, const char *argv[],
+ char **result, size_t *resultLen,
+ PermissionRequestList *permissions)
+{
+ UNUSED(cookie);
+ CHECK_FN();
+ NO_PERMS(permissions);
+
+ if (argc != 2) {
+ LOGE("Command %s requires exactly two arguments\n", name);
+ return 1;
+ }
+
+ char pathbuf[PATH_MAX];
+ const char *root_path = argv[0];
+ const char *path = translate_root_path(root_path, pathbuf, sizeof(pathbuf));
+ if (path == NULL) {
+ LOGE("Command %s: bad path \"%s\"\n", name, root_path);
+ return 1;
+ }
+
+ if (ensure_root_path_mounted(root_path)) {
+ LOGE("Can't mount %s\n", root_path);
+ return 1;
+ }
+
+ const char *needle = argv[1];
+ char *haystack = (char*) load_file(path, NULL);
+ if (haystack == NULL) {
+ LOGI("%s: Can't read \"%s\" (%s)\n", name, path, strerror(errno));
+ *result = ""; /* File not found is not an error. */
+ } else if (strstr(haystack, needle) == NULL) {
+ LOGI("%s: Can't find \"%s\" in \"%s\"\n", name, needle, path);
+ *result = strdup("");
+ free(haystack);
+ } else {
+ *result = strdup("true");
+ free(haystack);
+ }
+
+ if (resultLen != NULL) {
+ *resultLen = strlen(*result);
+ }
+ return 0;
+}
+
+int
+register_update_commands(RecoveryCommandContext *ctx)
+{
+ int ret;
+
+ ret = commandInit();
+ if (ret < 0) return ret;
+
+ /*
+ * Commands
+ */
+
+ ret = registerCommand("assert", CMD_ARGS_BOOLEAN, cmd_assert, (void *)ctx);
+ if (ret < 0) return ret;
+
+ ret = registerCommand("delete", CMD_ARGS_WORDS, cmd_delete, (void *)ctx);
+ if (ret < 0) return ret;
+
+ ret = registerCommand("delete_recursive", CMD_ARGS_WORDS, cmd_delete,
+ (void *)ctx);
+ if (ret < 0) return ret;
+
+ ret = registerCommand("copy_dir", CMD_ARGS_WORDS,
+ cmd_copy_dir, (void *)ctx);
+ if (ret < 0) return ret;
+
+ ret = registerCommand("run_program", CMD_ARGS_WORDS,
+ cmd_run_program, (void *)ctx);
+ if (ret < 0) return ret;
+
+ ret = registerCommand("set_perm", CMD_ARGS_WORDS,
+ cmd_set_perm, (void *)ctx);
+ if (ret < 0) return ret;
+
+ ret = registerCommand("set_perm_recursive", CMD_ARGS_WORDS,
+ cmd_set_perm, (void *)ctx);
+ if (ret < 0) return ret;
+
+ ret = registerCommand("show_progress", CMD_ARGS_WORDS,
+ cmd_show_progress, (void *)ctx);
+ if (ret < 0) return ret;
+
+ ret = registerCommand("symlink", CMD_ARGS_WORDS, cmd_symlink, (void *)ctx);
+ if (ret < 0) return ret;
+
+ ret = registerCommand("format", CMD_ARGS_WORDS, cmd_format, (void *)ctx);
+ if (ret < 0) return ret;
+
+ ret = registerCommand("write_radio_image", CMD_ARGS_WORDS,
+ cmd_write_firmware_image, (void *)ctx);
+ if (ret < 0) return ret;
+
+ ret = registerCommand("write_hboot_image", CMD_ARGS_WORDS,
+ cmd_write_firmware_image, (void *)ctx);
+ if (ret < 0) return ret;
+
+ ret = registerCommand("write_raw_image", CMD_ARGS_WORDS,
+ cmd_write_raw_image, (void *)ctx);
+ if (ret < 0) return ret;
+
+ ret = registerCommand("mark", CMD_ARGS_WORDS, cmd_mark, (void *)ctx);
+ if (ret < 0) return ret;
+
+ ret = registerCommand("done", CMD_ARGS_WORDS, cmd_done, (void *)ctx);
+ if (ret < 0) return ret;
+
+ /*
+ * Functions
+ */
+
+ ret = registerFunction("compatible_with", fn_compatible_with, (void *)ctx);
+ if (ret < 0) return ret;
+
+ ret = registerFunction("update_forced", fn_update_forced, (void *)ctx);
+ if (ret < 0) return ret;
+
+ ret = registerFunction("get_mark", fn_get_mark, (void *)ctx);
+ if (ret < 0) return ret;
+
+ ret = registerFunction("hash_dir", fn_hash_dir, (void *)ctx);
+ if (ret < 0) return ret;
+
+ ret = registerFunction("matches", fn_matches, (void *)ctx);
+ if (ret < 0) return ret;
+
+ ret = registerFunction("concat", fn_concat, (void *)ctx);
+ if (ret < 0) return ret;
+
+ ret = registerFunction("getprop", fn_getprop, (void *)ctx);
+ if (ret < 0) return ret;
+
+ ret = registerFunction("file_contains", fn_file_contains, (void *)ctx);
+ if (ret < 0) return ret;
+
+ return 0;
+}
diff --git a/commands.h b/commands.h
new file mode 100644
index 0000000..e9acea2
--- /dev/null
+++ b/commands.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef RECOVERY_COMMANDS_H_
+#define RECOVERY_COMMANDS_H_
+
+#include "minzip/Zip.h"
+
+typedef struct {
+ ZipArchive *package;
+} RecoveryCommandContext;
+
+int register_update_commands(RecoveryCommandContext *ctx);
+
+#endif // RECOVERY_COMMANDS_H_
diff --git a/default_recovery_ui.c b/default_recovery_ui.c
index cb76b9a..025ee98 100644
--- a/default_recovery_ui.c
+++ b/default_recovery_ui.c
@@ -27,6 +27,8 @@
"apply sdcard:update.zip",
"wipe data/factory reset",
"wipe cache partition",
+ "toggle signature verification",
+ "toggle script asserts",
NULL };
int device_toggle_display(volatile char* key_pressed, int key_code) {
diff --git a/extendedcommands.c b/extendedcommands.c
new file mode 100644
index 0000000..b2aacc9
--- /dev/null
+++ b/extendedcommands.c
@@ -0,0 +1,39 @@
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <limits.h>
+#include <linux/input.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/reboot.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "bootloader.h"
+#include "common.h"
+#include "cutils/properties.h"
+#include "firmware.h"
+#include "install.h"
+#include "minui/minui.h"
+#include "minzip/DirUtil.h"
+#include "roots.h"
+#include "recovery_ui.h"
+
+int signature_check_enabled = 1;
+int script_assert_enabled = 1;
+
+void
+toggle_signature_check()
+{
+ signature_check_enabled = !signature_check_enabled;
+ ui_print("Signature Check: %s\n", signature_check_enabled ? "Enabled" : "Disabled");
+}
+
+void toggle_script_asserts()
+{
+ script_assert_enabled = !script_assert_enabled;
+ ui_print("Script Asserts: %s\n", signature_check_enabled ? "Enabled" : "Disabled");
+}
\ No newline at end of file
diff --git a/extendedcommands.h b/extendedcommands.h
new file mode 100644
index 0000000..5b82960
--- /dev/null
+++ b/extendedcommands.h
@@ -0,0 +1,5 @@
+extern int signature_check_enabled;
+extern int script_assert_enabled;
+
+void
+toggle_signature_check();
\ No newline at end of file
diff --git a/install.c b/install.c
index d0d9038..5a829d1 100644
--- a/install.c
+++ b/install.c
@@ -35,6 +35,8 @@
#include "firmware.h"
#include "legacy.h"
+#include "extendedcommands.h"
+
#define ASSUMED_UPDATE_BINARY_NAME "META-INF/com/google/android/update-binary"
#define PUBLIC_KEYS_FILE "/res/keys"
@@ -347,27 +349,30 @@
ui_print("Opening update package...\n");
LOGI("Update file path: %s\n", path);
- int numKeys;
- RSAPublicKey* loadedKeys = load_keys(PUBLIC_KEYS_FILE, &numKeys);
- if (loadedKeys == NULL) {
- LOGE("Failed to load keys\n");
- return INSTALL_CORRUPT;
- }
- LOGI("%d key(s) loaded from %s\n", numKeys, PUBLIC_KEYS_FILE);
-
- // Give verification half the progress bar...
- ui_print("Verifying update package...\n");
- ui_show_progress(
- VERIFICATION_PROGRESS_FRACTION,
- VERIFICATION_PROGRESS_TIME);
-
int err;
- err = verify_file(path, loadedKeys, numKeys);
- free(loadedKeys);
- LOGI("verify_file returned %d\n", err);
- if (err != VERIFY_SUCCESS) {
- LOGE("signature verification failed\n");
- return INSTALL_CORRUPT;
+
+ if (signature_check_enabled) {
+ int numKeys;
+ RSAPublicKey* loadedKeys = load_keys(PUBLIC_KEYS_FILE, &numKeys);
+ if (loadedKeys == NULL) {
+ LOGE("Failed to load keys\n");
+ return INSTALL_CORRUPT;
+ }
+ LOGI("%d key(s) loaded from %s\n", numKeys, PUBLIC_KEYS_FILE);
+
+ // Give verification half the progress bar...
+ ui_print("Verifying update package...\n");
+ ui_show_progress(
+ VERIFICATION_PROGRESS_FRACTION,
+ VERIFICATION_PROGRESS_TIME);
+
+ err = verify_file(path, loadedKeys, numKeys);
+ free(loadedKeys);
+ LOGI("verify_file returned %d\n", err);
+ if (err != VERIFY_SUCCESS) {
+ LOGE("signature verification failed\n");
+ return INSTALL_CORRUPT;
+ }
}
/* Try to open the package.
diff --git a/legacy.c b/legacy.c
index b50ba3e..dab06ad 100644
--- a/legacy.c
+++ b/legacy.c
@@ -46,9 +46,44 @@
#include "roots.h"
#include "verifier.h"
+static int read_data(ZipArchive *zip, const ZipEntry *entry,
+ char** ppData, int* pLength) {
+ int len = (int)mzGetZipEntryUncompLen(entry);
+ if (len <= 0) {
+ LOGE("Bad data length %d\n", len);
+ return -1;
+ }
+ char *data = malloc(len + 1);
+ if (data == NULL) {
+ LOGE("Can't allocate %d bytes for data\n", len + 1);
+ return -2;
+ }
+ bool ok = mzReadZipEntry(zip, entry, data, len);
+ if (!ok) {
+ LOGE("Error while reading data\n");
+ free(data);
+ return -3;
+ }
+ data[len] = '\0'; // not necessary, but just to be safe
+ *ppData = data;
+ if (pLength) {
+ *pLength = len;
+ }
+ return 0;
+}
+
int
handle_update_script(ZipArchive *zip, const ZipEntry *update_script_entry)
{
+ // This is bizarre. The build fails with "undefined reference"
+ // unless the following two functions are referenced from somewhere to
+ // force them to be linked. This seems to be a problem with yacc/lex.
+ if (zip == 1)
+ {
+ fwrite(NULL, 0, 0, NULL);
+ fileno(NULL);
+ }
+
/* Read the entire script into a buffer.
*/
int script_len;
diff --git a/recovery.c b/recovery.c
index 5b3f6e5..b857610 100644
--- a/recovery.c
+++ b/recovery.c
@@ -38,6 +38,9 @@
#include "roots.h"
#include "recovery_ui.h"
+#include "extendedcommands.h"
+#include "commands.h"
+
static const struct option OPTIONS[] = {
{ "send_intent", required_argument, NULL, 's' },
{ "update_package", required_argument, NULL, 'u' },
@@ -430,6 +433,12 @@
}
}
break;
+ case ITEM_SIG_CHECK:
+ toggle_signature_check();
+ break;
+ case ITEM_ASSERTS:
+ toggle_script_asserts();
+ break;
}
}
}
@@ -482,6 +491,11 @@
fprintf(stderr, "\n");
int status = INSTALL_SUCCESS;
+
+ RecoveryCommandContext ctx = { NULL };
+ if (register_update_commands(&ctx)) {
+ LOGE("Can't install update commands\n");
+ }
if (update_package != NULL) {
status = install_package(update_package);
diff --git a/recovery_ui.h b/recovery_ui.h
index 8818ef3..1aabbdf 100644
--- a/recovery_ui.h
+++ b/recovery_ui.h
@@ -66,6 +66,8 @@
#define ITEM_APPLY_SDCARD 1
#define ITEM_WIPE_DATA 2
#define ITEM_WIPE_CACHE 3
+#define ITEM_SIG_CHECK 4
+#define ITEM_ASSERTS 5
// Header text to display above the main menu.
extern char* MENU_HEADERS[];