From a2bb8bf00fbd01859e2475fc36fc6fa070e6eaa9 Mon Sep 17 00:00:00 2001 From: The Android Open Source Project Date: Wed, 17 Dec 2008 18:03:48 -0800 Subject: Code drop from //branches/cupcake/...@124589 --- Android.mk | 27 ++ config.mk | 191 ++++++++++++++ editdisklbl/Android.mk | 22 ++ editdisklbl/editdisklbl.c | 201 +++++++++++++++ init.rc | 30 +++ installer.c | 477 +++++++++++++++++++++++++++++++++++ installer.conf | 45 ++++ installer.h | 35 +++ installer_img_layout.conf | 23 ++ libdiskconfig/Android.mk | 46 ++++ libdiskconfig/config_mbr.c | 318 ++++++++++++++++++++++++ libdiskconfig/diskconfig.c | 533 ++++++++++++++++++++++++++++++++++++++++ libdiskconfig/diskconfig.h | 120 +++++++++ libdiskconfig/diskutils.c | 117 +++++++++ libdiskconfig/dump_diskconfig.c | 42 ++++ libdiskconfig/write_lst.c | 92 +++++++ 16 files changed, 2319 insertions(+) create mode 100644 Android.mk create mode 100644 config.mk create mode 100644 editdisklbl/Android.mk create mode 100644 editdisklbl/editdisklbl.c create mode 100644 init.rc create mode 100644 installer.c create mode 100644 installer.conf create mode 100644 installer.h create mode 100644 installer_img_layout.conf create mode 100644 libdiskconfig/Android.mk create mode 100644 libdiskconfig/config_mbr.c create mode 100644 libdiskconfig/diskconfig.c create mode 100644 libdiskconfig/diskconfig.h create mode 100644 libdiskconfig/diskutils.c create mode 100644 libdiskconfig/dump_diskconfig.c create mode 100644 libdiskconfig/write_lst.c diff --git a/Android.mk b/Android.mk new file mode 100644 index 0000000..ac0b872 --- /dev/null +++ b/Android.mk @@ -0,0 +1,27 @@ +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +ifeq ($(TARGET_USE_DISKINSTALLER),true) + +LOCAL_SRC_FILES := \ + installer.c + +LOCAL_C_INCLUDES := $(LOCAL_PATH)/libdiskconfig + +LOCAL_CFLAGS := -O2 -g -W -Wall -Werror + +LOCAL_MODULE := diskinstaller +LOCAL_MODULE_TAGS := system_builder + +LOCAL_STATIC_LIBRARIES := $(TARGET_DISK_CONFIG_LIB) +LOCAL_SYSTEM_SHARED_LIBRARIES := \ + libdiskconfig \ + libcutils \ + liblog \ + libc + +include $(BUILD_EXECUTABLE) + +include $(call first-makefiles-under,$(LOCAL_PATH)) +endif + diff --git a/config.mk b/config.mk new file mode 100644 index 0000000..2cb034d --- /dev/null +++ b/config.mk @@ -0,0 +1,191 @@ +# note: requires x86 because we assume grub is the mbr bootloader. +ifeq ($(TARGET_ARCH),x86) +ifeq ($(TARGET_USE_DISKINSTALLER),true) + +diskinstaller_root := bootable/diskinstaller + +android_sysbase_modules := \ + libc \ + libcutils \ + libdl \ + liblog \ + libm \ + libstdc++ \ + linker \ + sh \ + toolbox \ + logcat \ + gdbserver \ + strace \ + netcfg +android_sysbase_files = \ + $(call module-installed-files,$(android_sysbase_modules)) + +# $(1): source base dir +# $(2): target base dir +define sysbase-copy-files +$(hide) $(foreach _f,$(android_sysbase_files), \ + f=$(patsubst $(1)/%,$(2)/%,$(_f)); \ + mkdir -p `dirname $$f`; \ + echo "Copy: $$f" ; \ + cp -fR $(_f) $$f; \ +) +endef + +installer_base_modules := \ + libdiskconfig \ + libext2fs \ + libext2_com_err \ + libext2_e2p \ + libext2_blkid \ + libext2_uuid \ + libext2_profile \ + badblocks \ + resize2fs \ + tune2fs \ + mke2fs \ + e2fsck +installer_base_files = \ + $(call module-built-files,$(installer_base_modules)) + +# $(1): source base dir +# $(2): target base dir +define installer-copy-modules +$(hide) $(foreach m,$(installer_base_modules), \ + src=$(firstword $(strip $(call module-built-files,$(m)))); \ + dest=$(patsubst $(strip $(1))/%,$(strip $(2))/%,\ + $(firstword $(strip $(call module-installed-files,$(m))))); \ + echo "Copy: $$src -> $$dest"; \ + mkdir -p `dirname $$dest`; \ + cp -fdp $$src $$dest; \ +) +endef + +# Build the installer ramdisk image +installer_initrc := $(diskinstaller_root)/init.rc +installer_kernel := $(INSTALLED_KERNEL_TARGET) +installer_ramdisk := $(TARGET_INSTALLER_OUT)/ramdisk-installer.img +installer_build_prop := $(INSTALLED_BUILD_PROP_TARGET) +installer_config := $(diskinstaller_root)/installer.conf +installer_binary := \ + $(call intermediates-dir-for,EXECUTABLES,diskinstaller)/diskinstaller + +$(installer_ramdisk): $(diskinstaller_root)/config.mk \ + $(MKBOOTFS) \ + $(INSTALLED_RAMDISK_TARGET) \ + $(INSTALLED_BOOTIMAGE_TARGET) \ + $(TARGET_DISK_LAYOUT_CONFIG) \ + $(installer_binary) \ + $(installer_initrc) \ + $(installer_kernel) \ + $(installer_config) \ + $(android_sysbase_files) \ + $(installer_base_files) \ + $(installer_build_prop) + @echo ----- Making installer image ------ + rm -rf $(TARGET_INSTALLER_OUT) + mkdir -p $(TARGET_INSTALLER_OUT) + mkdir -p $(TARGET_INSTALLER_ROOT_OUT) + mkdir -p $(TARGET_INSTALLER_ROOT_OUT)/sbin + mkdir -p $(TARGET_INSTALLER_ROOT_OUT)/data + mkdir -p $(TARGET_INSTALLER_SYSTEM_OUT) + mkdir -p $(TARGET_INSTALLER_SYSTEM_OUT)/etc + mkdir -p $(TARGET_INSTALLER_SYSTEM_OUT)/bin + @echo Copying baseline ramdisk... + cp -fR $(TARGET_ROOT_OUT) $(TARGET_INSTALLER_OUT) + @echo Copying sysbase files... + $(call sysbase-copy-files,$(TARGET_OUT),$(TARGET_INSTALLER_SYSTEM_OUT)) + @echo Copying installer base files... + $(call installer-copy-modules,$(TARGET_OUT),\ + $(TARGET_INSTALLER_SYSTEM_OUT)) + @echo Modifying ramdisk contents... + cp -f $(installer_initrc) $(TARGET_INSTALLER_ROOT_OUT)/ + cp -f $(TARGET_DISK_LAYOUT_CONFIG) \ + $(TARGET_INSTALLER_SYSTEM_OUT)/etc/disk_layout.conf + cp -f $(installer_config) \ + $(TARGET_INSTALLER_SYSTEM_OUT)/etc/installer.conf + cp -f $(installer_binary) $(TARGET_INSTALLER_SYSTEM_OUT)/bin/installer + $(hide) chmod ug+rw $(TARGET_INSTALLER_ROOT_OUT)/default.prop + cat $(installer_build_prop) >> $(TARGET_INSTALLER_ROOT_OUT)/default.prop + $(MKBOOTFS) $(TARGET_INSTALLER_ROOT_OUT) | gzip > $(installer_ramdisk) + @echo ----- Made installer ramdisk -[ $@ ]- + +###################################################################### +# Now the installer boot image which includes the kernel and the ramdisk +internal_installerimage_args := \ + --kernel $(installer_kernel) \ + --ramdisk $(installer_ramdisk) + +internal_installerimage_files := \ + $(filter-out --%,$(internal_installerimage_args)) + +BOARD_KERNEL_CMDLINE := $(strip $(BOARD_KERNEL_CMDLINE)) +ifdef BOARD_KERNEL_CMDLINE + internal_installerimage_args += --cmdline "$(BOARD_KERNEL_CMDLINE)" +endif + +installer_tmp_img := $(TARGET_INSTALLER_OUT)/installer_tmp.img +tmp_dir_for_inst_image := \ + $(call intermediates-dir-for,EXECUTABLES,installer_img)/installer_img +internal_installerimage_args += --tmpdir $(tmp_dir_for_inst_image) +internal_installerimage_args += --genext2fs $(MKEXT2IMG) +$(installer_tmp_img): $(MKEXT2IMG) $(internal_installerimage_files) + $(call pretty,"Target installer image: $@") + $(hide) $(MKEXT2BOOTIMG) $(internal_installerimage_args) --output $@ + +###################################################################### +# Now make a data image that contains all the target image files for the +# installer. +bootldr_bin := $(PRODUCT_OUT)/grub/grub.bin +installer_target_data_files := \ + $(INSTALLED_BOOTIMAGE_TARGET) \ + $(INSTALLED_SYSTEMIMAGE) \ + $(INSTALLED_USERDATAIMAGE_TARGET) \ + $(bootldr_bin) + +installer_data_img := $(TARGET_INSTALLER_OUT)/installer_data.img +$(installer_data_img): $(diskinstaller_root)/config.mk \ + $(installer_target_data_files) \ + $(MKEXT2IMG) \ + $(installer_ramdisk) + @echo --- Making installer data image ------ + mkdir -p $(TARGET_INSTALLER_OUT) + mkdir -p $(TARGET_INSTALLER_OUT)/data + cp -f $(bootldr_bin) $(TARGET_INSTALLER_OUT)/data/bootldr.bin + cp -f $(INSTALLED_BOOTIMAGE_TARGET) $(TARGET_INSTALLER_OUT)/data/boot.img + cp -f $(INSTALLED_SYSTEMIMAGE) \ + $(TARGET_INSTALLER_OUT)/data/system.img + cp -f $(INSTALLED_USERDATAIMAGE_TARGET) \ + $(TARGET_INSTALLER_OUT)/data/userdata.img + $(call build-userimage-ext2-target,$(TARGET_INSTALLER_OUT)/data,$@,\ + inst_data,) + @echo --- Finished installer data image -[ $@ ]- + +###################################################################### +# now combine the installer image with the grub bootloader +grub_bin := $(PRODUCT_OUT)/grub/grub.bin +installer_layout := $(diskinstaller_root)/installer_img_layout.conf +edit_mbr := $(HOST_OUT_EXECUTABLES)/editdisklbl + +INSTALLED_DISKINSTALLERIMAGE_TARGET := $(PRODUCT_OUT)/installer.img +$(INSTALLED_DISKINSTALLERIMAGE_TARGET): \ + $(installer_tmp_img) \ + $(installer_data_img) \ + $(grub_bin) \ + $(edit_mbr) \ + $(installer_layout) + @echo "Creating bootable installer image: $@" + @rm -f $@ + @cat $(grub_bin) > $@ + @$(edit_mbr) -l $(installer_layout) -i $@ \ + inst_boot=$(installer_tmp_img) \ + inst_data=$(installer_data_img) + @echo "Done with bootable installer image -[ $@ ]-" + +else # ! TARGET_USE_DISKINSTALLER +INSTALLED_DISKINSTALLERIMAGE_TARGET := +endif +endif # TARGET_ARCH == x86 + +.PHONY: installer_img +installer_img: $(INSTALLED_DISKINSTALLERIMAGE_TARGET) diff --git a/editdisklbl/Android.mk b/editdisklbl/Android.mk new file mode 100644 index 0000000..1379d0d --- /dev/null +++ b/editdisklbl/Android.mk @@ -0,0 +1,22 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +ifneq ($(TARGET_SIMULATOR),true) +ifeq ($(TARGET_ARCH),x86) + +LOCAL_SRC_FILES := \ + editdisklbl.c + +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH)/../libdiskconfig + +LOCAL_CFLAGS := -O2 -g -W -Wall -Werror + +LOCAL_MODULE := editdisklbl +LOCAL_STATIC_LIBRARIES := libdiskconfig_host libcutils liblog + +include $(BUILD_HOST_EXECUTABLE) + +endif # TARGET_ARCH == x86 +endif # !TARGET_SIMULATOR diff --git a/editdisklbl/editdisklbl.c b/editdisklbl/editdisklbl.c new file mode 100644 index 0000000..6a0c31c --- /dev/null +++ b/editdisklbl/editdisklbl.c @@ -0,0 +1,201 @@ +/* tools/editdisklbl/editdisklbl.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 __USE_LARGEFILE64 +#define __USE_FILE_OFFSET64 +#include +#include +#include +#include +#include +#include +#include +#include + +#include "diskconfig.h" + +/* give us some room */ +#define EXTRA_LBAS 100 + +static struct pf_map { + struct part_info *pinfo; + const char *filename; +} part_file_map[MAX_NUM_PARTS] = { {0, 0} }; + +static int +usage(void) +{ + fprintf(stderr, + "\nusage: editdisklbl part1=file1 [part2=file2,...]\n" + "Where options can be one of:\n" + "\t\t-l -- The image layout config file.\n" + "\t\t-i -- The image file to edit.\n" + "\t\t-t -- Test mode (optional)\n" + "\t\t-v -- Be verbose\n" + "\t\t-h -- This message (optional)\n" + ); + return 1; +} + +static int +parse_args(int argc, char *argv[], struct disk_info **dinfo, int *test, + int *verbose) +{ + char *layout_conf = NULL; + char *img_file = NULL; + struct stat filestat; + int x; + int update_lba = 0; + + while ((x = getopt (argc, argv, "thl:i:")) != EOF) { + switch (x) { + case 'h': + return usage(); + case 'l': + layout_conf = optarg; + break; + case 't': + *test = 1; + break; + case 'i': + img_file = optarg; + break; + case 'v': + *verbose = 1; + break; + default: + fprintf(stderr, "Unknown argument: %c\n", (char)optopt); + return usage(); + } + } + + if (!img_file || !layout_conf) { + fprintf(stderr, "Image filename and configuration file are required\n"); + return usage(); + } + + /* we'll need to parse the command line later for partition-file + * mappings, so make sure there's at least something there */ + if (optind >= argc) { + fprintf(stderr, "Must provide partition -> file mappings\n"); + return usage(); + } + + if (stat(img_file, &filestat)) { + perror("Cannot stat image file"); + return 1; + } + + /* make sure we don't screw up and write to a block device on the host + * and wedge things. I just don't trust myself. */ + if (!S_ISREG(filestat.st_mode)) { + fprintf(stderr, "This program should only be used on regular files."); + return 1; + } + + /* load the disk layout file */ + if (!(*dinfo = load_diskconfig(layout_conf, img_file))) { + fprintf(stderr, "Errors encountered while loading disk conf file %s", + layout_conf); + return 1; + } + + if ((*dinfo)->num_lba == 0) { + (*dinfo)->num_lba = (*dinfo)->skip_lba + EXTRA_LBAS; + update_lba = 1; + } + + /* parse the filename->partition mappings from the command line and patch + * up a loaded config file's partition table entries to have + * length == filesize */ + x = 0; + while (optind < argc) { + char *pair = argv[optind++]; + char *part_name; + struct part_info *pinfo; + struct stat tmp_stat; + + if (x >= MAX_NUM_PARTS) { + fprintf(stderr, "Error: Too many partitions specified (%d)!\n", x); + return 1; + } + + if (!(part_name = strsep(&pair, "=")) || !pair || !(*pair)) { + fprintf(stderr, "Error parsing partition mappings\n"); + return usage(); + } + + if (!(pinfo = find_part(*dinfo, part_name))) { + fprintf(stderr, "Partition '%s' not found.\n", part_name); + return 1; + } + + /* here pair points to the filename (after the '=') */ + part_file_map[x].pinfo = pinfo; + part_file_map[x++].filename = pair; + + if (stat(pair, &tmp_stat) < 0) { + fprintf(stderr, "Could not stat file: %s\n", pair); + return 1; + } + + pinfo->len_kb = (uint32_t) ((tmp_stat.st_size + 1023) >> 10); + if (update_lba) + (*dinfo)->num_lba += + ((uint64_t)pinfo->len_kb * 1024) / (*dinfo)->sect_size; + printf("Updated %s length to be %uKB\n", pinfo->name, pinfo->len_kb); + } + + return 0; +} + +int +main(int argc, char *argv[]) +{ + struct disk_info *dinfo = NULL; + int test = 0; + int verbose = 0; + int cnt; + + if (parse_args(argc, argv, &dinfo, &test, &verbose)) + return 1; + + if (verbose) + dump_disk_config(dinfo); + + if (test) + printf("Test mode enabled. Actions will not be committed to disk!\n"); + + if (apply_disk_config(dinfo, test)) { + fprintf(stderr, "Could not apply disk configuration!\n"); + return 1; + } + + printf("Copying images to specified partition offsets\n"); + /* now copy the images to their appropriate locations on disk */ + for (cnt = 0; cnt < MAX_NUM_PARTS && part_file_map[cnt].pinfo; ++cnt) { + loff_t offs = part_file_map[cnt].pinfo->start_lba * dinfo->sect_size; + const char *dest_fn = dinfo->device; + if (write_raw_image(dest_fn, part_file_map[cnt].filename, offs, test)) { + fprintf(stderr, "Could not write images after editing label.\n"); + return 1; + } + } + printf("File edit complete. Wrote %d images.\n", cnt); + + return 0; +} diff --git a/init.rc b/init.rc new file mode 100644 index 0000000..48cf3ca --- /dev/null +++ b/init.rc @@ -0,0 +1,30 @@ +on init + export PATH /sbin:/system/sbin:/system/bin + export ANDROID_ROOT /system + export ANDROID_DATA /data + + symlink /system/etc /etc + + mkdir /data + mkdir /cache + mount /tmp /tmp tmpfs + +# mount the installer needed partitions +# mount ext2 /dev/block/sdb2 /data ro + +on boot + ifup lo + hostname localhost + domainname localdomain + + class_start default + +service installer /system/bin/installer -p /dev/block/sdb2 + console + oneshot + +service logcat /system/bin/logcat + console + +service console /system/bin/sh + console 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +#include + +#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 to installer conf file " + "(/system/etc/installer.conf)\n"); + fprintf(stderr, "\t-l - 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 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; +} diff --git a/installer.conf b/installer.conf new file mode 100644 index 0000000..acdd7a2 --- /dev/null +++ b/installer.conf @@ -0,0 +1,45 @@ +images { + bootldr { + offset 0 + filename /data/bootldr.bin + type raw + } + + boot { + partition boot + filename /data/boot.img + type raw + } + + system { + partition system + filename /data/system.img + type ext2 + flags resize,addjournal + } + + data { + partition data + filename /data/userdata.img + type ext2 + flags resize,addjournal + } + + cache { + partition cache + mkfs ext3 + } + + third_party { + partition third_party + mkfs ext3 + } + +## TODO: Add the support for this later? +# system { +# partition system +# filename /data/system.tar.gz +# type targz +# mkfs ext3 +# } +} diff --git a/installer.h b/installer.h new file mode 100644 index 0000000..98bb2f9 --- /dev/null +++ b/installer.h @@ -0,0 +1,35 @@ +/* commands/sysloader/installer/installer.h + * + * 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. + */ + +#ifndef __COMMANDS_SYSLOADER_INSTALLER_INSTALLER_H +#define __COMMANDS_SYSLOADER_INSTALLER_INSTALLER_H + +#include + +/* image types */ +#define INSTALL_IMAGE_RAW 1 +#define INSTALL_IMAGE_EXT2 2 +#define INSTALL_IMAGE_EXT3 3 +#define INSTALL_IMAGE_TARGZ 10 + + +/* flags */ +#define INSTALL_FLAG_RESIZE 0x1 +#define INSTALL_FLAG_ADDJOURNAL 0x2 + +#endif /* __COMMANDS_SYSLOADER_INSTALLER_INSTALLER_H */ + diff --git a/installer_img_layout.conf b/installer_img_layout.conf new file mode 100644 index 0000000..42e3ea4 --- /dev/null +++ b/installer_img_layout.conf @@ -0,0 +1,23 @@ +device { + scheme mbr + + # bytes in a disk "block", must be a power of 2! + sector_size 512 + + # What LBA should the partitions start at? + start_lba 2048 + + # Autodetect disk size if == 0 + num_lba 0 + + partitions { + inst_boot { + active y + type linux + } + + inst_data { + type linux + } + } +} diff --git a/libdiskconfig/Android.mk b/libdiskconfig/Android.mk new file mode 100644 index 0000000..fc7e68f --- /dev/null +++ b/libdiskconfig/Android.mk @@ -0,0 +1,46 @@ +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +commonSources := \ + diskconfig.c \ + diskutils.c \ + write_lst.c \ + config_mbr.c + +ifneq ($(TARGET_SIMULATOR),true) +ifeq ($(TARGET_ARCH),x86) + +########################### +# static library for host +LOCAL_SRC_FILES := $(commonSources) + +LOCAL_CFLAGS := -O2 -g -W -Wall -Werror -D_LARGEFILE64_SOURCE + +LOCAL_MODULE := libdiskconfig_host +LOCAL_STATIC_LIBRARIES := libcutils +include $(BUILD_HOST_STATIC_LIBRARY) + +## Build a test executable for host (to dump configuration files). +include $(CLEAR_VARS) +LOCAL_SRC_FILES := $(commonSources) +LOCAL_SRC_FILES += dump_diskconfig.c +LOCAL_MODULE := dump_diskconfig +LOCAL_STATIC_LIBRARIES := libdiskconfig_host libcutils +include $(BUILD_HOST_EXECUTABLE) + +########################### +# shared library for target +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := $(commonSources) + +LOCAL_CFLAGS := -O2 -g -W -Wall -Werror + +LOCAL_MODULE := libdiskconfig +LOCAL_MODULE_TAGS := system_builder +LOCAL_SYSTEM_SHARED_LIBRARIES := libcutils liblog libc + +include $(BUILD_SHARED_LIBRARY) + +endif # ! TARGET_SIMULATOR +endif # TARGET_ARCH == x86 diff --git a/libdiskconfig/config_mbr.c b/libdiskconfig/config_mbr.c new file mode 100644 index 0000000..4836f80 --- /dev/null +++ b/libdiskconfig/config_mbr.c @@ -0,0 +1,318 @@ +/* libs/diskconfig/diskconfig.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 "config_mbr" +#include +#include +#include +#include + +#include + +#include "diskconfig.h" + + +/* start and len are in LBA units */ +static void +cfg_pentry(struct pc_partition *pentry, uint8_t status, uint8_t type, + uint32_t start, uint32_t len) +{ + /* zero out the c/h/s entries.. they are not used */ + memset(&pentry->start, 0, sizeof(struct chs)); + memset(&pentry->end, 0, sizeof(struct chs)); + + pentry->status = status; + pentry->type = type; + pentry->start_lba = start; + pentry->len_lba = len; + + LOGI("Configuring pentry. status=0x%x type=0x%x start_lba=%u len_lba=%u", + pentry->status, pentry->type, pentry->start_lba, pentry->len_lba); +} + + +static inline uint32_t +kb_to_lba(uint32_t len_kb, uint32_t sect_size) +{ + uint64_t lba; + + lba = (uint64_t)len_kb * 1024; + /* bump it up to the next LBA boundary just in case */ + lba = (lba + (uint64_t)sect_size - 1) & ~((uint64_t)sect_size - 1); + lba /= (uint64_t)sect_size; + if (lba >= 0xffffffffULL) + LOGE("Error converting kb -> lba. 32bit overflow, expect weirdness"); + return (uint32_t)(lba & 0xffffffffULL); +} + + +static struct write_list * +mk_pri_pentry(struct disk_info *dinfo, struct part_info *pinfo, int pnum, + uint32_t *lba) +{ + struct write_list *item; + struct pc_partition *pentry; + + if (pnum >= PC_NUM_BOOT_RECORD_PARTS) { + LOGE("Maximum number of primary partition exceeded."); + return NULL; + } + + if (!(item = alloc_wl(sizeof(struct pc_partition)))) { + LOGE("Unable to allocate memory for partition entry."); + return NULL; + } + + { + /* DO NOT DEREFERENCE */ + struct pc_boot_record *mbr = (void *)PC_MBR_DISK_OFFSET; + /* grab the offset in mbr where to write this partition entry. */ + item->offset = (loff_t)((uint32_t)((uint8_t *)(&mbr->ptable[pnum]))); + } + + pentry = (struct pc_partition *) &item->data; + + /* need a standard primary partition entry */ + if (pinfo) { + /* need this to be 64 bit in case len_kb is large */ + uint64_t len_lba; + + if (pinfo->len_kb != (uint32_t)-1) { + /* bump it up to the next LBA boundary just in case */ + len_lba = ((uint64_t)pinfo->len_kb * 1024); + len_lba += ((uint64_t)dinfo->sect_size - 1); + len_lba &= ~((uint64_t)dinfo->sect_size - 1); + len_lba /= (uint64_t)dinfo->sect_size; + } else { + /* make it fill the rest of disk */ + len_lba = dinfo->num_lba - *lba; + } + + cfg_pentry(pentry, ((pinfo->flags & PART_ACTIVE_FLAG) ? + PC_PART_ACTIVE : PC_PART_NORMAL), + pinfo->type, *lba, (uint32_t)len_lba); + + pinfo->start_lba = *lba; + *lba += (uint32_t)len_lba; + } else { + /* this should be made an extended partition, and should take + * up the rest of the disk as a primary partition */ + cfg_pentry(pentry, PC_PART_NORMAL, PC_PART_TYPE_EXTENDED, + *lba, dinfo->num_lba - *lba); + + /* note that we do not update the *lba because we now have to + * create a chain of extended partition tables, and first one is at + * *lba */ + } + + return item; +} + + +/* This function configures an extended boot record at the beginning of an + * extended partition. This creates a logical partition and a pointer to + * the next EBR. + * + * ext_lba == The start of the toplevel extended partition (pointed to by the + * entry in the MBR). + */ +static struct write_list * +mk_ext_pentry(struct disk_info *dinfo, struct part_info *pinfo, uint32_t *lba, + uint32_t ext_lba, struct part_info *pnext) +{ + struct write_list *item; + struct pc_boot_record *ebr; + uint32_t len; /* in lba units */ + + if (!(item = alloc_wl(sizeof(struct pc_boot_record)))) { + LOGE("Unable to allocate memory for EBR."); + return NULL; + } + + /* we are going to write the ebr at the current LBA, and then bump the + * lba counter since that is where the logical data partition will start */ + item->offset = (*lba) * dinfo->sect_size; + (*lba)++; + + ebr = (struct pc_boot_record *) &item->data; + memset(ebr, 0, sizeof(struct pc_boot_record)); + ebr->mbr_sig = PC_BIOS_BOOT_SIG; + + if (pinfo->len_kb != (uint32_t)-1) + len = kb_to_lba(pinfo->len_kb, dinfo->sect_size); + else { + if (pnext) { + LOGE("Only the last partition can be specified to fill the disk " + "(name = '%s')", pinfo->name); + goto fail; + } + len = dinfo->num_lba - *lba; + /* update the pinfo structure to reflect the new size, for + * bookkeeping */ + pinfo->len_kb = + (uint32_t)(((uint64_t)len * (uint64_t)dinfo->sect_size) / + ((uint64_t)1024)); + } + + cfg_pentry(&ebr->ptable[PC_EBR_LOGICAL_PART], PC_PART_NORMAL, + pinfo->type, 1, len); + + pinfo->start_lba = *lba; + *lba += len; + + /* If this is not the last partition, we have to create a link to the + * next extended partition. + * + * Otherwise, there's nothing to do since the "pointer entry" is + * already zero-filled. + */ + if (pnext) { + /* The start lba for next partition is an offset from the beginning + * of the top-level extended partition */ + uint32_t next_start_lba = *lba - ext_lba; + uint32_t next_len_lba; + if (pnext->len_kb != (uint32_t)-1) + next_len_lba = 1 + kb_to_lba(pnext->len_kb, dinfo->sect_size); + else + next_len_lba = dinfo->num_lba - *lba; + cfg_pentry(&ebr->ptable[PC_EBR_NEXT_PTR_PART], PC_PART_NORMAL, + PC_PART_TYPE_EXTENDED, next_start_lba, next_len_lba); + } + + return item; + +fail: + free_wl(item); + return NULL; +} + + +struct write_list * +config_mbr(struct disk_info *dinfo) +{ + struct part_info *pinfo; + uint32_t cur_lba = dinfo->skip_lba; + uint32_t ext_lba = 0; + struct write_list *wr_list = NULL; + struct write_list *temp_wr = NULL; + int cnt = 0; + int extended = 0; + + if (!dinfo->part_lst) + return NULL; + + for (cnt = 0; cnt < dinfo->num_parts; ++cnt) { + pinfo = &dinfo->part_lst[cnt]; + + /* Should we create an extedned partition? */ + if (cnt == (PC_NUM_BOOT_RECORD_PARTS - 1)) { + if (cnt + 1 < dinfo->num_parts) { + extended = 1; + ext_lba = cur_lba; + if ((temp_wr = mk_pri_pentry(dinfo, NULL, cnt, &cur_lba))) + wlist_add(&wr_list, temp_wr); + else { + LOGE("Cannot create primary extended partition."); + goto fail; + } + } + } + + /* if extended, need 1 lba for ebr */ + if ((cur_lba + extended) >= dinfo->num_lba) + goto nospace; + else if (pinfo->len_kb != (uint32_t)-1) { + uint32_t sz_lba = (pinfo->len_kb / dinfo->sect_size) * 1024; + if ((cur_lba + sz_lba + extended) > dinfo->num_lba) + goto nospace; + } + + if (!extended) + temp_wr = mk_pri_pentry(dinfo, pinfo, cnt, &cur_lba); + else { + struct part_info *pnext; + pnext = cnt + 1 < dinfo->num_parts ? &dinfo->part_lst[cnt+1] : NULL; + temp_wr = mk_ext_pentry(dinfo, pinfo, &cur_lba, ext_lba, pnext); + } + + if (temp_wr) + wlist_add(&wr_list, temp_wr); + else { + LOGE("Cannot create partition %d (%s).", cnt, pinfo->name); + goto fail; + } + } + + /* fill in the rest of the MBR with empty parts (if needed). */ + for (; cnt < PC_NUM_BOOT_RECORD_PARTS; ++cnt) { + struct part_info blank; + cur_lba = 0; + memset(&blank, 0, sizeof(struct part_info)); + if (!(temp_wr = mk_pri_pentry(dinfo, &blank, cnt, &cur_lba))) { + LOGE("Cannot create blank partition %d.", cnt); + goto fail; + } + wlist_add(&wr_list, temp_wr); + } + + return wr_list; + +nospace: + LOGE("Not enough space to add parttion '%s'.", pinfo->name); + +fail: + wlist_free(wr_list); + return NULL; +} + + +/* Returns the device path of the partition referred to by 'name' + * Must be freed by the caller. + */ +char * +find_mbr_part(struct disk_info *dinfo, const char *name) +{ + struct part_info *plist = dinfo->part_lst; + int num = 0; + char *dev_name = NULL; + int has_extended = (dinfo->num_parts > PC_NUM_BOOT_RECORD_PARTS); + + for(num = 1; num <= dinfo->num_parts; ++num) { + if (!strcmp(plist[num-1].name, name)) + break; + } + + if (num > dinfo->num_parts) + return NULL; + + if (has_extended && (num >= PC_NUM_BOOT_RECORD_PARTS)) + num++; + + if (!(dev_name = malloc(MAX_NAME_LEN))) { + LOGE("Cannot allocate memory."); + return NULL; + } + + num = snprintf(dev_name, MAX_NAME_LEN, "%s%d", dinfo->device, num); + if (num >= MAX_NAME_LEN) { + LOGE("Device name is too long?!"); + free(dev_name); + return NULL; + } + + return dev_name; +} diff --git a/libdiskconfig/diskconfig.c b/libdiskconfig/diskconfig.c new file mode 100644 index 0000000..8a932ca --- /dev/null +++ b/libdiskconfig/diskconfig.c @@ -0,0 +1,533 @@ +/* libs/diskconfig/diskconfig.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 "diskconfig" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include "diskconfig.h" + + +static int +parse_len(const char *str, uint64_t *plen) +{ + char tmp[64]; + int len_str; + uint32_t multiple = 1; + + strncpy(tmp, str, sizeof(tmp)); + tmp[sizeof(tmp)-1] = '\0'; + len_str = strlen(tmp); + if (!len_str) { + LOGE("Invalid disk length specified."); + return 1; + } + + switch(tmp[len_str - 1]) { + case 'M': case 'm': + /* megabyte */ + multiple <<= 10; + case 'K': case 'k': + /* kilobytes */ + multiple <<= 10; + tmp[len_str - 1] = '\0'; + break; + default: + break; + } + + *plen = strtoull(tmp, NULL, 0); + if (!*plen) { + LOGE("Invalid length specified: %s", str); + return 1; + } + + if (*plen == (uint64_t)-1) { + if (multiple > 1) { + LOGE("Size modifier illegal when len is -1"); + return 1; + } + } else { + /* convert len to kilobytes */ + if (multiple > 1024) + multiple >>= 10; + *plen *= multiple; + + if (*plen > 0xffffffffULL) { + LOGE("Length specified is too large!: %llu KB", *plen); + return 1; + } + } + + return 0; +} + + +static int +load_partitions(cnode *root, struct disk_info *dinfo) +{ + cnode *partnode; + + dinfo->num_parts = 0; + for (partnode = root->first_child; partnode; partnode = partnode->next) { + struct part_info *pinfo = &dinfo->part_lst[dinfo->num_parts]; + const char *tmp; + + /* bleh, i will leak memory here, but i DONT CARE since + * the only right thing to do when this function fails + * is to quit */ + pinfo->name = strdup(partnode->name); + + if(config_bool(partnode, "active", 0)) + pinfo->flags |= PART_ACTIVE_FLAG; + + if (!(tmp = config_str(partnode, "type", NULL))) { + LOGE("Partition type required: %s", pinfo->name); + return 1; + } + + /* possible values are: linux */ + if (!strcmp(tmp, "linux")) { + pinfo->type = PC_PART_TYPE_LINUX; + } else { + LOGE("Unsupported partition type found: %s", tmp); + return 1; + } + + if ((tmp = config_str(partnode, "len", NULL)) != NULL) { + uint64_t len; + if (parse_len(tmp, &len)) + return 1; + pinfo->len_kb = (uint32_t) len; + } else + pinfo->len_kb = 0; + + ++dinfo->num_parts; + } + + return 0; +} + +struct disk_info * +load_diskconfig(const char *fn, char *path_override) +{ + struct disk_info *dinfo; + cnode *devroot; + cnode *partnode; + cnode *root = config_node("", ""); + const char *tmp; + + if (!(dinfo = malloc(sizeof(struct disk_info)))) { + LOGE("Could not malloc disk_info"); + return NULL; + } + memset(dinfo, 0, sizeof(struct disk_info)); + + if (!(dinfo->part_lst = malloc(MAX_NUM_PARTS * sizeof(struct part_info)))) { + LOGE("Could not malloc part_lst"); + goto fail; + } + memset(dinfo->part_lst, 0, + sizeof(MAX_NUM_PARTS * sizeof(struct part_info))); + + config_load_file(root, fn); + if (root->first_child == NULL) { + LOGE("Could not read config file %s", fn); + goto fail; + } + + if (!(devroot = config_find(root, "device"))) { + LOGE("Could not find device section in config file '%s'", fn); + goto fail; + } + + + if (!(tmp = config_str(devroot, "path", path_override))) { + LOGE("device path is requried"); + goto fail; + } + dinfo->device = strdup(tmp); + + /* find the partition scheme */ + if (!(tmp = config_str(devroot, "scheme", NULL))) { + LOGE("partition scheme is required"); + goto fail; + } else if (!strcmp(tmp, "mbr")) { + dinfo->scheme = PART_SCHEME_MBR; + } else if (!strcmp(tmp, "gpt")) { + LOGE("'gpt' partition scheme not supported yet."); + goto fail; + } else { + LOGE("Unknown partition scheme specified: %s", tmp); + goto fail; + } + + /* grab the sector size (in bytes) */ + tmp = config_str(devroot, "sector_size", "512"); + dinfo->sect_size = strtol(tmp, NULL, 0); + if (!dinfo->sect_size) { + LOGE("Invalid sector size: %s", tmp); + goto fail; + } + + /* first lba where the partitions will start on disk */ + if (!(tmp = config_str(devroot, "start_lba", NULL))) { + LOGE("start_lba must be provided"); + goto fail; + } + + if (!(dinfo->skip_lba = strtol(tmp, NULL, 0))) { + LOGE("Invalid starting LBA (or zero): %s", tmp); + goto fail; + } + + /* Number of LBAs on disk */ + if (!(tmp = config_str(devroot, "num_lba", NULL))) { + LOGE("num_lba is required"); + goto fail; + } + dinfo->num_lba = strtoul(tmp, NULL, 0); + + if (!(partnode = config_find(devroot, "partitions"))) { + LOGE("Device must specify partition list"); + goto fail; + } + + if (load_partitions(partnode, dinfo)) + goto fail; + + return dinfo; + +fail: + if (dinfo->part_lst) + free(dinfo->part_lst); + if (dinfo->device) + free(dinfo->device); + free(dinfo); + return NULL; +} + +static int +sync_ptable(int fd) +{ + struct stat stat; + int rv; + + sync(); + + if (fstat(fd, &stat)) { + LOGE("Cannot stat, errno=%d.", errno); + return -1; + } + + if (S_ISBLK(stat.st_mode) && ((rv = ioctl(fd, BLKRRPART, NULL)) < 0)) { + LOGE("Could not re-read partition table. REBOOT!. (errno=%d)", errno); + return -1; + } + + return 0; +} + +/* This function verifies that the disk info provided is valid, and if so, + * returns an open file descriptor. + * + * This does not necessarily mean that it will later be successfully written + * though. If we use the pc-bios partitioning scheme, we must use extended + * partitions, which eat up some hd space. If the user manually provisioned + * every single partition, but did not account for the extra needed space, + * then we will later fail. + * + * TODO: Make validation more complete. + */ +static int +validate(struct disk_info *dinfo) +{ + int fd; + int sect_sz; + uint64_t disk_size; + uint64_t total_size; + int cnt; + struct stat stat; + + if (!dinfo) + return -1; + + if ((fd = open(dinfo->device, O_RDWR)) < 0) { + LOGE("Cannot open device '%s' (errno=%d)", dinfo->device, errno); + return -1; + } + + if (fstat(fd, &stat)) { + LOGE("Cannot stat file '%s', errno=%d.", dinfo->device, errno); + goto fail; + } + + + /* XXX: Some of the code below is kind of redundant and should probably + * be refactored a little, but it will do for now. */ + + /* Verify that we can operate on the device that was requested. + * We presently only support block devices and regular file images. */ + if (S_ISBLK(stat.st_mode)) { + /* get the sector size and make sure we agree */ + if (ioctl(fd, BLKSSZGET, §_sz) < 0) { + LOGE("Cannot get sector size (errno=%d)", errno); + goto fail; + } + + if (!sect_sz || sect_sz != dinfo->sect_size) { + LOGE("Device sector size is zero or sector sizes do not match!"); + goto fail; + } + + /* allow the user override the "disk size" if they provided num_lba */ + if (!dinfo->num_lba) { + if (ioctl(fd, BLKGETSIZE64, &disk_size) < 0) { + LOGE("Could not get block device size (errno=%d)", errno); + goto fail; + } + /* XXX: we assume that the disk has < 2^32 sectors :-) */ + dinfo->num_lba = (uint32_t)(disk_size / (uint64_t)dinfo->sect_size); + } else + disk_size = (uint64_t)dinfo->num_lba * (uint64_t)dinfo->sect_size; + } else if (S_ISREG(stat.st_mode)) { + LOGI("Requesting operation on a regular file, not block device."); + if (!dinfo->sect_size) { + LOGE("Sector size for regular file images cannot be zero"); + goto fail; + } + if (dinfo->num_lba) + disk_size = (uint64_t)dinfo->num_lba * (uint64_t)dinfo->sect_size; + else { + dinfo->num_lba = (uint32_t)(stat.st_size / dinfo->sect_size); + disk_size = (uint64_t)stat.st_size; + } + } else { + LOGE("Device does not refer to a regular file or a block device!"); + goto fail; + } + +#if 0 + LOGV("Device/file %s: size=%llu bytes, num_lba=%u, sect_size=%d", + dinfo->device, disk_size, dinfo->num_lba, dinfo->sect_size); +#endif + + /* since this is our offset into the disk, we start off with that as + * our size of needed partitions */ + total_size = dinfo->skip_lba * dinfo->sect_size; + + /* add up all the partition sizes and make sure it fits */ + for (cnt = 0; cnt < dinfo->num_parts; ++cnt) { + struct part_info *part = &dinfo->part_lst[cnt]; + if (part->len_kb != (uint32_t)-1) { + total_size += part->len_kb * 1024; + } else if (part->len_kb == 0) { + LOGE("Zero-size partition '%s' is invalid.", part->name); + goto fail; + } else { + /* the partition requests the rest of the disk. */ + if (cnt + 1 != dinfo->num_parts) { + LOGE("Only the last partition in the list can request to fill " + "the rest of disk."); + goto fail; + } + } + + if (part->type != PC_PART_TYPE_LINUX) { + LOGE("Unknown partition type (0x%x) encountered for partition " + "'%s'\n", part->type, part->name); + goto fail; + } + } + + /* only matters for disks, not files */ + if (S_ISBLK(stat.st_mode) && total_size > disk_size) { + LOGE("Total requested size of partitions (%llu) is greater than disk " + "size (%llu).", total_size, disk_size); + goto fail; + } + + return fd; + +fail: + close(fd); + return -1; +} + +static int +validate_and_config(struct disk_info *dinfo, int *fd, struct write_list **lst) +{ + *lst = NULL; + *fd = -1; + + if ((*fd = validate(dinfo)) < 0) + return 1; + + switch (dinfo->scheme) { + case PART_SCHEME_MBR: + *lst = config_mbr(dinfo); + return *lst == NULL; + case PART_SCHEME_GPT: + /* not supported yet */ + default: + LOGE("Uknown partition scheme."); + break; + } + + close(*fd); + *lst = NULL; + return 1; +} + +/* validate and process the disk layout configuration. + * This will cause an update to the partitions' start lba. + * + * Basically, this does the same thing as apply_disk_config in test mode, + * except that wlist_commit is not called to print out the data to be + * written. + */ +int +process_disk_config(struct disk_info *dinfo) +{ + struct write_list *lst; + int fd; + + if (validate_and_config(dinfo, &fd, &lst) != 0) + return 1; + + close(fd); + wlist_free(lst); + return 0; +} + + +int +apply_disk_config(struct disk_info *dinfo, int test) +{ + int fd; + struct write_list *wr_lst = NULL; + int rv; + + if (validate_and_config(dinfo, &fd, &wr_lst) != 0) { + LOGE("Configuration is invalid."); + goto fail; + } + + if ((rv = wlist_commit(fd, wr_lst, test)) >= 0) + rv = test ? 0 : sync_ptable(fd); + + close(fd); + wlist_free(wr_lst); + return rv; + +fail: + close(fd); + if (wr_lst) + wlist_free(wr_lst); + return 1; +} + +int +dump_disk_config(struct disk_info *dinfo) +{ + int cnt; + struct part_info *part; + + printf("Device: %s\n", dinfo->device); + printf("Scheme: "); + switch (dinfo->scheme) { + case PART_SCHEME_MBR: + printf("MBR"); + break; + case PART_SCHEME_GPT: + printf("GPT (unsupported)"); + break; + default: + printf("Unknown"); + break; + } + printf ("\n"); + + printf("Sector size: %d\n", dinfo->sect_size); + printf("Skip leading LBAs: %u\n", dinfo->skip_lba); + printf("Number of LBAs: %u\n", dinfo->num_lba); + printf("Partitions:\n"); + + for (cnt = 0; cnt < dinfo->num_parts; ++cnt) { + part = &dinfo->part_lst[cnt]; + printf("\tname = %s\n", part->name); + printf("\t\tflags = %s\n", + part->flags & PART_ACTIVE_FLAG ? "Active" : "None"); + printf("\t\ttype = %s\n", + part->type == PC_PART_TYPE_LINUX ? "Linux" : "Unknown"); + if (part->len_kb == (uint32_t)-1) + printf("\t\tlen = rest of disk\n"); + else + printf("\t\tlen = %uKB\n", part->len_kb); + } + printf("Total number of partitions: %d\n", cnt); + printf("\n"); + + return 0; +} + +struct part_info * +find_part(struct disk_info *dinfo, const char *name) +{ + struct part_info *pinfo; + int cnt; + + for (cnt = 0; cnt < dinfo->num_parts; ++cnt) { + pinfo = &dinfo->part_lst[cnt]; + if (!strcmp(pinfo->name, name)) + return pinfo; + } + + return NULL; +} + +/* NOTE: If the returned ptr is non-NULL, it must be freed by the caller. */ +char * +find_part_device(struct disk_info *dinfo, const char *name) +{ + switch (dinfo->scheme) { + case PART_SCHEME_MBR: + return find_mbr_part(dinfo, name); + case PART_SCHEME_GPT: + LOGE("GPT is presently not supported"); + break; + default: + LOGE("Unknown partition table scheme"); + break; + } + + return NULL; +} + + diff --git a/libdiskconfig/diskconfig.h b/libdiskconfig/diskconfig.h new file mode 100644 index 0000000..9ff8c4d --- /dev/null +++ b/libdiskconfig/diskconfig.h @@ -0,0 +1,120 @@ +/* libs/diskconfig/diskconfig.h + * + * 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. + */ + +#ifndef __LIBS_DISKCONFIG_H +#define __LIBS_DISKCONFIG_H + +#include + +#define MAX_NAME_LEN 512 +#define MAX_NUM_PARTS 16 + +/* known partition schemes */ +#define PART_SCHEME_MBR 0x1 +#define PART_SCHEME_GPT 0x2 + +/* PC Bios partition status */ +#define PC_PART_ACTIVE 0x80 +#define PC_PART_NORMAL 0x0 + +/* Known (rather, used by us) partition types */ +#define PC_PART_TYPE_LINUX 0x83 +#define PC_PART_TYPE_EXTENDED 0x05 + +#define PC_NUM_BOOT_RECORD_PARTS 4 + +#define PC_EBR_LOGICAL_PART 0 +#define PC_EBR_NEXT_PTR_PART 1 + +#define PC_BIOS_BOOT_SIG 0xAA55 + +#define PC_MBR_DISK_OFFSET 0 +#define PC_MBR_SIZE 512 + +#define PART_ACTIVE_FLAG 0x1 + +struct chs { + uint8_t cylinder; + uint8_t head; + uint8_t sector; +}; + +/* 16 byte pc partition descriptor that sits in MBR and EPBR. + * Note: multi-byte entities have little-endian layout on disk */ +struct pc_partition { + uint8_t status; /* byte 0 */ + struct chs start; /* bytes 1-3 */ + uint8_t type; /* byte 4 */ + struct chs end; /* bytes 5-7 */ + uint32_t start_lba; /* bytes 8-11 */ + uint32_t len_lba; /* bytes 12-15 */ +} __attribute__((__packed__)); + +struct pc_boot_record { + uint8_t code[440]; /* bytes 0-439 */ + uint32_t disk_sig; /* bytes 440-443 */ + uint16_t pad; /* bytes 444-445 */ + struct pc_partition ptable[PC_NUM_BOOT_RECORD_PARTS]; /* bytes 446-509 */ + uint16_t mbr_sig; /* bytes 510-511 */ +} __attribute__((__packed__)); + +struct part_info { + char *name; + uint8_t flags; + uint8_t type; + uint32_t len_kb; /* in 1K-bytes */ + uint32_t start_lba; /* the LBA where this partition begins */ +}; + +struct disk_info { + char *device; + uint8_t scheme; + int sect_size; /* expected sector size in bytes. MUST BE POWER OF 2 */ + uint32_t skip_lba; /* in sectors (1 unit of LBA) */ + uint32_t num_lba; /* the size of the disk in LBA units */ + struct part_info *part_lst; + int num_parts; +}; + +struct write_list { + struct write_list *next; + loff_t offset; + uint32_t len; + uint8_t data[0]; +}; + + +struct write_list *alloc_wl(uint32_t data_len); +void free_wl(struct write_list *item); +struct write_list *wlist_add(struct write_list **lst, struct write_list *item); +void wlist_free(struct write_list *lst); +int wlist_commit(int fd, struct write_list *lst, int test); + +struct disk_info *load_diskconfig(const char *fn, char *path_override); +int dump_disk_config(struct disk_info *dinfo); +int apply_disk_config(struct disk_info *dinfo, int test); +char *find_part_device(struct disk_info *dinfo, const char *name); +int process_disk_config(struct disk_info *dinfo); +struct part_info *find_part(struct disk_info *dinfo, const char *name); + +int write_raw_image(const char *dst, const char *src, loff_t offset, int test); + +/* For MBR partition schemes */ +struct write_list *config_mbr(struct disk_info *dinfo); +char *find_mbr_part(struct disk_info *dinfo, const char *name); + +#endif /* __LIBS_DISKCONFIG_H */ diff --git a/libdiskconfig/diskutils.c b/libdiskconfig/diskutils.c new file mode 100644 index 0000000..a63e2bc --- /dev/null +++ b/libdiskconfig/diskutils.c @@ -0,0 +1,117 @@ +/* libs/diskconfig/diskutils.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 "diskutils" + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "diskconfig.h" + +int +write_raw_image(const char *dst, const char *src, loff_t offset, int test) +{ + int dst_fd = -1; + int src_fd = -1; + uint8_t buffer[2048]; + int nr_bytes; + int tmp; + int done = 0; + uint64_t total = 0; + + LOGI("Writing RAW image '%s' to '%s' (offset=%llu)", src, dst, offset); + if ((src_fd = open(src, O_RDONLY)) < 0) { + LOGE("Could not open %s for reading (errno=%d).", src, errno); + goto fail; + } + + if (!test) { + if ((dst_fd = open(dst, O_RDWR)) < 0) { + LOGE("Could not open '%s' for read/write (errno=%d).", dst, errno); + goto fail; + } + + if (lseek64(dst_fd, offset, SEEK_SET) != offset) { + LOGE("Could not seek to offset %lld in %s.", offset, dst); + goto fail; + } + } + + while (!done) { + if ((nr_bytes = read(src_fd, buffer, sizeof(buffer))) < 0) { + /* XXX: Should we not even bother with EINTR? */ + if (errno == EINTR) + continue; + LOGE("Error (%d) while reading from '%s'", errno, src); + goto fail; + } + + if (!nr_bytes) { + /* we're done. */ + done = 1; + break; + } + + total += nr_bytes; + + /* skip the write loop if we're testing */ + if (test) + nr_bytes = 0; + + while (nr_bytes > 0) { + if ((tmp = write(dst_fd, buffer, nr_bytes)) < 0) { + /* XXX: Should we not even bother with EINTR? */ + if (errno == EINTR) + continue; + LOGE("Error (%d) while writing to '%s'", errno, dst); + goto fail; + } + if (!tmp) + continue; + nr_bytes -= tmp; + } + } + + if (!done) { + LOGE("Exited read/write loop without setting flag! WTF?!"); + goto fail; + } + + if (dst_fd >= 0) + fsync(dst_fd); + + LOGI("Wrote %llu bytes to %s @ %lld", total, dst, offset); + + close(src_fd); + if (dst_fd >= 0) + close(dst_fd); + return 0; + +fail: + if (dst_fd >= 0) + close(dst_fd); + if (src_fd >= 0) + close(src_fd); + return 1; +} diff --git a/libdiskconfig/dump_diskconfig.c b/libdiskconfig/dump_diskconfig.c new file mode 100644 index 0000000..fff19f5 --- /dev/null +++ b/libdiskconfig/dump_diskconfig.c @@ -0,0 +1,42 @@ +/* libs/diskconfig/dump_diskconfig.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 "dump_diskconfig" +#include + +#include + +#include "diskconfig.h" + +int +main(int argc, char *argv[]) +{ + struct disk_info *dinfo; + + if (argc < 2) { + LOGE("usage: %s ", argv[0]); + return 1; + } + + if (!(dinfo = load_diskconfig(argv[1], NULL))) + return 1; + + dump_disk_config(dinfo); + + return 0; +} + diff --git a/libdiskconfig/write_lst.c b/libdiskconfig/write_lst.c new file mode 100644 index 0000000..bd645b4 --- /dev/null +++ b/libdiskconfig/write_lst.c @@ -0,0 +1,92 @@ +/* libs/diskconfig/write_lst.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 "write_lst" +#include +#include +#include +#include +#include + +#include + +#include "diskconfig.h" + +struct write_list * +alloc_wl(uint32_t data_len) +{ + struct write_list *item; + + if (!(item = malloc(sizeof(struct write_list) + data_len))) { + LOGE("Unable to allocate memory."); + return NULL; + } + + item->len = data_len; + return item; +} + +void +free_wl(struct write_list *item) +{ + if (item) + free(item); +} + +struct write_list * +wlist_add(struct write_list **lst, struct write_list *item) +{ + item->next = (*lst); + *lst = item; + return item; +} + +void +wlist_free(struct write_list *lst) +{ + struct write_list *temp_wr; + while (lst) { + temp_wr = lst->next; + free_wl(lst); + lst = temp_wr; + } +} + +int +wlist_commit(int fd, struct write_list *lst, int test) +{ + for(; lst; lst = lst->next) { + if (lseek64(fd, lst->offset, SEEK_SET) != (loff_t)lst->offset) { + LOGE("Cannot seek to the specified position (%lld).", lst->offset); + goto fail; + } + + if (!test) { + if (write(fd, lst->data, lst->len) != (int)lst->len) { + LOGE("Failed writing %u bytes at position %lld.", lst->len, + lst->offset); + goto fail; + } + } else + LOGI("Would write %d bytes @ offset %lld.", lst->len, lst->offset); + } + + return 0; + +fail: + return -1; +} -- cgit v1.2.3