summaryrefslogtreecommitdiff
path: root/installer.c
diff options
context:
space:
mode:
Diffstat (limited to 'installer.c')
-rw-r--r--installer.c477
1 files changed, 477 insertions, 0 deletions
diff --git a/installer.c b/installer.c
new file mode 100644
index 0000000..0bf8f48
--- /dev/null
+++ b/installer.c
@@ -0,0 +1,477 @@
+/* commands/sysloader/installer/installer.c
+ *
+ * Copyright 2008, 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.
+ */
+
+#define LOG_TAG "installer"
+
+#include <sys/types.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+
+#include <cutils/config_utils.h>
+#include <cutils/log.h>
+
+#include "diskconfig.h"
+#include "installer.h"
+
+#define MKE2FS_BIN "/system/bin/mke2fs"
+#define E2FSCK_BIN "/system/bin/e2fsck"
+#define TUNE2FS_BIN "/system/bin/tune2fs"
+#define RESIZE2FS_BIN "/system/bin/resize2fs"
+
+static int
+usage(void)
+{
+ fprintf(stderr, "Usage: %s\n", LOG_TAG);
+ fprintf(stderr, "\t-c <path> - Path to installer conf file "
+ "(/system/etc/installer.conf)\n");
+ fprintf(stderr, "\t-l <path> - Path to device disk layout conf file "
+ "(/system/etc/disk_layout.conf)\n");
+ fprintf(stderr, "\t-h - This help message\n");
+ fprintf(stderr, "\t-d - Dump the compiled in partition info.\n");
+ fprintf(stderr, "\t-p <path> - Path to device that should be mounted"
+ " to /data.\n");
+ fprintf(stderr, "\t-t - Test mode. Don't write anything to disk.\n");
+ return 1;
+}
+
+static cnode *
+read_conf_file(const char *fn)
+{
+ cnode *root = config_node("", "");
+ config_load_file(root, fn);
+
+ if (root->first_child == NULL) {
+ LOGE("Could not read config file %s", fn);
+ return NULL;
+ }
+
+ return root;
+}
+
+static int
+exec_cmd(const char *cmd, ...) /* const char *arg, ...) */
+{
+ va_list ap;
+ int size = 0;
+ char *str;
+ char *outbuf;
+ int rv;
+
+ /* compute the size for the command buffer */
+ size = strlen(cmd) + 1;
+ va_start(ap, cmd);
+ while ((str = va_arg(ap, char *))) {
+ size += strlen(str) + 1; /* need room for the space separator */
+ }
+ va_end(ap);
+
+ if (!(outbuf = malloc(size + 1))) {
+ LOGE("Can't allocate memory to exec cmd");
+ return -1;
+ }
+
+ /* this is a bit inefficient, but is trivial, and works */
+ strcpy(outbuf, cmd);
+ va_start(ap, cmd);
+ while ((str = va_arg(ap, char *))) {
+ strcat(outbuf, " ");
+ strcat(outbuf, str);
+ }
+ va_end(ap);
+
+ LOGI("Executing: %s", outbuf);
+ rv = system(outbuf);
+ free(outbuf);
+ if (rv < 0) {
+ LOGI("Error while trying to execute '%s'", cmd);
+ return -1;
+ }
+ rv = WEXITSTATUS(rv);
+ LOGI("Done executing %s (%d)", outbuf, rv);
+ return rv;
+}
+
+
+static int
+do_fsck(const char *dst, int force)
+{
+ int rv;
+ const char *opts = force ? "-fy" : "-y";
+
+
+ LOGI("Running e2fsck... (force=%d) This MAY take a while.", force);
+ if ((rv = exec_cmd(E2FSCK_BIN, "-C 0", opts, dst, NULL)) < 0)
+ return 1;
+ if (rv >= 4) {
+ LOGE("Error while running e2fsck: %d", rv);
+ return 1;
+ }
+ sync();
+ LOGI("e2fsck succeeded (exit code: %d)", rv);
+
+ return 0;
+}
+
+static int
+process_ext2_image(const char *dst, const char *src, uint32_t flags, int test)
+{
+ int rv;
+
+ /* First, write the image to disk. */
+ if (write_raw_image(dst, src, 0, test))
+ return 1;
+
+ if (test)
+ return 0;
+
+ /* Next, let's e2fsck the fs to make sure it got written ok, and
+ * everything is peachy */
+ if (do_fsck(dst, 1))
+ return 1;
+
+ /* set the mount count to 1 so that 1st mount on boot doesn't complain */
+ if ((rv = exec_cmd(TUNE2FS_BIN, "-C", "1", dst, NULL)) < 0)
+ return 1;
+ if (rv) {
+ LOGE("Error while running tune2fs: %d", rv);
+ return 1;
+ }
+
+ /* If the user requested that we resize, let's do it now */
+ if (flags & INSTALL_FLAG_RESIZE) {
+ if ((rv = exec_cmd(RESIZE2FS_BIN, "-F", dst, NULL)) < 0)
+ return 1;
+ if (rv) {
+ LOGE("Error while running resize2fs: %d", rv);
+ return 1;
+ }
+ sync();
+ if (do_fsck(dst, 0))
+ return 1;
+ }
+
+ /* make this an ext3 fs? */
+ if (flags & INSTALL_FLAG_ADDJOURNAL) {
+ if ((rv = exec_cmd(TUNE2FS_BIN, "-j", dst, NULL)) < 0)
+ return 1;
+ if (rv) {
+ LOGE("Error while running tune2fs: %d", rv);
+ return 1;
+ }
+ sync();
+ if (do_fsck(dst, 0))
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/* TODO: PLEASE break up this function into several functions that just
+ * do what they need with the image node. Many of them will end up
+ * looking at same strings, but it will be sooo much cleaner */
+static int
+process_image_node(cnode *img, struct disk_info *dinfo, int test)
+{
+ struct part_info *pinfo = NULL;
+ loff_t offset = (loff_t)-1;
+ const char *filename = NULL;
+ char *dest_part = NULL;
+ const char *tmp;
+ uint32_t flags = 0;
+ uint8_t type = 0;
+ int rv;
+ int func_ret = 1;
+
+ filename = config_str(img, "filename", NULL);
+
+ /* process the 'offset' image parameter */
+ if ((tmp = config_str(img, "offset", NULL)) != NULL)
+ offset = strtoull(tmp, NULL, 0);
+
+ /* process the 'partition' image parameter */
+ if ((tmp = config_str(img, "partition", NULL)) != NULL) {
+ if (offset != (loff_t)-1) {
+ LOGE("Cannot specify the partition name AND an offset for %s",
+ img->name);
+ goto fail;
+ }
+
+ if (!(pinfo = find_part(dinfo, tmp))) {
+ LOGE("Cannot find partition %s while processing %s",
+ tmp, img->name);
+ goto fail;
+ }
+
+ if (!(dest_part = find_part_device(dinfo, pinfo->name))) {
+ LOGE("Could not get the device name for partition %s while"
+ " processing image %s", pinfo->name, img->name);
+ goto fail;
+ }
+ offset = pinfo->start_lba * dinfo->sect_size;
+ }
+
+ /* process the 'mkfs' parameter */
+ if ((tmp = config_str(img, "mkfs", NULL)) != NULL) {
+ char *journal_opts;
+ char vol_lbl[16]; /* ext2/3 has a 16-char volume label */
+
+ if (!pinfo) {
+ LOGE("Target partition required for mkfs for '%s'", img->name);
+ goto fail;
+ } else if (filename) {
+ LOGE("Providing filename and mkfs parameters is meaningless");
+ goto fail;
+ }
+
+ if (!strcmp(tmp, "ext2"))
+ journal_opts = "";
+ else if (!strcmp(tmp, "ext3"))
+ journal_opts = "-j";
+ else {
+ LOGE("Unknown filesystem type for mkfs: %s", tmp);
+ goto fail;
+ }
+
+ /* put the partition name as the volume label */
+ strncpy(vol_lbl, pinfo->name, sizeof(vol_lbl));
+
+ /* since everything checked out, lets make the fs, and return since
+ * we don't need to do anything else */
+ rv = exec_cmd(MKE2FS_BIN, "-L", vol_lbl, journal_opts, dest_part, NULL);
+ if (rv < 0)
+ goto fail;
+ else if (rv > 0) {
+ LOGE("Error while running mke2fs: %d", rv);
+ goto fail;
+ }
+ sync();
+ if (do_fsck(dest_part, 0))
+ goto fail;
+ goto done;
+ }
+
+ /* since we didn't mkfs above, all the rest of the options assume
+ * there's a filename involved */
+ if (!filename) {
+ LOGE("Filename is required for image %s", img->name);
+ goto fail;
+ }
+
+ /* process the 'flags' image parameter */
+ if ((tmp = config_str(img, "flags", NULL)) != NULL) {
+ char *flagstr, *flagstr_orig;
+
+ if (!(flagstr = flagstr_orig = strdup(tmp))) {
+ LOGE("Cannot allocate memory for dup'd flags string");
+ goto fail;
+ }
+ while ((tmp = strsep(&flagstr, ","))) {
+ if (!strcmp(tmp, "resize"))
+ flags |= INSTALL_FLAG_RESIZE;
+ else if (!strcmp(tmp, "addjournal"))
+ flags |= INSTALL_FLAG_ADDJOURNAL;
+ else {
+ LOGE("Unknown flag '%s' for image %s", tmp, img->name);
+ free(flagstr_orig);
+ goto fail;
+ }
+ }
+ free(flagstr_orig);
+ }
+
+ /* process the 'type' image parameter */
+ if (!(tmp = config_str(img, "type", NULL))) {
+ LOGE("Type is required for image %s", img->name);
+ goto fail;
+ } else if (!strcmp(tmp, "raw")) {
+ type = INSTALL_IMAGE_RAW;
+ } else if (!strcmp(tmp, "ext2")) {
+ type = INSTALL_IMAGE_EXT2;
+ } else if (!strcmp(tmp, "ext3")) {
+ type = INSTALL_IMAGE_EXT3;
+ } else {
+ LOGE("Unknown image type '%s' for image %s", tmp, img->name);
+ goto fail;
+ }
+
+ /* at this point we MUST either have a partition in 'pinfo' or a raw
+ * 'offset', otherwise quit */
+ if (!pinfo && (offset == (loff_t)-1)) {
+ LOGE("Offset to write into the disk is unknown for %s", img->name);
+ goto fail;
+ }
+
+ if (!pinfo && (type != INSTALL_IMAGE_RAW)) {
+ LOGE("Only raw images can specify direct offset on the disk. Please"
+ " specify the target partition name instead. (%s)", img->name);
+ goto fail;
+ }
+
+ switch(type) {
+ case INSTALL_IMAGE_RAW:
+ if (write_raw_image(dinfo->device, filename, offset, test))
+ goto fail;
+ break;
+
+ case INSTALL_IMAGE_EXT3:
+ /* makes the error checking in the imager function easier */
+ if (flags & INSTALL_FLAG_ADDJOURNAL) {
+ LOGW("addjournal flag is meaningless for ext3 images");
+ flags &= ~INSTALL_FLAG_ADDJOURNAL;
+ }
+ /* ...fall through... */
+
+ case INSTALL_IMAGE_EXT2:
+ if (process_ext2_image(dest_part, filename, flags, test))
+ goto fail;
+ break;
+
+ default:
+ LOGE("Unknown image type: %d", type);
+ goto fail;
+ }
+
+done:
+ func_ret = 0;
+
+fail:
+ if (dest_part)
+ free(dest_part);
+ return func_ret;
+}
+
+int
+main(int argc, char *argv[])
+{
+ char *disk_conf_file = "/system/etc/disk_layout.conf";
+ char *inst_conf_file = "/system/etc/installer.conf";
+ char *inst_data_dir = "/data";
+ char *inst_data_dev = NULL;
+ char *data_fstype = "ext2";
+ cnode *config;
+ cnode *images;
+ cnode *img;
+ int cnt = 0;
+ struct disk_info *device_disk_info;
+ int dump = 0;
+ int test = 0;
+ int x;
+
+ while ((x = getopt (argc, argv, "thdc:l:p:")) != EOF) {
+ switch (x) {
+ case 'h':
+ return usage();
+ case 'c':
+ inst_conf_file = optarg;
+ break;
+ case 'l':
+ disk_conf_file = optarg;
+ break;
+ case 't':
+ test = 1;
+ break;
+ case 'p':
+ inst_data_dev = optarg;
+ break;
+ case 'd':
+ dump = 1;
+ break;
+ default:
+ fprintf(stderr, "Unknown argument: %c\n", (char)optopt);
+ return usage();
+ }
+ }
+
+ /* If the user asked us to wait for data device, wait for it to appear,
+ * and then mount it onto /data */
+ if (inst_data_dev && !dump) {
+ struct stat filestat;
+
+ LOGI("Waiting for device: %s", inst_data_dev);
+ while (stat(inst_data_dev, &filestat))
+ sleep(1);
+ LOGI("Device %s ready", inst_data_dev);
+ if (mount(inst_data_dev, inst_data_dir, data_fstype, MS_RDONLY, NULL)) {
+ LOGE("Could not mount %s on %s as %s", inst_data_dev, inst_data_dir,
+ data_fstype);
+ return 1;
+ }
+ }
+
+ /* Read and process the disk configuration */
+ if (!(device_disk_info = load_diskconfig(disk_conf_file, NULL))) {
+ LOGE("Errors encountered while loading disk conf file %s",
+ disk_conf_file);
+ return 1;
+ }
+
+ if (process_disk_config(device_disk_info)) {
+ LOGE("Errors encountered while processing disk config from %s",
+ disk_conf_file);
+ return 1;
+ }
+
+ /* Was all of this for educational purposes? If so, quit. */
+ if (dump) {
+ dump_disk_config(device_disk_info);
+ return 0;
+ }
+
+ /* This doesnt do anything but load the config file */
+ if (!(config = read_conf_file(inst_conf_file)))
+ return 1;
+
+ /* First, partition the drive */
+ if (apply_disk_config(device_disk_info, test))
+ return 1;
+
+ /* Now process the installer config file and write the images to disk */
+ if (!(images = config_find(config, "images"))) {
+ LOGE("Invalid configuration file %s. Missing 'images' section",
+ inst_conf_file);
+ return 1;
+ }
+
+ for (img = images->first_child; img; img = img->next) {
+ if (process_image_node(img, device_disk_info, test))
+ return 1;
+ ++cnt;
+ }
+
+ /*
+ * We have to do the apply() twice. We must do it once before the image
+ * writes to layout the disk partitions so that we can write images to
+ * them. We then do the apply() again in case one of the images
+ * replaced the MBR with a new bootloader, and thus messed with
+ * partition table.
+ */
+ if (apply_disk_config(device_disk_info, test))
+ return 1;
+
+ LOGI("Done processing installer config. Configured %d images", cnt);
+ return 0;
+}