/* 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[]) { 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[]) { 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; }
static int cmd_restore_rom(const char *name, void *cookie, int argc, const char *argv[], PermissionRequestList *permissions) { UNUSED(cookie); CHECK_WORDS(); int restoreboot = 1; int restoresystem = 1; int restoredata = 1; int restorecache = 1; int restoresdext = 1; int i; for (i = 0; i < argc; i++) { if (strcmp(argv[i], "noboot") == 0) restoreboot = 0; else if (strcmp(argv[i], "nosystem") == 0) restoresystem = 0; else if (strcmp(argv[i], "nodata") == 0) restoredata = 0; else if (strcmp(argv[i], "nocache") == 0) restorecache = 0; else if (strcmp(argv[i], "nosd-ext") == 0) restorecache = 0; } return nandroid_restore(argv[0], restoreboot, restoresystem, restoredata, restorecache, restoresdext); }
static int cmd_backup_rom(const char *name, void *cookie, int argc, const char *argv[], PermissionRequestList *permissions) { UNUSED(cookie); CHECK_WORDS(); char* backup_name = NULL; char backup_path[PATH_MAX]; switch(argc) { case 0: { char backup_path[PATH_MAX]; nandroid_generate_timestamp_path(backup_path); backup_name = backup_path; } break; case 1: backup_name = argv[0]; break; default: LOGE("Command %s requires zero or one argument\n", name); return 1; } return nandroid_backup(backup_name); }
/* format <root> */ static int cmd_format(const char *name, void *cookie, int argc, const char *argv[], PermissionRequestList *permissions) { UNUSED(name); UNUSED(cookie); CHECK_WORDS(); if (argc != 1) { LOGE("Command %s requires exactly one argument\n", name); return 1; } const char *root = argv[0]; ui_print("Formatting %s...\n", root); int ret = format_root_device(root); if (ret != 0) { LOGE("Can't format %s\n", root); return 1; } #ifdef BOARD_HAS_DATADATA if (0 == strcmp(root, "DATA:")) { ret = format_root_device("DATADATA:"); if (ret != 0) { LOGE("Can't format %s\n", root); return 1; } } #endif return 0; }
/* done */ static int cmd_done(const char *name, void *cookie, int argc, const char *argv[]) { UNUSED(name); UNUSED(cookie); CHECK_WORDS(); //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; }
/* mark <resource> dirty|clean */ static int cmd_mark(const char *name, void *cookie, int argc, const char *argv[]) { 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; }
/* 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; }
static int cmd_install_zip(const char *name, void *cookie, int argc, const char *argv[], PermissionRequestList *permissions) { UNUSED(cookie); CHECK_WORDS(); if (argc != 1) { LOGE("Command %s requires exactly one argument\n", name); return 1; } return install_zip(argv[0]); }
/* 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; }
static int cmd_sleep(const char *name, void *cookie, int argc, const char *argv[], PermissionRequestList *permissions) { UNUSED(cookie); CHECK_WORDS(); if (argc != 1) { LOGE("Command %s requires exactly one argument\n", name); return 1; } int seconds = atoi(argv[0]); sleep(seconds); return 0; }
static int cmd_print(const char *name, void *cookie, int argc, const char *argv[], PermissionRequestList *permissions) { UNUSED(cookie); CHECK_WORDS(); char message[1024]; message[0] = NULL; int i; for (i = 0; i < argc; i++) { strcat(message, argv[i]); strcat(message, " "); } strcat(message, "\n"); ui_print(message); return 0; }
/* format <root> */ static int cmd_format(const char *name, void *cookie, int argc, const char *argv[]) { 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..", root); int ret = format_root_device(root); if (ret != 0) { LOGE("Can't format %s\n", root); return 1; } return 0; }
/* 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[]) { UNUSED(name); UNUSED(cookie); CHECK_WORDS(); // 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; }
/* 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[]) { 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; }
/* 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[]) { 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; } }
/* write_raw_image <src-image> <dest-root> */ static int cmd_write_raw_image(const char *name, void *cookie, int argc, const char *argv[]) { UNUSED(cookie); CHECK_WORDS(); 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; }