Commit 8fd8fc6c4d23e1a287fc64f817de23cabcf55b78
- Diff rendering mode:
- inline
- side by side
Android.mk
(26 / 0)
|   | |||
| 1 | LOCAL_PATH := $(call my-dir) | ||
| 2 | include $(CLEAR_VARS) | ||
| 3 | |||
| 4 | ifeq ($(TARGET_USE_DISKINSTALLER),true) | ||
| 5 | |||
| 6 | LOCAL_SRC_FILES := \ | ||
| 7 | installer.c | ||
| 8 | |||
| 9 | LOCAL_C_INCLUDES := $(LOCAL_PATH)/libdiskconfig | ||
| 10 | |||
| 11 | LOCAL_CFLAGS := -O2 -g -W -Wall -Werror | ||
| 12 | |||
| 13 | LOCAL_MODULE := diskinstaller | ||
| 14 | LOCAL_MODULE_TAGS := system_builder | ||
| 15 | |||
| 16 | LOCAL_STATIC_LIBRARIES := $(TARGET_DISK_CONFIG_LIB) | ||
| 17 | LOCAL_SYSTEM_SHARED_LIBRARIES := \ | ||
| 18 | libdiskconfig \ | ||
| 19 | libcutils \ | ||
| 20 | liblog \ | ||
| 21 | libc | ||
| 22 | |||
| 23 | include $(BUILD_EXECUTABLE) | ||
| 24 | |||
| 25 | include $(call first-makefiles-under,$(LOCAL_PATH)) | ||
| 26 | endif |
config.mk
(191 / 0)
|   | |||
| 1 | # note: requires x86 because we assume grub is the mbr bootloader. | ||
| 2 | ifeq ($(TARGET_ARCH),x86) | ||
| 3 | ifeq ($(TARGET_USE_DISKINSTALLER),true) | ||
| 4 | |||
| 5 | diskinstaller_root := bootable/diskinstaller | ||
| 6 | |||
| 7 | android_sysbase_modules := \ | ||
| 8 | libc \ | ||
| 9 | libcutils \ | ||
| 10 | libdl \ | ||
| 11 | liblog \ | ||
| 12 | libm \ | ||
| 13 | libstdc++ \ | ||
| 14 | linker \ | ||
| 15 | sh \ | ||
| 16 | toolbox \ | ||
| 17 | logcat \ | ||
| 18 | gdbserver \ | ||
| 19 | strace \ | ||
| 20 | netcfg | ||
| 21 | android_sysbase_files = \ | ||
| 22 | $(call module-installed-files,$(android_sysbase_modules)) | ||
| 23 | |||
| 24 | # $(1): source base dir | ||
| 25 | # $(2): target base dir | ||
| 26 | define sysbase-copy-files | ||
| 27 | $(hide) $(foreach _f,$(android_sysbase_files), \ | ||
| 28 | f=$(patsubst $(1)/%,$(2)/%,$(_f)); \ | ||
| 29 | mkdir -p `dirname $$f`; \ | ||
| 30 | echo "Copy: $$f" ; \ | ||
| 31 | cp -fR $(_f) $$f; \ | ||
| 32 | ) | ||
| 33 | endef | ||
| 34 | |||
| 35 | installer_base_modules := \ | ||
| 36 | libdiskconfig \ | ||
| 37 | libext2fs \ | ||
| 38 | libext2_com_err \ | ||
| 39 | libext2_e2p \ | ||
| 40 | libext2_blkid \ | ||
| 41 | libext2_uuid \ | ||
| 42 | libext2_profile \ | ||
| 43 | badblocks \ | ||
| 44 | resize2fs \ | ||
| 45 | tune2fs \ | ||
| 46 | mke2fs \ | ||
| 47 | e2fsck | ||
| 48 | installer_base_files = \ | ||
| 49 | $(call module-built-files,$(installer_base_modules)) | ||
| 50 | |||
| 51 | # $(1): source base dir | ||
| 52 | # $(2): target base dir | ||
| 53 | define installer-copy-modules | ||
| 54 | $(hide) $(foreach m,$(installer_base_modules), \ | ||
| 55 | src=$(firstword $(strip $(call module-built-files,$(m)))); \ | ||
| 56 | dest=$(patsubst $(strip $(1))/%,$(strip $(2))/%,\ | ||
| 57 | $(firstword $(strip $(call module-installed-files,$(m))))); \ | ||
| 58 | echo "Copy: $$src -> $$dest"; \ | ||
| 59 | mkdir -p `dirname $$dest`; \ | ||
| 60 | cp -fdp $$src $$dest; \ | ||
| 61 | ) | ||
| 62 | endef | ||
| 63 | |||
| 64 | # Build the installer ramdisk image | ||
| 65 | installer_initrc := $(diskinstaller_root)/init.rc | ||
| 66 | installer_kernel := $(INSTALLED_KERNEL_TARGET) | ||
| 67 | installer_ramdisk := $(TARGET_INSTALLER_OUT)/ramdisk-installer.img | ||
| 68 | installer_build_prop := $(INSTALLED_BUILD_PROP_TARGET) | ||
| 69 | installer_config := $(diskinstaller_root)/installer.conf | ||
| 70 | installer_binary := \ | ||
| 71 | $(call intermediates-dir-for,EXECUTABLES,diskinstaller)/diskinstaller | ||
| 72 | |||
| 73 | $(installer_ramdisk): $(diskinstaller_root)/config.mk \ | ||
| 74 | $(MKBOOTFS) \ | ||
| 75 | $(INSTALLED_RAMDISK_TARGET) \ | ||
| 76 | $(INSTALLED_BOOTIMAGE_TARGET) \ | ||
| 77 | $(TARGET_DISK_LAYOUT_CONFIG) \ | ||
| 78 | $(installer_binary) \ | ||
| 79 | $(installer_initrc) \ | ||
| 80 | $(installer_kernel) \ | ||
| 81 | $(installer_config) \ | ||
| 82 | $(android_sysbase_files) \ | ||
| 83 | $(installer_base_files) \ | ||
| 84 | $(installer_build_prop) | ||
| 85 | @echo ----- Making installer image ------ | ||
| 86 | rm -rf $(TARGET_INSTALLER_OUT) | ||
| 87 | mkdir -p $(TARGET_INSTALLER_OUT) | ||
| 88 | mkdir -p $(TARGET_INSTALLER_ROOT_OUT) | ||
| 89 | mkdir -p $(TARGET_INSTALLER_ROOT_OUT)/sbin | ||
| 90 | mkdir -p $(TARGET_INSTALLER_ROOT_OUT)/data | ||
| 91 | mkdir -p $(TARGET_INSTALLER_SYSTEM_OUT) | ||
| 92 | mkdir -p $(TARGET_INSTALLER_SYSTEM_OUT)/etc | ||
| 93 | mkdir -p $(TARGET_INSTALLER_SYSTEM_OUT)/bin | ||
| 94 | @echo Copying baseline ramdisk... | ||
| 95 | cp -fR $(TARGET_ROOT_OUT) $(TARGET_INSTALLER_OUT) | ||
| 96 | @echo Copying sysbase files... | ||
| 97 | $(call sysbase-copy-files,$(TARGET_OUT),$(TARGET_INSTALLER_SYSTEM_OUT)) | ||
| 98 | @echo Copying installer base files... | ||
| 99 | $(call installer-copy-modules,$(TARGET_OUT),\ | ||
| 100 | $(TARGET_INSTALLER_SYSTEM_OUT)) | ||
| 101 | @echo Modifying ramdisk contents... | ||
| 102 | cp -f $(installer_initrc) $(TARGET_INSTALLER_ROOT_OUT)/ | ||
| 103 | cp -f $(TARGET_DISK_LAYOUT_CONFIG) \ | ||
| 104 | $(TARGET_INSTALLER_SYSTEM_OUT)/etc/disk_layout.conf | ||
| 105 | cp -f $(installer_config) \ | ||
| 106 | $(TARGET_INSTALLER_SYSTEM_OUT)/etc/installer.conf | ||
| 107 | cp -f $(installer_binary) $(TARGET_INSTALLER_SYSTEM_OUT)/bin/installer | ||
| 108 | $(hide) chmod ug+rw $(TARGET_INSTALLER_ROOT_OUT)/default.prop | ||
| 109 | cat $(installer_build_prop) >> $(TARGET_INSTALLER_ROOT_OUT)/default.prop | ||
| 110 | $(MKBOOTFS) $(TARGET_INSTALLER_ROOT_OUT) | gzip > $(installer_ramdisk) | ||
| 111 | @echo ----- Made installer ramdisk -[ $@ ]- | ||
| 112 | |||
| 113 | ###################################################################### | ||
| 114 | # Now the installer boot image which includes the kernel and the ramdisk | ||
| 115 | internal_installerimage_args := \ | ||
| 116 | --kernel $(installer_kernel) \ | ||
| 117 | --ramdisk $(installer_ramdisk) | ||
| 118 | |||
| 119 | internal_installerimage_files := \ | ||
| 120 | $(filter-out --%,$(internal_installerimage_args)) | ||
| 121 | |||
| 122 | BOARD_KERNEL_CMDLINE := $(strip $(BOARD_KERNEL_CMDLINE)) | ||
| 123 | ifdef BOARD_KERNEL_CMDLINE | ||
| 124 | internal_installerimage_args += --cmdline "$(BOARD_KERNEL_CMDLINE)" | ||
| 125 | endif | ||
| 126 | |||
| 127 | installer_tmp_img := $(TARGET_INSTALLER_OUT)/installer_tmp.img | ||
| 128 | tmp_dir_for_inst_image := \ | ||
| 129 | $(call intermediates-dir-for,EXECUTABLES,installer_img)/installer_img | ||
| 130 | internal_installerimage_args += --tmpdir $(tmp_dir_for_inst_image) | ||
| 131 | internal_installerimage_args += --genext2fs $(MKEXT2IMG) | ||
| 132 | $(installer_tmp_img): $(MKEXT2IMG) $(internal_installerimage_files) | ||
| 133 | $(call pretty,"Target installer image: $@") | ||
| 134 | $(hide) $(MKEXT2BOOTIMG) $(internal_installerimage_args) --output $@ | ||
| 135 | |||
| 136 | ###################################################################### | ||
| 137 | # Now make a data image that contains all the target image files for the | ||
| 138 | # installer. | ||
| 139 | bootldr_bin := $(PRODUCT_OUT)/grub/grub.bin | ||
| 140 | installer_target_data_files := \ | ||
| 141 | $(INSTALLED_BOOTIMAGE_TARGET) \ | ||
| 142 | $(INSTALLED_SYSTEMIMAGE) \ | ||
| 143 | $(INSTALLED_USERDATAIMAGE_TARGET) \ | ||
| 144 | $(bootldr_bin) | ||
| 145 | |||
| 146 | installer_data_img := $(TARGET_INSTALLER_OUT)/installer_data.img | ||
| 147 | $(installer_data_img): $(diskinstaller_root)/config.mk \ | ||
| 148 | $(installer_target_data_files) \ | ||
| 149 | $(MKEXT2IMG) \ | ||
| 150 | $(installer_ramdisk) | ||
| 151 | @echo --- Making installer data image ------ | ||
| 152 | mkdir -p $(TARGET_INSTALLER_OUT) | ||
| 153 | mkdir -p $(TARGET_INSTALLER_OUT)/data | ||
| 154 | cp -f $(bootldr_bin) $(TARGET_INSTALLER_OUT)/data/bootldr.bin | ||
| 155 | cp -f $(INSTALLED_BOOTIMAGE_TARGET) $(TARGET_INSTALLER_OUT)/data/boot.img | ||
| 156 | cp -f $(INSTALLED_SYSTEMIMAGE) \ | ||
| 157 | $(TARGET_INSTALLER_OUT)/data/system.img | ||
| 158 | cp -f $(INSTALLED_USERDATAIMAGE_TARGET) \ | ||
| 159 | $(TARGET_INSTALLER_OUT)/data/userdata.img | ||
| 160 | $(call build-userimage-ext2-target,$(TARGET_INSTALLER_OUT)/data,$@,\ | ||
| 161 | inst_data,) | ||
| 162 | @echo --- Finished installer data image -[ $@ ]- | ||
| 163 | |||
| 164 | ###################################################################### | ||
| 165 | # now combine the installer image with the grub bootloader | ||
| 166 | grub_bin := $(PRODUCT_OUT)/grub/grub.bin | ||
| 167 | installer_layout := $(diskinstaller_root)/installer_img_layout.conf | ||
| 168 | edit_mbr := $(HOST_OUT_EXECUTABLES)/editdisklbl | ||
| 169 | |||
| 170 | INSTALLED_DISKINSTALLERIMAGE_TARGET := $(PRODUCT_OUT)/installer.img | ||
| 171 | $(INSTALLED_DISKINSTALLERIMAGE_TARGET): \ | ||
| 172 | $(installer_tmp_img) \ | ||
| 173 | $(installer_data_img) \ | ||
| 174 | $(grub_bin) \ | ||
| 175 | $(edit_mbr) \ | ||
| 176 | $(installer_layout) | ||
| 177 | @echo "Creating bootable installer image: $@" | ||
| 178 | @rm -f $@ | ||
| 179 | @cat $(grub_bin) > $@ | ||
| 180 | @$(edit_mbr) -l $(installer_layout) -i $@ \ | ||
| 181 | inst_boot=$(installer_tmp_img) \ | ||
| 182 | inst_data=$(installer_data_img) | ||
| 183 | @echo "Done with bootable installer image -[ $@ ]-" | ||
| 184 | |||
| 185 | else # ! TARGET_USE_DISKINSTALLER | ||
| 186 | INSTALLED_DISKINSTALLERIMAGE_TARGET := | ||
| 187 | endif | ||
| 188 | endif # TARGET_ARCH == x86 | ||
| 189 | |||
| 190 | .PHONY: installer_img | ||
| 191 | installer_img: $(INSTALLED_DISKINSTALLERIMAGE_TARGET) |
editdisklbl/Android.mk
(22 / 0)
|   | |||
| 1 | LOCAL_PATH := $(call my-dir) | ||
| 2 | |||
| 3 | include $(CLEAR_VARS) | ||
| 4 | |||
| 5 | ifneq ($(TARGET_SIMULATOR),true) | ||
| 6 | ifeq ($(TARGET_ARCH),x86) | ||
| 7 | |||
| 8 | LOCAL_SRC_FILES := \ | ||
| 9 | editdisklbl.c | ||
| 10 | |||
| 11 | LOCAL_C_INCLUDES := \ | ||
| 12 | $(LOCAL_PATH)/../libdiskconfig | ||
| 13 | |||
| 14 | LOCAL_CFLAGS := -O2 -g -W -Wall -Werror | ||
| 15 | |||
| 16 | LOCAL_MODULE := editdisklbl | ||
| 17 | LOCAL_STATIC_LIBRARIES := libdiskconfig_host libcutils liblog | ||
| 18 | |||
| 19 | include $(BUILD_HOST_EXECUTABLE) | ||
| 20 | |||
| 21 | endif # TARGET_ARCH == x86 | ||
| 22 | endif # !TARGET_SIMULATOR |
editdisklbl/editdisklbl.c
(201 / 0)
|   | |||
| 1 | /* tools/editdisklbl/editdisklbl.c | ||
| 2 | * | ||
| 3 | * Copyright 2008, The Android Open Source Project | ||
| 4 | * | ||
| 5 | * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 6 | * you may not use this file except in compliance with the License. | ||
| 7 | * You may obtain a copy of the License at | ||
| 8 | * | ||
| 9 | * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 10 | * | ||
| 11 | * Unless required by applicable law or agreed to in writing, software | ||
| 12 | * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 14 | * See the License for the specific language governing permissions and | ||
| 15 | * limitations under the License. | ||
| 16 | */ | ||
| 17 | |||
| 18 | #define __USE_LARGEFILE64 | ||
| 19 | #define __USE_FILE_OFFSET64 | ||
| 20 | #include <errno.h> | ||
| 21 | #include <fcntl.h> | ||
| 22 | #include <stdio.h> | ||
| 23 | #include <stdlib.h> | ||
| 24 | #include <string.h> | ||
| 25 | #include <unistd.h> | ||
| 26 | #include <sys/stat.h> | ||
| 27 | #include <sys/types.h> | ||
| 28 | |||
| 29 | #include "diskconfig.h" | ||
| 30 | |||
| 31 | /* give us some room */ | ||
| 32 | #define EXTRA_LBAS 100 | ||
| 33 | |||
| 34 | static struct pf_map { | ||
| 35 | struct part_info *pinfo; | ||
| 36 | const char *filename; | ||
| 37 | } part_file_map[MAX_NUM_PARTS] = { {0, 0} }; | ||
| 38 | |||
| 39 | static int | ||
| 40 | usage(void) | ||
| 41 | { | ||
| 42 | fprintf(stderr, | ||
| 43 | "\nusage: editdisklbl <options> part1=file1 [part2=file2,...]\n" | ||
| 44 | "Where options can be one of:\n" | ||
| 45 | "\t\t-l <layout conf> -- The image layout config file.\n" | ||
| 46 | "\t\t-i <image file> -- The image file to edit.\n" | ||
| 47 | "\t\t-t -- Test mode (optional)\n" | ||
| 48 | "\t\t-v -- Be verbose\n" | ||
| 49 | "\t\t-h -- This message (optional)\n" | ||
| 50 | ); | ||
| 51 | return 1; | ||
| 52 | } | ||
| 53 | |||
| 54 | static int | ||
| 55 | parse_args(int argc, char *argv[], struct disk_info **dinfo, int *test, | ||
| 56 | int *verbose) | ||
| 57 | { | ||
| 58 | char *layout_conf = NULL; | ||
| 59 | char *img_file = NULL; | ||
| 60 | struct stat filestat; | ||
| 61 | int x; | ||
| 62 | int update_lba = 0; | ||
| 63 | |||
| 64 | while ((x = getopt (argc, argv, "thl:i:")) != EOF) { | ||
| 65 | switch (x) { | ||
| 66 | case 'h': | ||
| 67 | return usage(); | ||
| 68 | case 'l': | ||
| 69 | layout_conf = optarg; | ||
| 70 | break; | ||
| 71 | case 't': | ||
| 72 | *test = 1; | ||
| 73 | break; | ||
| 74 | case 'i': | ||
| 75 | img_file = optarg; | ||
| 76 | break; | ||
| 77 | case 'v': | ||
| 78 | *verbose = 1; | ||
| 79 | break; | ||
| 80 | default: | ||
| 81 | fprintf(stderr, "Unknown argument: %c\n", (char)optopt); | ||
| 82 | return usage(); | ||
| 83 | } | ||
| 84 | } | ||
| 85 | |||
| 86 | if (!img_file || !layout_conf) { | ||
| 87 | fprintf(stderr, "Image filename and configuration file are required\n"); | ||
| 88 | return usage(); | ||
| 89 | } | ||
| 90 | |||
| 91 | /* we'll need to parse the command line later for partition-file | ||
| 92 | * mappings, so make sure there's at least something there */ | ||
| 93 | if (optind >= argc) { | ||
| 94 | fprintf(stderr, "Must provide partition -> file mappings\n"); | ||
| 95 | return usage(); | ||
| 96 | } | ||
| 97 | |||
| 98 | if (stat(img_file, &filestat)) { | ||
| 99 | perror("Cannot stat image file"); | ||
| 100 | return 1; | ||
| 101 | } | ||
| 102 | |||
| 103 | /* make sure we don't screw up and write to a block device on the host | ||
| 104 | * and wedge things. I just don't trust myself. */ | ||
| 105 | if (!S_ISREG(filestat.st_mode)) { | ||
| 106 | fprintf(stderr, "This program should only be used on regular files."); | ||
| 107 | return 1; | ||
| 108 | } | ||
| 109 | |||
| 110 | /* load the disk layout file */ | ||
| 111 | if (!(*dinfo = load_diskconfig(layout_conf, img_file))) { | ||
| 112 | fprintf(stderr, "Errors encountered while loading disk conf file %s", | ||
| 113 | layout_conf); | ||
| 114 | return 1; | ||
| 115 | } | ||
| 116 | |||
| 117 | if ((*dinfo)->num_lba == 0) { | ||
| 118 | (*dinfo)->num_lba = (*dinfo)->skip_lba + EXTRA_LBAS; | ||
| 119 | update_lba = 1; | ||
| 120 | } | ||
| 121 | |||
| 122 | /* parse the filename->partition mappings from the command line and patch | ||
| 123 | * up a loaded config file's partition table entries to have | ||
| 124 | * length == filesize */ | ||
| 125 | x = 0; | ||
| 126 | while (optind < argc) { | ||
| 127 | char *pair = argv[optind++]; | ||
| 128 | char *part_name; | ||
| 129 | struct part_info *pinfo; | ||
| 130 | struct stat tmp_stat; | ||
| 131 | |||
| 132 | if (x >= MAX_NUM_PARTS) { | ||
| 133 | fprintf(stderr, "Error: Too many partitions specified (%d)!\n", x); | ||
| 134 | return 1; | ||
| 135 | } | ||
| 136 | |||
| 137 | if (!(part_name = strsep(&pair, "=")) || !pair || !(*pair)) { | ||
| 138 | fprintf(stderr, "Error parsing partition mappings\n"); | ||
| 139 | return usage(); | ||
| 140 | } | ||
| 141 | |||
| 142 | if (!(pinfo = find_part(*dinfo, part_name))) { | ||
| 143 | fprintf(stderr, "Partition '%s' not found.\n", part_name); | ||
| 144 | return 1; | ||
| 145 | } | ||
| 146 | |||
| 147 | /* here pair points to the filename (after the '=') */ | ||
| 148 | part_file_map[x].pinfo = pinfo; | ||
| 149 | part_file_map[x++].filename = pair; | ||
| 150 | |||
| 151 | if (stat(pair, &tmp_stat) < 0) { | ||
| 152 | fprintf(stderr, "Could not stat file: %s\n", pair); | ||
| 153 | return 1; | ||
| 154 | } | ||
| 155 | |||
| 156 | pinfo->len_kb = (uint32_t) ((tmp_stat.st_size + 1023) >> 10); | ||
| 157 | if (update_lba) | ||
| 158 | (*dinfo)->num_lba += | ||
| 159 | ((uint64_t)pinfo->len_kb * 1024) / (*dinfo)->sect_size; | ||
| 160 | printf("Updated %s length to be %uKB\n", pinfo->name, pinfo->len_kb); | ||
| 161 | } | ||
| 162 | |||
| 163 | return 0; | ||
| 164 | } | ||
| 165 | |||
| 166 | int | ||
| 167 | main(int argc, char *argv[]) | ||
| 168 | { | ||
| 169 | struct disk_info *dinfo = NULL; | ||
| 170 | int test = 0; | ||
| 171 | int verbose = 0; | ||
| 172 | int cnt; | ||
| 173 | |||
| 174 | if (parse_args(argc, argv, &dinfo, &test, &verbose)) | ||
| 175 | return 1; | ||
| 176 | |||
| 177 | if (verbose) | ||
| 178 | dump_disk_config(dinfo); | ||
| 179 | |||
| 180 | if (test) | ||
| 181 | printf("Test mode enabled. Actions will not be committed to disk!\n"); | ||
| 182 | |||
| 183 | if (apply_disk_config(dinfo, test)) { | ||
| 184 | fprintf(stderr, "Could not apply disk configuration!\n"); | ||
| 185 | return 1; | ||
| 186 | } | ||
| 187 | |||
| 188 | printf("Copying images to specified partition offsets\n"); | ||
| 189 | /* now copy the images to their appropriate locations on disk */ | ||
| 190 | for (cnt = 0; cnt < MAX_NUM_PARTS && part_file_map[cnt].pinfo; ++cnt) { | ||
| 191 | loff_t offs = part_file_map[cnt].pinfo->start_lba * dinfo->sect_size; | ||
| 192 | const char *dest_fn = dinfo->device; | ||
| 193 | if (write_raw_image(dest_fn, part_file_map[cnt].filename, offs, test)) { | ||
| 194 | fprintf(stderr, "Could not write images after editing label.\n"); | ||
| 195 | return 1; | ||
| 196 | } | ||
| 197 | } | ||
| 198 | printf("File edit complete. Wrote %d images.\n", cnt); | ||
| 199 | |||
| 200 | return 0; | ||
| 201 | } |
init.rc
(30 / 0)
|   | |||
| 1 | on init | ||
| 2 | export PATH /sbin:/system/sbin:/system/bin | ||
| 3 | export ANDROID_ROOT /system | ||
| 4 | export ANDROID_DATA /data | ||
| 5 | |||
| 6 | symlink /system/etc /etc | ||
| 7 | |||
| 8 | mkdir /data | ||
| 9 | mkdir /cache | ||
| 10 | mount /tmp /tmp tmpfs | ||
| 11 | |||
| 12 | # mount the installer needed partitions | ||
| 13 | # mount ext2 /dev/block/sdb2 /data ro | ||
| 14 | |||
| 15 | on boot | ||
| 16 | ifup lo | ||
| 17 | hostname localhost | ||
| 18 | domainname localdomain | ||
| 19 | |||
| 20 | class_start default | ||
| 21 | |||
| 22 | service installer /system/bin/installer -p /dev/block/sdb2 | ||
| 23 | console | ||
| 24 | oneshot | ||
| 25 | |||
| 26 | service logcat /system/bin/logcat | ||
| 27 | console | ||
| 28 | |||
| 29 | service console /system/bin/sh | ||
| 30 | console |
installer.c
(477 / 0)
|   | |||
| 1 | /* commands/sysloader/installer/installer.c | ||
| 2 | * | ||
| 3 | * Copyright 2008, The Android Open Source Project | ||
| 4 | * | ||
| 5 | * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 6 | * you may not use this file except in compliance with the License. | ||
| 7 | * You may obtain a copy of the License at | ||
| 8 | * | ||
| 9 | * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 10 | * | ||
| 11 | * Unless required by applicable law or agreed to in writing, software | ||
| 12 | * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 14 | * See the License for the specific language governing permissions and | ||
| 15 | * limitations under the License. | ||
| 16 | */ | ||
| 17 | |||
| 18 | #define LOG_TAG "installer" | ||
| 19 | |||
| 20 | #include <sys/types.h> | ||
| 21 | #include <errno.h> | ||
| 22 | #include <fcntl.h> | ||
| 23 | #include <stdint.h> | ||
| 24 | #include <stdio.h> | ||
| 25 | #include <stdlib.h> | ||
| 26 | #include <string.h> | ||
| 27 | #include <unistd.h> | ||
| 28 | #include <sys/mount.h> | ||
| 29 | #include <sys/stat.h> | ||
| 30 | #include <sys/wait.h> | ||
| 31 | |||
| 32 | |||
| 33 | #include <cutils/config_utils.h> | ||
| 34 | #include <cutils/log.h> | ||
| 35 | |||
| 36 | #include "diskconfig.h" | ||
| 37 | #include "installer.h" | ||
| 38 | |||
| 39 | #define MKE2FS_BIN "/system/bin/mke2fs" | ||
| 40 | #define E2FSCK_BIN "/system/bin/e2fsck" | ||
| 41 | #define TUNE2FS_BIN "/system/bin/tune2fs" | ||
| 42 | #define RESIZE2FS_BIN "/system/bin/resize2fs" | ||
| 43 | |||
| 44 | static int | ||
| 45 | usage(void) | ||
| 46 | { | ||
| 47 | fprintf(stderr, "Usage: %s\n", LOG_TAG); | ||
| 48 | fprintf(stderr, "\t-c <path> - Path to installer conf file " | ||
| 49 | "(/system/etc/installer.conf)\n"); | ||
| 50 | fprintf(stderr, "\t-l <path> - Path to device disk layout conf file " | ||
| 51 | "(/system/etc/disk_layout.conf)\n"); | ||
| 52 | fprintf(stderr, "\t-h - This help message\n"); | ||
| 53 | fprintf(stderr, "\t-d - Dump the compiled in partition info.\n"); | ||
| 54 | fprintf(stderr, "\t-p <path> - Path to device that should be mounted" | ||
| 55 | " to /data.\n"); | ||
| 56 | fprintf(stderr, "\t-t - Test mode. Don't write anything to disk.\n"); | ||
| 57 | return 1; | ||
| 58 | } | ||
| 59 | |||
| 60 | static cnode * | ||
| 61 | read_conf_file(const char *fn) | ||
| 62 | { | ||
| 63 | cnode *root = config_node("", ""); | ||
| 64 | config_load_file(root, fn); | ||
| 65 | |||
| 66 | if (root->first_child == NULL) { | ||
| 67 | LOGE("Could not read config file %s", fn); | ||
| 68 | return NULL; | ||
| 69 | } | ||
| 70 | |||
| 71 | return root; | ||
| 72 | } | ||
| 73 | |||
| 74 | static int | ||
| 75 | exec_cmd(const char *cmd, ...) /* const char *arg, ...) */ | ||
| 76 | { | ||
| 77 | va_list ap; | ||
| 78 | int size = 0; | ||
| 79 | char *str; | ||
| 80 | char *outbuf; | ||
| 81 | int rv; | ||
| 82 | |||
| 83 | /* compute the size for the command buffer */ | ||
| 84 | size = strlen(cmd) + 1; | ||
| 85 | va_start(ap, cmd); | ||
| 86 | while ((str = va_arg(ap, char *))) { | ||
| 87 | size += strlen(str) + 1; /* need room for the space separator */ | ||
| 88 | } | ||
| 89 | va_end(ap); | ||
| 90 | |||
| 91 | if (!(outbuf = malloc(size + 1))) { | ||
| 92 | LOGE("Can't allocate memory to exec cmd"); | ||
| 93 | return -1; | ||
| 94 | } | ||
| 95 | |||
| 96 | /* this is a bit inefficient, but is trivial, and works */ | ||
| 97 | strcpy(outbuf, cmd); | ||
| 98 | va_start(ap, cmd); | ||
| 99 | while ((str = va_arg(ap, char *))) { | ||
| 100 | strcat(outbuf, " "); | ||
| 101 | strcat(outbuf, str); | ||
| 102 | } | ||
| 103 | va_end(ap); | ||
| 104 | |||
| 105 | LOGI("Executing: %s", outbuf); | ||
| 106 | rv = system(outbuf); | ||
| 107 | free(outbuf); | ||
| 108 | if (rv < 0) { | ||
| 109 | LOGI("Error while trying to execute '%s'", cmd); | ||
| 110 | return -1; | ||
| 111 | } | ||
| 112 | rv = WEXITSTATUS(rv); | ||
| 113 | LOGI("Done executing %s (%d)", outbuf, rv); | ||
| 114 | return rv; | ||
| 115 | } | ||
| 116 | |||
| 117 | |||
| 118 | static int | ||
| 119 | do_fsck(const char *dst, int force) | ||
| 120 | { | ||
| 121 | int rv; | ||
| 122 | const char *opts = force ? "-fy" : "-y"; | ||
| 123 | |||
| 124 | |||
| 125 | LOGI("Running e2fsck... (force=%d) This MAY take a while.", force); | ||
| 126 | if ((rv = exec_cmd(E2FSCK_BIN, "-C 0", opts, dst, NULL)) < 0) | ||
| 127 | return 1; | ||
| 128 | if (rv >= 4) { | ||
| 129 | LOGE("Error while running e2fsck: %d", rv); | ||
| 130 | return 1; | ||
| 131 | } | ||
| 132 | sync(); | ||
| 133 | LOGI("e2fsck succeeded (exit code: %d)", rv); | ||
| 134 | |||
| 135 | return 0; | ||
| 136 | } | ||
| 137 | |||
| 138 | static int | ||
| 139 | process_ext2_image(const char *dst, const char *src, uint32_t flags, int test) | ||
| 140 | { | ||
| 141 | int rv; | ||
| 142 | |||
| 143 | /* First, write the image to disk. */ | ||
| 144 | if (write_raw_image(dst, src, 0, test)) | ||
| 145 | return 1; | ||
| 146 | |||
| 147 | if (test) | ||
| 148 | return 0; | ||
| 149 | |||
| 150 | /* Next, let's e2fsck the fs to make sure it got written ok, and | ||
| 151 | * everything is peachy */ | ||
| 152 | if (do_fsck(dst, 1)) | ||
| 153 | return 1; | ||
| 154 | |||
| 155 | /* set the mount count to 1 so that 1st mount on boot doesn't complain */ | ||
| 156 | if ((rv = exec_cmd(TUNE2FS_BIN, "-C", "1", dst, NULL)) < 0) | ||
| 157 | return 1; | ||
| 158 | if (rv) { | ||
| 159 | LOGE("Error while running tune2fs: %d", rv); | ||
| 160 | return 1; | ||
| 161 | } | ||
| 162 | |||
| 163 | /* If the user requested that we resize, let's do it now */ | ||
| 164 | if (flags & INSTALL_FLAG_RESIZE) { | ||
| 165 | if ((rv = exec_cmd(RESIZE2FS_BIN, "-F", dst, NULL)) < 0) | ||
| 166 | return 1; | ||
| 167 | if (rv) { | ||
| 168 | LOGE("Error while running resize2fs: %d", rv); | ||
| 169 | return 1; | ||
| 170 | } | ||
| 171 | sync(); | ||
| 172 | if (do_fsck(dst, 0)) | ||
| 173 | return 1; | ||
| 174 | } | ||
| 175 | |||
| 176 | /* make this an ext3 fs? */ | ||
| 177 | if (flags & INSTALL_FLAG_ADDJOURNAL) { | ||
| 178 | if ((rv = exec_cmd(TUNE2FS_BIN, "-j", dst, NULL)) < 0) | ||
| 179 | return 1; | ||
| 180 | if (rv) { | ||
| 181 | LOGE("Error while running tune2fs: %d", rv); | ||
| 182 | return 1; | ||
| 183 | } | ||
| 184 | sync(); | ||
| 185 | if (do_fsck(dst, 0)) | ||
| 186 | return 1; | ||
| 187 | } | ||
| 188 | |||
| 189 | return 0; | ||
| 190 | } | ||
| 191 | |||
| 192 | |||
| 193 | /* TODO: PLEASE break up this function into several functions that just | ||
| 194 | * do what they need with the image node. Many of them will end up | ||
| 195 | * looking at same strings, but it will be sooo much cleaner */ | ||
| 196 | static int | ||
| 197 | process_image_node(cnode *img, struct disk_info *dinfo, int test) | ||
| 198 | { | ||
| 199 | struct part_info *pinfo = NULL; | ||
| 200 | loff_t offset = (loff_t)-1; | ||
| 201 | const char *filename = NULL; | ||
| 202 | char *dest_part = NULL; | ||
| 203 | const char *tmp; | ||
| 204 | uint32_t flags = 0; | ||
| 205 | uint8_t type = 0; | ||
| 206 | int rv; | ||
| 207 | int func_ret = 1; | ||
| 208 | |||
| 209 | filename = config_str(img, "filename", NULL); | ||
| 210 | |||
| 211 | /* process the 'offset' image parameter */ | ||
| 212 | if ((tmp = config_str(img, "offset", NULL)) != NULL) | ||
| 213 | offset = strtoull(tmp, NULL, 0); | ||
| 214 | |||
| 215 | /* process the 'partition' image parameter */ | ||
| 216 | if ((tmp = config_str(img, "partition", NULL)) != NULL) { | ||
| 217 | if (offset != (loff_t)-1) { | ||
| 218 | LOGE("Cannot specify the partition name AND an offset for %s", | ||
| 219 | img->name); | ||
| 220 | goto fail; | ||
| 221 | } | ||
| 222 | |||
| 223 | if (!(pinfo = find_part(dinfo, tmp))) { | ||
| 224 | LOGE("Cannot find partition %s while processing %s", | ||
| 225 | tmp, img->name); | ||
| 226 | goto fail; | ||
| 227 | } | ||
| 228 | |||
| 229 | if (!(dest_part = find_part_device(dinfo, pinfo->name))) { | ||
| 230 | LOGE("Could not get the device name for partition %s while" | ||
| 231 | " processing image %s", pinfo->name, img->name); | ||
| 232 | goto fail; | ||
| 233 | } | ||
| 234 | offset = pinfo->start_lba * dinfo->sect_size; | ||
| 235 | } | ||
| 236 | |||
| 237 | /* process the 'mkfs' parameter */ | ||
| 238 | if ((tmp = config_str(img, "mkfs", NULL)) != NULL) { | ||
| 239 | char *journal_opts; | ||
| 240 | char vol_lbl[16]; /* ext2/3 has a 16-char volume label */ | ||
| 241 | |||
| 242 | if (!pinfo) { | ||
| 243 | LOGE("Target partition required for mkfs for '%s'", img->name); | ||
| 244 | goto fail; | ||
| 245 | } else if (filename) { | ||
| 246 | LOGE("Providing filename and mkfs parameters is meaningless"); | ||
| 247 | goto fail; | ||
| 248 | } | ||
| 249 | |||
| 250 | if (!strcmp(tmp, "ext2")) | ||
| 251 | journal_opts = ""; | ||
| 252 | else if (!strcmp(tmp, "ext3")) | ||
| 253 | journal_opts = "-j"; | ||
| 254 | else { | ||
| 255 | LOGE("Unknown filesystem type for mkfs: %s", tmp); | ||
| 256 | goto fail; | ||
| 257 | } | ||
| 258 | |||
| 259 | /* put the partition name as the volume label */ | ||
| 260 | strncpy(vol_lbl, pinfo->name, sizeof(vol_lbl)); | ||
| 261 | |||
| 262 | /* since everything checked out, lets make the fs, and return since | ||
| 263 | * we don't need to do anything else */ | ||
| 264 | rv = exec_cmd(MKE2FS_BIN, "-L", vol_lbl, journal_opts, dest_part, NULL); | ||
| 265 | if (rv < 0) | ||
| 266 | goto fail; | ||
| 267 | else if (rv > 0) { | ||
| 268 | LOGE("Error while running mke2fs: %d", rv); | ||
| 269 | goto fail; | ||
| 270 | } | ||
| 271 | sync(); | ||
| 272 | if (do_fsck(dest_part, 0)) | ||
| 273 | goto fail; | ||
| 274 | goto done; | ||
| 275 | } | ||
| 276 | |||
| 277 | /* since we didn't mkfs above, all the rest of the options assume | ||
| 278 | * there's a filename involved */ | ||
| 279 | if (!filename) { | ||
| 280 | LOGE("Filename is required for image %s", img->name); | ||
| 281 | goto fail; | ||
| 282 | } | ||
| 283 | |||
| 284 | /* process the 'flags' image parameter */ | ||
| 285 | if ((tmp = config_str(img, "flags", NULL)) != NULL) { | ||
| 286 | char *flagstr, *flagstr_orig; | ||
| 287 | |||
| 288 | if (!(flagstr = flagstr_orig = strdup(tmp))) { | ||
| 289 | LOGE("Cannot allocate memory for dup'd flags string"); | ||
| 290 | goto fail; | ||
| 291 | } | ||
| 292 | while ((tmp = strsep(&flagstr, ","))) { | ||
| 293 | if (!strcmp(tmp, "resize")) | ||
| 294 | flags |= INSTALL_FLAG_RESIZE; | ||
| 295 | else if (!strcmp(tmp, "addjournal")) | ||
| 296 | flags |= INSTALL_FLAG_ADDJOURNAL; | ||
| 297 | else { | ||
| 298 | LOGE("Unknown flag '%s' for image %s", tmp, img->name); | ||
| 299 | free(flagstr_orig); | ||
| 300 | goto fail; | ||
| 301 | } | ||
| 302 | } | ||
| 303 | free(flagstr_orig); | ||
| 304 | } | ||
| 305 | |||
| 306 | /* process the 'type' image parameter */ | ||
| 307 | if (!(tmp = config_str(img, "type", NULL))) { | ||
| 308 | LOGE("Type is required for image %s", img->name); | ||
| 309 | goto fail; | ||
| 310 | } else if (!strcmp(tmp, "raw")) { | ||
| 311 | type = INSTALL_IMAGE_RAW; | ||
| 312 | } else if (!strcmp(tmp, "ext2")) { | ||
| 313 | type = INSTALL_IMAGE_EXT2; | ||
| 314 | } else if (!strcmp(tmp, "ext3")) { | ||
| 315 | type = INSTALL_IMAGE_EXT3; | ||
| 316 | } else { | ||
| 317 | LOGE("Unknown image type '%s' for image %s", tmp, img->name); | ||
| 318 | goto fail; | ||
| 319 | } | ||
| 320 | |||
| 321 | /* at this point we MUST either have a partition in 'pinfo' or a raw | ||
| 322 | * 'offset', otherwise quit */ | ||
| 323 | if (!pinfo && (offset == (loff_t)-1)) { | ||
| 324 | LOGE("Offset to write into the disk is unknown for %s", img->name); | ||
| 325 | goto fail; | ||
| 326 | } | ||
| 327 | |||
| 328 | if (!pinfo && (type != INSTALL_IMAGE_RAW)) { | ||
| 329 | LOGE("Only raw images can specify direct offset on the disk. Please" | ||
| 330 | " specify the target partition name instead. (%s)", img->name); | ||
| 331 | goto fail; | ||
| 332 | } | ||
| 333 | |||
| 334 | switch(type) { | ||
| 335 | case INSTALL_IMAGE_RAW: | ||
| 336 | if (write_raw_image(dinfo->device, filename, offset, test)) | ||
| 337 | goto fail; | ||
| 338 | break; | ||
| 339 | |||
| 340 | case INSTALL_IMAGE_EXT3: | ||
| 341 | /* makes the error checking in the imager function easier */ | ||
| 342 | if (flags & INSTALL_FLAG_ADDJOURNAL) { | ||
| 343 | LOGW("addjournal flag is meaningless for ext3 images"); | ||
| 344 | flags &= ~INSTALL_FLAG_ADDJOURNAL; | ||
| 345 | } | ||
| 346 | /* ...fall through... */ | ||
| 347 | |||
| 348 | case INSTALL_IMAGE_EXT2: | ||
| 349 | if (process_ext2_image(dest_part, filename, flags, test)) | ||
| 350 | goto fail; | ||
| 351 | break; | ||
| 352 | |||
| 353 | default: | ||
| 354 | LOGE("Unknown image type: %d", type); | ||
| 355 | goto fail; | ||
| 356 | } | ||
| 357 | |||
| 358 | done: | ||
| 359 | func_ret = 0; | ||
| 360 | |||
| 361 | fail: | ||
| 362 | if (dest_part) | ||
| 363 | free(dest_part); | ||
| 364 | return func_ret; | ||
| 365 | } | ||
| 366 | |||
| 367 | int | ||
| 368 | main(int argc, char *argv[]) | ||
| 369 | { | ||
| 370 | char *disk_conf_file = "/system/etc/disk_layout.conf"; | ||
| 371 | char *inst_conf_file = "/system/etc/installer.conf"; | ||
| 372 | char *inst_data_dir = "/data"; | ||
| 373 | char *inst_data_dev = NULL; | ||
| 374 | char *data_fstype = "ext2"; | ||
| 375 | cnode *config; | ||
| 376 | cnode *images; | ||
| 377 | cnode *img; | ||
| 378 | int cnt = 0; | ||
| 379 | struct disk_info *device_disk_info; | ||
| 380 | int dump = 0; | ||
| 381 | int test = 0; | ||
| 382 | int x; | ||
| 383 | |||
| 384 | while ((x = getopt (argc, argv, "thdc:l:p:")) != EOF) { | ||
| 385 | switch (x) { | ||
| 386 | case 'h': | ||
| 387 | return usage(); | ||
| 388 | case 'c': | ||
| 389 | inst_conf_file = optarg; | ||
| 390 | break; | ||
| 391 | case 'l': | ||
| 392 | disk_conf_file = optarg; | ||
| 393 | break; | ||
| 394 | case 't': | ||
| 395 | test = 1; | ||
| 396 | break; | ||
| 397 | case 'p': | ||
| 398 | inst_data_dev = optarg; | ||
| 399 | break; | ||
| 400 | case 'd': | ||
| 401 | dump = 1; | ||
| 402 | break; | ||
| 403 | default: | ||
| 404 | fprintf(stderr, "Unknown argument: %c\n", (char)optopt); | ||
| 405 | return usage(); | ||
| 406 | } | ||
| 407 | } | ||
| 408 | |||
| 409 | /* If the user asked us to wait for data device, wait for it to appear, | ||
| 410 | * and then mount it onto /data */ | ||
| 411 | if (inst_data_dev && !dump) { | ||
| 412 | struct stat filestat; | ||
| 413 | |||
| 414 | LOGI("Waiting for device: %s", inst_data_dev); | ||
| 415 | while (stat(inst_data_dev, &filestat)) | ||
| 416 | sleep(1); | ||
| 417 | LOGI("Device %s ready", inst_data_dev); | ||
| 418 | if (mount(inst_data_dev, inst_data_dir, data_fstype, MS_RDONLY, NULL)) { | ||
| 419 | LOGE("Could not mount %s on %s as %s", inst_data_dev, inst_data_dir, | ||
| 420 | data_fstype); | ||
| 421 | return 1; | ||
| 422 | } | ||
| 423 | } | ||
| 424 | |||
| 425 | /* Read and process the disk configuration */ | ||
| 426 | if (!(device_disk_info = load_diskconfig(disk_conf_file, NULL))) { | ||
| 427 | LOGE("Errors encountered while loading disk conf file %s", | ||
| 428 | disk_conf_file); | ||
| 429 | return 1; | ||
| 430 | } | ||
| 431 | |||
| 432 | if (process_disk_config(device_disk_info)) { | ||
| 433 | LOGE("Errors encountered while processing disk config from %s", | ||
| 434 | disk_conf_file); | ||
| 435 | return 1; | ||
| 436 | } | ||
| 437 | |||
| 438 | /* Was all of this for educational purposes? If so, quit. */ | ||
| 439 | if (dump) { | ||
| 440 | dump_disk_config(device_disk_info); | ||
| 441 | return 0; | ||
| 442 | } | ||
| 443 | |||
| 444 | /* This doesnt do anything but load the config file */ | ||
| 445 | if (!(config = read_conf_file(inst_conf_file))) | ||
| 446 | return 1; | ||
| 447 | |||
| 448 | /* First, partition the drive */ | ||
| 449 | if (apply_disk_config(device_disk_info, test)) | ||
| 450 | return 1; | ||
| 451 | |||
| 452 | /* Now process the installer config file and write the images to disk */ | ||
| 453 | if (!(images = config_find(config, "images"))) { | ||
| 454 | LOGE("Invalid configuration file %s. Missing 'images' section", | ||
| 455 | inst_conf_file); | ||
| 456 | return 1; | ||
| 457 | } | ||
| 458 | |||
| 459 | for (img = images->first_child; img; img = img->next) { | ||
| 460 | if (process_image_node(img, device_disk_info, test)) | ||
| 461 | return 1; | ||
| 462 | ++cnt; | ||
| 463 | } | ||
| 464 | |||
| 465 | /* | ||
| 466 | * We have to do the apply() twice. We must do it once before the image | ||
| 467 | * writes to layout the disk partitions so that we can write images to | ||
| 468 | * them. We then do the apply() again in case one of the images | ||
| 469 | * replaced the MBR with a new bootloader, and thus messed with | ||
| 470 | * partition table. | ||
| 471 | */ | ||
| 472 | if (apply_disk_config(device_disk_info, test)) | ||
| 473 | return 1; | ||
| 474 | |||
| 475 | LOGI("Done processing installer config. Configured %d images", cnt); | ||
| 476 | return 0; | ||
| 477 | } |
installer.conf
(45 / 0)
|   | |||
| 1 | images { | ||
| 2 | bootldr { | ||
| 3 | offset 0 | ||
| 4 | filename /data/bootldr.bin | ||
| 5 | type raw | ||
| 6 | } | ||
| 7 | |||
| 8 | boot { | ||
| 9 | partition boot | ||
| 10 | filename /data/boot.img | ||
| 11 | type raw | ||
| 12 | } | ||
| 13 | |||
| 14 | system { | ||
| 15 | partition system | ||
| 16 | filename /data/system.img | ||
| 17 | type ext2 | ||
| 18 | flags resize,addjournal | ||
| 19 | } | ||
| 20 | |||
| 21 | data { | ||
| 22 | partition data | ||
| 23 | filename /data/userdata.img | ||
| 24 | type ext2 | ||
| 25 | flags resize,addjournal | ||
| 26 | } | ||
| 27 | |||
| 28 | cache { | ||
| 29 | partition cache | ||
| 30 | mkfs ext3 | ||
| 31 | } | ||
| 32 | |||
| 33 | third_party { | ||
| 34 | partition third_party | ||
| 35 | mkfs ext3 | ||
| 36 | } | ||
| 37 | |||
| 38 | ## TODO: Add the support for this later? | ||
| 39 | # system { | ||
| 40 | # partition system | ||
| 41 | # filename /data/system.tar.gz | ||
| 42 | # type targz | ||
| 43 | # mkfs ext3 | ||
| 44 | # } | ||
| 45 | } |
installer.h
(34 / 0)
|   | |||
| 1 | /* commands/sysloader/installer/installer.h | ||
| 2 | * | ||
| 3 | * Copyright 2008, The Android Open Source Project | ||
| 4 | * | ||
| 5 | * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 6 | * you may not use this file except in compliance with the License. | ||
| 7 | * You may obtain a copy of the License at | ||
| 8 | * | ||
| 9 | * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 10 | * | ||
| 11 | * Unless required by applicable law or agreed to in writing, software | ||
| 12 | * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 14 | * See the License for the specific language governing permissions and | ||
| 15 | * limitations under the License. | ||
| 16 | */ | ||
| 17 | |||
| 18 | #ifndef __COMMANDS_SYSLOADER_INSTALLER_INSTALLER_H | ||
| 19 | #define __COMMANDS_SYSLOADER_INSTALLER_INSTALLER_H | ||
| 20 | |||
| 21 | #include <stdint.h> | ||
| 22 | |||
| 23 | /* image types */ | ||
| 24 | #define INSTALL_IMAGE_RAW 1 | ||
| 25 | #define INSTALL_IMAGE_EXT2 2 | ||
| 26 | #define INSTALL_IMAGE_EXT3 3 | ||
| 27 | #define INSTALL_IMAGE_TARGZ 10 | ||
| 28 | |||
| 29 | |||
| 30 | /* flags */ | ||
| 31 | #define INSTALL_FLAG_RESIZE 0x1 | ||
| 32 | #define INSTALL_FLAG_ADDJOURNAL 0x2 | ||
| 33 | |||
| 34 | #endif /* __COMMANDS_SYSLOADER_INSTALLER_INSTALLER_H */ |
installer_img_layout.conf
(23 / 0)
|   | |||
| 1 | device { | ||
| 2 | scheme mbr | ||
| 3 | |||
| 4 | # bytes in a disk "block", must be a power of 2! | ||
| 5 | sector_size 512 | ||
| 6 | |||
| 7 | # What LBA should the partitions start at? | ||
| 8 | start_lba 2048 | ||
| 9 | |||
| 10 | # Autodetect disk size if == 0 | ||
| 11 | num_lba 0 | ||
| 12 | |||
| 13 | partitions { | ||
| 14 | inst_boot { | ||
| 15 | active y | ||
| 16 | type linux | ||
| 17 | } | ||
| 18 | |||
| 19 | inst_data { | ||
| 20 | type linux | ||
| 21 | } | ||
| 22 | } | ||
| 23 | } |
libdiskconfig/Android.mk
(46 / 0)
|   | |||
| 1 | LOCAL_PATH := $(call my-dir) | ||
| 2 | include $(CLEAR_VARS) | ||
| 3 | |||
| 4 | commonSources := \ | ||
| 5 | diskconfig.c \ | ||
| 6 | diskutils.c \ | ||
| 7 | write_lst.c \ | ||
| 8 | config_mbr.c | ||
| 9 | |||
| 10 | ifneq ($(TARGET_SIMULATOR),true) | ||
| 11 | ifeq ($(TARGET_ARCH),x86) | ||
| 12 | |||
| 13 | ########################### | ||
| 14 | # static library for host | ||
| 15 | LOCAL_SRC_FILES := $(commonSources) | ||
| 16 | |||
| 17 | LOCAL_CFLAGS := -O2 -g -W -Wall -Werror -D_LARGEFILE64_SOURCE | ||
| 18 | |||
| 19 | LOCAL_MODULE := libdiskconfig_host | ||
| 20 | LOCAL_STATIC_LIBRARIES := libcutils | ||
| 21 | include $(BUILD_HOST_STATIC_LIBRARY) | ||
| 22 | |||
| 23 | ## Build a test executable for host (to dump configuration files). | ||
| 24 | include $(CLEAR_VARS) | ||
| 25 | LOCAL_SRC_FILES := $(commonSources) | ||
| 26 | LOCAL_SRC_FILES += dump_diskconfig.c | ||
| 27 | LOCAL_MODULE := dump_diskconfig | ||
| 28 | LOCAL_STATIC_LIBRARIES := libdiskconfig_host libcutils | ||
| 29 | include $(BUILD_HOST_EXECUTABLE) | ||
| 30 | |||
| 31 | ########################### | ||
| 32 | # shared library for target | ||
| 33 | include $(CLEAR_VARS) | ||
| 34 | |||
| 35 | LOCAL_SRC_FILES := $(commonSources) | ||
| 36 | |||
| 37 | LOCAL_CFLAGS := -O2 -g -W -Wall -Werror | ||
| 38 | |||
| 39 | LOCAL_MODULE := libdiskconfig | ||
| 40 | LOCAL_MODULE_TAGS := system_builder | ||
| 41 | LOCAL_SYSTEM_SHARED_LIBRARIES := libcutils liblog libc | ||
| 42 | |||
| 43 | include $(BUILD_SHARED_LIBRARY) | ||
| 44 | |||
| 45 | endif # ! TARGET_SIMULATOR | ||
| 46 | endif # TARGET_ARCH == x86 |
libdiskconfig/config_mbr.c
(325 / 0)
|   | |||
| 1 | /* libs/diskconfig/diskconfig.c | ||
| 2 | * | ||
| 3 | * Copyright 2008, The Android Open Source Project | ||
| 4 | * | ||
| 5 | * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 6 | * you may not use this file except in compliance with the License. | ||
| 7 | * You may obtain a copy of the License at | ||
| 8 | * | ||
| 9 | * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 10 | * | ||
| 11 | * Unless required by applicable law or agreed to in writing, software | ||
| 12 | * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 14 | * See the License for the specific language governing permissions and | ||
| 15 | * limitations under the License. | ||
| 16 | */ | ||
| 17 | |||
| 18 | #define LOG_TAG "config_mbr" | ||
| 19 | #include <stdint.h> | ||
| 20 | #include <stdlib.h> | ||
| 21 | #include <string.h> | ||
| 22 | #include <stdio.h> | ||
| 23 | |||
| 24 | #include <cutils/log.h> | ||
| 25 | |||
| 26 | #include "diskconfig.h" | ||
| 27 | |||
| 28 | |||
| 29 | /* start and len are in LBA units */ | ||
| 30 | static void | ||
| 31 | cfg_pentry(struct pc_partition *pentry, uint8_t status, uint8_t type, | ||
| 32 | uint32_t start, uint32_t len) | ||
| 33 | { | ||
| 34 | if (len > 0) { | ||
| 35 | /* seems that somes BIOSens can get wedged on boot while verifying | ||
| 36 | * the mbr if these are 0 */ | ||
| 37 | memset(&pentry->start, 0xff, sizeof(struct chs)); | ||
| 38 | memset(&pentry->end, 0xff, sizeof(struct chs)); | ||
| 39 | } else { | ||
| 40 | /* zero out the c/h/s entries.. they are not used */ | ||
| 41 | memset(&pentry->start, 0, sizeof(struct chs)); | ||
| 42 | memset(&pentry->end, 0, sizeof(struct chs)); | ||
| 43 | } | ||
| 44 | |||
| 45 | pentry->status = status; | ||
| 46 | pentry->type = type; | ||
| 47 | pentry->start_lba = start; | ||
| 48 | pentry->len_lba = len; | ||
| 49 | |||
| 50 | LOGI("Configuring pentry. status=0x%x type=0x%x start_lba=%u len_lba=%u", | ||
| 51 | pentry->status, pentry->type, pentry->start_lba, pentry->len_lba); | ||
| 52 | } | ||
| 53 | |||
| 54 | |||
| 55 | static inline uint32_t | ||
| 56 | kb_to_lba(uint32_t len_kb, uint32_t sect_size) | ||
| 57 | { | ||
| 58 | uint64_t lba; | ||
| 59 | |||
| 60 | lba = (uint64_t)len_kb * 1024; | ||
| 61 | /* bump it up to the next LBA boundary just in case */ | ||
| 62 | lba = (lba + (uint64_t)sect_size - 1) & ~((uint64_t)sect_size - 1); | ||
| 63 | lba /= (uint64_t)sect_size; | ||
| 64 | if (lba >= 0xffffffffULL) | ||
| 65 | LOGE("Error converting kb -> lba. 32bit overflow, expect weirdness"); | ||
| 66 | return (uint32_t)(lba & 0xffffffffULL); | ||
| 67 | } | ||
| 68 | |||
| 69 | |||
| 70 | static struct write_list * | ||
| 71 | mk_pri_pentry(struct disk_info *dinfo, struct part_info *pinfo, int pnum, | ||
| 72 | uint32_t *lba) | ||
| 73 | { | ||
| 74 | struct write_list *item; | ||
| 75 | struct pc_partition *pentry; | ||
| 76 | |||
| 77 | if (pnum >= PC_NUM_BOOT_RECORD_PARTS) { | ||
| 78 | LOGE("Maximum number of primary partition exceeded."); | ||
| 79 | return NULL; | ||
| 80 | } | ||
| 81 | |||
| 82 | if (!(item = alloc_wl(sizeof(struct pc_partition)))) { | ||
| 83 | LOGE("Unable to allocate memory for partition entry."); | ||
| 84 | return NULL; | ||
| 85 | } | ||
| 86 | |||
| 87 | { | ||
| 88 | /* DO NOT DEREFERENCE */ | ||
| 89 | struct pc_boot_record *mbr = (void *)PC_MBR_DISK_OFFSET; | ||
| 90 | /* grab the offset in mbr where to write this partition entry. */ | ||
| 91 | item->offset = (loff_t)((uint32_t)((uint8_t *)(&mbr->ptable[pnum]))); | ||
| 92 | } | ||
| 93 | |||
| 94 | pentry = (struct pc_partition *) &item->data; | ||
| 95 | |||
| 96 | /* need a standard primary partition entry */ | ||
| 97 | if (pinfo) { | ||
| 98 | /* need this to be 64 bit in case len_kb is large */ | ||
| 99 | uint64_t len_lba; | ||
| 100 | |||
| 101 | if (pinfo->len_kb != (uint32_t)-1) { | ||
| 102 | /* bump it up to the next LBA boundary just in case */ | ||
| 103 | len_lba = ((uint64_t)pinfo->len_kb * 1024); | ||
| 104 | len_lba += ((uint64_t)dinfo->sect_size - 1); | ||
| 105 | len_lba &= ~((uint64_t)dinfo->sect_size - 1); | ||
| 106 | len_lba /= (uint64_t)dinfo->sect_size; | ||
| 107 | } else { | ||
| 108 | /* make it fill the rest of disk */ | ||
| 109 | len_lba = dinfo->num_lba - *lba; | ||
| 110 | } | ||
| 111 | |||
| 112 | cfg_pentry(pentry, ((pinfo->flags & PART_ACTIVE_FLAG) ? | ||
| 113 | PC_PART_ACTIVE : PC_PART_NORMAL), | ||
| 114 | pinfo->type, *lba, (uint32_t)len_lba); | ||
| 115 | |||
| 116 | pinfo->start_lba = *lba; | ||
| 117 | *lba += (uint32_t)len_lba; | ||
| 118 | } else { | ||
| 119 | /* this should be made an extended partition, and should take | ||
| 120 | * up the rest of the disk as a primary partition */ | ||
| 121 | cfg_pentry(pentry, PC_PART_NORMAL, PC_PART_TYPE_EXTENDED, | ||
| 122 | *lba, dinfo->num_lba - *lba); | ||
| 123 | |||
| 124 | /* note that we do not update the *lba because we now have to | ||
| 125 | * create a chain of extended partition tables, and first one is at | ||
| 126 | * *lba */ | ||
| 127 | } | ||
| 128 | |||
| 129 | return item; | ||
| 130 | } | ||
| 131 | |||
| 132 | |||
| 133 | /* This function configures an extended boot record at the beginning of an | ||
| 134 | * extended partition. This creates a logical partition and a pointer to | ||
| 135 | * the next EBR. | ||
| 136 | * | ||
| 137 | * ext_lba == The start of the toplevel extended partition (pointed to by the | ||
| 138 | * entry in the MBR). | ||
| 139 | */ | ||
| 140 | static struct write_list * | ||
| 141 | mk_ext_pentry(struct disk_info *dinfo, struct part_info *pinfo, uint32_t *lba, | ||
| 142 | uint32_t ext_lba, struct part_info *pnext) | ||
| 143 | { | ||
| 144 | struct write_list *item; | ||
| 145 | struct pc_boot_record *ebr; | ||
| 146 | uint32_t len; /* in lba units */ | ||
| 147 | |||
| 148 | if (!(item = alloc_wl(sizeof(struct pc_boot_record)))) { | ||
| 149 | LOGE("Unable to allocate memory for EBR."); | ||
| 150 | return NULL; | ||
| 151 | } | ||
| 152 | |||
| 153 | /* we are going to write the ebr at the current LBA, and then bump the | ||
| 154 | * lba counter since that is where the logical data partition will start */ | ||
| 155 | item->offset = (*lba) * dinfo->sect_size; | ||
| 156 | (*lba)++; | ||
| 157 | |||
| 158 | ebr = (struct pc_boot_record *) &item->data; | ||
| 159 | memset(ebr, 0, sizeof(struct pc_boot_record)); | ||
| 160 | ebr->mbr_sig = PC_BIOS_BOOT_SIG; | ||
| 161 | |||
| 162 | if (pinfo->len_kb != (uint32_t)-1) | ||
| 163 | len = kb_to_lba(pinfo->len_kb, dinfo->sect_size); | ||
| 164 | else { | ||
| 165 | if (pnext) { | ||
| 166 | LOGE("Only the last partition can be specified to fill the disk " | ||
| 167 | "(name = '%s')", pinfo->name); | ||
| 168 | goto fail; | ||
| 169 | } | ||
| 170 | len = dinfo->num_lba - *lba; | ||
| 171 | /* update the pinfo structure to reflect the new size, for | ||
| 172 | * bookkeeping */ | ||
| 173 | pinfo->len_kb = | ||
| 174 | (uint32_t)(((uint64_t)len * (uint64_t)dinfo->sect_size) / | ||
| 175 | ((uint64_t)1024)); | ||
| 176 | } | ||
| 177 | |||
| 178 | cfg_pentry(&ebr->ptable[PC_EBR_LOGICAL_PART], PC_PART_NORMAL, | ||
| 179 | pinfo->type, 1, len); | ||
| 180 | |||
| 181 | pinfo->start_lba = *lba; | ||
| 182 | *lba += len; | ||
| 183 | |||
| 184 | /* If this is not the last partition, we have to create a link to the | ||
| 185 | * next extended partition. | ||
| 186 | * | ||
| 187 | * Otherwise, there's nothing to do since the "pointer entry" is | ||
| 188 | * already zero-filled. | ||
| 189 | */ | ||
| 190 | if (pnext) { | ||
| 191 | /* The start lba for next partition is an offset from the beginning | ||
| 192 | * of the top-level extended partition */ | ||
| 193 | uint32_t next_start_lba = *lba - ext_lba; | ||
| 194 | uint32_t next_len_lba; | ||
| 195 | if (pnext->len_kb != (uint32_t)-1) | ||
| 196 | next_len_lba = 1 + kb_to_lba(pnext->len_kb, dinfo->sect_size); | ||
| 197 | else | ||
| 198 | next_len_lba = dinfo->num_lba - *lba; | ||
| 199 | cfg_pentry(&ebr->ptable[PC_EBR_NEXT_PTR_PART], PC_PART_NORMAL, | ||
| 200 | PC_PART_TYPE_EXTENDED, next_start_lba, next_len_lba); | ||
| 201 | } | ||
| 202 | |||
| 203 | return item; | ||
| 204 | |||
| 205 | fail: | ||
| 206 | free_wl(item); | ||
| 207 | return NULL; | ||
| 208 | } | ||
| 209 | |||
| 210 | |||
| 211 | struct write_list * | ||
| 212 | config_mbr(struct disk_info *dinfo) | ||
| 213 | { | ||
| 214 | struct part_info *pinfo; | ||
| 215 | uint32_t cur_lba = dinfo->skip_lba; | ||
| 216 | uint32_t ext_lba = 0; | ||
| 217 | struct write_list *wr_list = NULL; | ||
| 218 | struct write_list *temp_wr = NULL; | ||
| 219 | int cnt = 0; | ||
| 220 | int extended = 0; | ||
| 221 | |||
| 222 | if (!dinfo->part_lst) | ||
| 223 | return NULL; | ||
| 224 | |||
| 225 | for (cnt = 0; cnt < dinfo->num_parts; ++cnt) { | ||
| 226 | pinfo = &dinfo->part_lst[cnt]; | ||
| 227 | |||
| 228 | /* Should we create an extedned partition? */ | ||
| 229 | if (cnt == (PC_NUM_BOOT_RECORD_PARTS - 1)) { | ||
| 230 | if (cnt + 1 < dinfo->num_parts) { | ||
| 231 | extended = 1; | ||
| 232 | ext_lba = cur_lba; | ||
| 233 | if ((temp_wr = mk_pri_pentry(dinfo, NULL, cnt, &cur_lba))) | ||
| 234 | wlist_add(&wr_list, temp_wr); | ||
| 235 | else { | ||
| 236 | LOGE("Cannot create primary extended partition."); | ||
| 237 | goto fail; | ||
| 238 | } | ||
| 239 | } | ||
| 240 | } | ||
| 241 | |||
| 242 | /* if extended, need 1 lba for ebr */ | ||
| 243 | if ((cur_lba + extended) >= dinfo->num_lba) | ||
| 244 | goto nospace; | ||
| 245 | else if (pinfo->len_kb != (uint32_t)-1) { | ||
| 246 | uint32_t sz_lba = (pinfo->len_kb / dinfo->sect_size) * 1024; | ||
| 247 | if ((cur_lba + sz_lba + extended) > dinfo->num_lba) | ||
| 248 | goto nospace; | ||
| 249 | } | ||
| 250 | |||
| 251 | if (!extended) | ||
| 252 | temp_wr = mk_pri_pentry(dinfo, pinfo, cnt, &cur_lba); | ||
| 253 | else { | ||
| 254 | struct part_info *pnext; | ||
| 255 | pnext = cnt + 1 < dinfo->num_parts ? &dinfo->part_lst[cnt+1] : NULL; | ||
| 256 | temp_wr = mk_ext_pentry(dinfo, pinfo, &cur_lba, ext_lba, pnext); | ||
| 257 | } | ||
| 258 | |||
| 259 | if (temp_wr) | ||
| 260 | wlist_add(&wr_list, temp_wr); | ||
| 261 | else { | ||
| 262 | LOGE("Cannot create partition %d (%s).", cnt, pinfo->name); | ||
| 263 | goto fail; | ||
| 264 | } | ||
| 265 | } | ||
| 266 | |||
| 267 | /* fill in the rest of the MBR with empty parts (if needed). */ | ||
| 268 | for (; cnt < PC_NUM_BOOT_RECORD_PARTS; ++cnt) { | ||
| 269 | struct part_info blank; | ||
| 270 | cur_lba = 0; | ||
| 271 | memset(&blank, 0, sizeof(struct part_info)); | ||
| 272 | if (!(temp_wr = mk_pri_pentry(dinfo, &blank, cnt, &cur_lba))) { | ||
| 273 | LOGE("Cannot create blank partition %d.", cnt); | ||
| 274 | goto fail; | ||
| 275 | } | ||
| 276 | wlist_add(&wr_list, temp_wr); | ||
| 277 | } | ||
| 278 | |||
| 279 | return wr_list; | ||
| 280 | |||
| 281 | nospace: | ||
| 282 | LOGE("Not enough space to add parttion '%s'.", pinfo->name); | ||
| 283 | |||
| 284 | fail: | ||
| 285 | wlist_free(wr_list); | ||
| 286 | return NULL; | ||
| 287 | } | ||
| 288 | |||
| 289 | |||
| 290 | /* Returns the device path of the partition referred to by 'name' | ||
| 291 | * Must be freed by the caller. | ||
| 292 | */ | ||
| 293 | char * | ||
| 294 | find_mbr_part(struct disk_info *dinfo, const char *name) | ||
| 295 | { | ||
| 296 | struct part_info *plist = dinfo->part_lst; | ||
| 297 | int num = 0; | ||
| 298 | char *dev_name = NULL; | ||
| 299 | int has_extended = (dinfo->num_parts > PC_NUM_BOOT_RECORD_PARTS); | ||
| 300 | |||
| 301 | for(num = 1; num <= dinfo->num_parts; ++num) { | ||
| 302 | if (!strcmp(plist[num-1].name, name)) | ||
| 303 | break; | ||
| 304 | } | ||
| 305 | |||
| 306 | if (num > dinfo->num_parts) | ||
| 307 | return NULL; | ||
| 308 | |||
| 309 | if (has_extended && (num >= PC_NUM_BOOT_RECORD_PARTS)) | ||
| 310 | num++; | ||
| 311 | |||
| 312 | if (!(dev_name = malloc(MAX_NAME_LEN))) { | ||
| 313 | LOGE("Cannot allocate memory."); | ||
| 314 | return NULL; | ||
| 315 | } | ||
| 316 | |||
| 317 | num = snprintf(dev_name, MAX_NAME_LEN, "%s%d", dinfo->device, num); | ||
| 318 | if (num >= MAX_NAME_LEN) { | ||
| 319 | LOGE("Device name is too long?!"); | ||
| 320 | free(dev_name); | ||
| 321 | return NULL; | ||
| 322 | } | ||
| 323 | |||
| 324 | return dev_name; | ||
| 325 | } |
libdiskconfig/diskconfig.c
(531 / 0)
|   | |||
| 1 | /* libs/diskconfig/diskconfig.c | ||
| 2 | * | ||
| 3 | * Copyright 2008, The Android Open Source Project | ||
| 4 | * | ||
| 5 | * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 6 | * you may not use this file except in compliance with the License. | ||
| 7 | * You may obtain a copy of the License at | ||
| 8 | * | ||
| 9 | * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 10 | * | ||
| 11 | * Unless required by applicable law or agreed to in writing, software | ||
| 12 | * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 14 | * See the License for the specific language governing permissions and | ||
| 15 | * limitations under the License. | ||
| 16 | */ | ||
| 17 | |||
| 18 | #define LOG_TAG "diskconfig" | ||
| 19 | |||
| 20 | #include <errno.h> | ||
| 21 | #include <fcntl.h> | ||
| 22 | #include <stdio.h> | ||
| 23 | #include <stdlib.h> | ||
| 24 | #include <string.h> | ||
| 25 | #include <unistd.h> | ||
| 26 | #include <sys/ioctl.h> | ||
| 27 | #include <sys/stat.h> | ||
| 28 | |||
| 29 | #include <linux/fs.h> | ||
| 30 | |||
| 31 | #include <cutils/config_utils.h> | ||
| 32 | #include <cutils/log.h> | ||
| 33 | |||
| 34 | #include "diskconfig.h" | ||
| 35 | |||
| 36 | |||
| 37 | static int | ||
| 38 | parse_len(const char *str, uint64_t *plen) | ||
| 39 | { | ||
| 40 | char tmp[64]; | ||
| 41 | int len_str; | ||
| 42 | uint32_t multiple = 1; | ||
| 43 | |||
| 44 | strncpy(tmp, str, sizeof(tmp)); | ||
| 45 | tmp[sizeof(tmp)-1] = '\0'; | ||
| 46 | len_str = strlen(tmp); | ||
| 47 | if (!len_str) { | ||
| 48 | LOGE("Invalid disk length specified."); | ||
| 49 | return 1; | ||
| 50 | } | ||
| 51 | |||
| 52 | switch(tmp[len_str - 1]) { | ||
| 53 | case 'M': case 'm': | ||
| 54 | /* megabyte */ | ||
| 55 | multiple <<= 10; | ||
| 56 | case 'K': case 'k': | ||
| 57 | /* kilobytes */ | ||
| 58 | multiple <<= 10; | ||
| 59 | tmp[len_str - 1] = '\0'; | ||
| 60 | break; | ||
| 61 | default: | ||
| 62 | break; | ||
| 63 | } | ||
| 64 | |||
| 65 | *plen = strtoull(tmp, NULL, 0); | ||
| 66 | if (!*plen) { | ||
| 67 | LOGE("Invalid length specified: %s", str); | ||
| 68 | return 1; | ||
| 69 | } | ||
| 70 | |||
| 71 | if (*plen == (uint64_t)-1) { | ||
| 72 | if (multiple > 1) { | ||
| 73 | LOGE("Size modifier illegal when len is -1"); | ||
| 74 | return 1; | ||
| 75 | } | ||
| 76 | } else { | ||
| 77 | /* convert len to kilobytes */ | ||
| 78 | if (multiple > 1024) | ||
| 79 | multiple >>= 10; | ||
| 80 | *plen *= multiple; | ||
| 81 | |||
| 82 | if (*plen > 0xffffffffULL) { | ||
| 83 | LOGE("Length specified is too large!: %llu KB", *plen); | ||
| 84 | return 1; | ||
| 85 | } | ||
| 86 | } | ||
| 87 | |||
| 88 | return 0; | ||
| 89 | } | ||
| 90 | |||
| 91 | |||
| 92 | static int | ||
| 93 | load_partitions(cnode *root, struct disk_info *dinfo) | ||
| 94 | { | ||
| 95 | cnode *partnode; | ||
| 96 | |||
| 97 | dinfo->num_parts = 0; | ||
| 98 | for (partnode = root->first_child; partnode; partnode = partnode->next) { | ||
| 99 | struct part_info *pinfo = &dinfo->part_lst[dinfo->num_parts]; | ||
| 100 | const char *tmp; | ||
| 101 | |||
| 102 | /* bleh, i will leak memory here, but i DONT CARE since | ||
| 103 | * the only right thing to do when this function fails | ||
| 104 | * is to quit */ | ||
| 105 | pinfo->name = strdup(partnode->name); | ||
| 106 | |||
| 107 | if(config_bool(partnode, "active", 0)) | ||
| 108 | pinfo->flags |= PART_ACTIVE_FLAG; | ||
| 109 | |||
| 110 | if (!(tmp = config_str(partnode, "type", NULL))) { | ||
| 111 | LOGE("Partition type required: %s", pinfo->name); | ||
| 112 | return 1; | ||
| 113 | } | ||
| 114 | |||
| 115 | /* possible values are: linux */ | ||
| 116 | if (!strcmp(tmp, "linux")) { | ||
| 117 | pinfo->type = PC_PART_TYPE_LINUX; | ||
| 118 | } else { | ||
| 119 | LOGE("Unsupported partition type found: %s", tmp); | ||
| 120 | return 1; | ||
| 121 | } | ||
| 122 | |||
| 123 | if ((tmp = config_str(partnode, "len", NULL)) != NULL) { | ||
| 124 | uint64_t len; | ||
| 125 | if (parse_len(tmp, &len)) | ||
| 126 | return 1; | ||
| 127 | pinfo->len_kb = (uint32_t) len; | ||
| 128 | } else | ||
| 129 | pinfo->len_kb = 0; | ||
| 130 | |||
| 131 | ++dinfo->num_parts; | ||
| 132 | } | ||
| 133 | |||
| 134 | return 0; | ||
| 135 | } | ||
| 136 | |||
| 137 | struct disk_info * | ||
| 138 | load_diskconfig(const char *fn, char *path_override) | ||
| 139 | { | ||
| 140 | struct disk_info *dinfo; | ||
| 141 | cnode *devroot; | ||
| 142 | cnode *partnode; | ||
| 143 | cnode *root = config_node("", ""); | ||
| 144 | const char *tmp; | ||
| 145 | |||
| 146 | if (!(dinfo = malloc(sizeof(struct disk_info)))) { | ||
| 147 | LOGE("Could not malloc disk_info"); | ||
| 148 | return NULL; | ||
| 149 | } | ||
| 150 | memset(dinfo, 0, sizeof(struct disk_info)); | ||
| 151 | |||
| 152 | if (!(dinfo->part_lst = malloc(MAX_NUM_PARTS * sizeof(struct part_info)))) { | ||
| 153 | LOGE("Could not malloc part_lst"); | ||
| 154 | goto fail; | ||
| 155 | } | ||
| 156 | memset(dinfo->part_lst, 0, | ||
| 157 | sizeof(MAX_NUM_PARTS * sizeof(struct part_info))); | ||
| 158 | |||
| 159 | config_load_file(root, fn); | ||
| 160 | if (root->first_child == NULL) { | ||
| 161 | LOGE("Could not read config file %s", fn); | ||
| 162 | goto fail; | ||
| 163 | } | ||
| 164 | |||
| 165 | if (!(devroot = config_find(root, "device"))) { | ||
| 166 | LOGE("Could not find device section in config file '%s'", fn); | ||
| 167 | goto fail; | ||
| 168 | } | ||
| 169 | |||
| 170 | |||
| 171 | if (!(tmp = config_str(devroot, "path", path_override))) { | ||
| 172 | LOGE("device path is requried"); | ||
| 173 | goto fail; | ||
| 174 | } | ||
| 175 | dinfo->device = strdup(tmp); | ||
| 176 | |||
| 177 | /* find the partition scheme */ | ||
| 178 | if (!(tmp = config_str(devroot, "scheme", NULL))) { | ||
| 179 | LOGE("partition scheme is required"); | ||
| 180 | goto fail; | ||
| 181 | } else if (!strcmp(tmp, "mbr")) { | ||
| 182 | dinfo->scheme = PART_SCHEME_MBR; | ||
| 183 | } else if (!strcmp(tmp, "gpt")) { | ||
| 184 | LOGE("'gpt' partition scheme not supported yet."); | ||
| 185 | goto fail; | ||
| 186 | } else { | ||
| 187 | LOGE("Unknown partition scheme specified: %s", tmp); | ||
| 188 | goto fail; | ||
| 189 | } | ||
| 190 | |||
| 191 | /* grab the sector size (in bytes) */ | ||
| 192 | tmp = config_str(devroot, "sector_size", "512"); | ||
| 193 | dinfo->sect_size = strtol(tmp, NULL, 0); | ||
| 194 | if (!dinfo->sect_size) { | ||
| 195 | LOGE("Invalid sector size: %s", tmp); | ||
| 196 | goto fail; | ||
| 197 | } | ||
| 198 | |||
| 199 | /* first lba where the partitions will start on disk */ | ||
| 200 | if (!(tmp = config_str(devroot, "start_lba", NULL))) { | ||
| 201 | LOGE("start_lba must be provided"); | ||
| 202 | goto fail; | ||
| 203 | } | ||
| 204 | |||
| 205 | if (!(dinfo->skip_lba = strtol(tmp, NULL, 0))) { | ||
| 206 | LOGE("Invalid starting LBA (or zero): %s", tmp); | ||
| 207 | goto fail; | ||
| 208 | } | ||
| 209 | |||
| 210 | /* Number of LBAs on disk */ | ||
| 211 | if (!(tmp = config_str(devroot, "num_lba", NULL))) { | ||
| 212 | LOGE("num_lba is required"); | ||
| 213 | goto fail; | ||
| 214 | } | ||
| 215 | dinfo->num_lba = strtoul(tmp, NULL, 0); | ||
| 216 | |||
| 217 | if (!(partnode = config_find(devroot, "partitions"))) { | ||
| 218 | LOGE("Device must specify partition list"); | ||
| 219 | goto fail; | ||
| 220 | } | ||
| 221 | |||
| 222 | if (load_partitions(partnode, dinfo)) | ||
| 223 | goto fail; | ||
| 224 | |||
| 225 | return dinfo; | ||
| 226 | |||
| 227 | fail: | ||
| 228 | if (dinfo->part_lst) | ||
| 229 | free(dinfo->part_lst); | ||
| 230 | if (dinfo->device) | ||
| 231 | free(dinfo->device); | ||
| 232 | free(dinfo); | ||
| 233 | return NULL; | ||
| 234 | } | ||
| 235 | |||
| 236 | static int | ||
| 237 | sync_ptable(int fd) | ||
| 238 | { | ||
| 239 | struct stat stat; | ||
| 240 | int rv; | ||
| 241 | |||
| 242 | sync(); | ||
| 243 | |||
| 244 | if (fstat(fd, &stat)) { | ||
| 245 | LOGE("Cannot stat, errno=%d.", errno); | ||
| 246 | return -1; | ||
| 247 | } | ||
| 248 | |||
| 249 | if (S_ISBLK(stat.st_mode) && ((rv = ioctl(fd, BLKRRPART, NULL)) < 0)) { | ||
| 250 | LOGE("Could not re-read partition table. REBOOT!. (errno=%d)", errno); | ||
| 251 | return -1; | ||
| 252 | } | ||
| 253 | |||
| 254 | return 0; | ||
| 255 | } | ||
| 256 | |||
| 257 | /* This function verifies that the disk info provided is valid, and if so, | ||
| 258 | * returns an open file descriptor. | ||
| 259 | * | ||
| 260 | * This does not necessarily mean that it will later be successfully written | ||
| 261 | * though. If we use the pc-bios partitioning scheme, we must use extended | ||
| 262 | * partitions, which eat up some hd space. If the user manually provisioned | ||
| 263 | * every single partition, but did not account for the extra needed space, | ||
| 264 | * then we will later fail. | ||
| 265 | * | ||
| 266 | * TODO: Make validation more complete. | ||
| 267 | */ | ||
| 268 | static int | ||
| 269 | validate(struct disk_info *dinfo) | ||
| 270 | { | ||
| 271 | int fd; | ||
| 272 | int sect_sz; | ||
| 273 | uint64_t disk_size; | ||
| 274 | uint64_t total_size; | ||
| 275 | int cnt; | ||
| 276 | struct stat stat; | ||
| 277 | |||
| 278 | if (!dinfo) | ||
| 279 | return -1; | ||
| 280 | |||
| 281 | if ((fd = open(dinfo->device, O_RDWR)) < 0) { | ||
| 282 | LOGE("Cannot open device '%s' (errno=%d)", dinfo->device, errno); | ||
| 283 | return -1; | ||
| 284 | } | ||
| 285 | |||
| 286 | if (fstat(fd, &stat)) { | ||
| 287 | LOGE("Cannot stat file '%s', errno=%d.", dinfo->device, errno); | ||
| 288 | goto fail; | ||
| 289 | } | ||
| 290 | |||
| 291 | |||
| 292 | /* XXX: Some of the code below is kind of redundant and should probably | ||
| 293 | * be refactored a little, but it will do for now. */ | ||
| 294 | |||
| 295 | /* Verify that we can operate on the device that was requested. | ||
| 296 | * We presently only support block devices and regular file images. */ | ||
| 297 | if (S_ISBLK(stat.st_mode)) { | ||
| 298 | /* get the sector size and make sure we agree */ | ||
| 299 | if (ioctl(fd, BLKSSZGET, §_sz) < 0) { | ||
| 300 | LOGE("Cannot get sector size (errno=%d)", errno); | ||
| 301 | goto fail; | ||
| 302 | } | ||
| 303 | |||
| 304 | if (!sect_sz || sect_sz != dinfo->sect_size) { | ||
| 305 | LOGE("Device sector size is zero or sector sizes do not match!"); | ||
| 306 | goto fail; | ||
| 307 | } | ||
| 308 | |||
| 309 | /* allow the user override the "disk size" if they provided num_lba */ | ||
| 310 | if (!dinfo->num_lba) { | ||
| 311 | if (ioctl(fd, BLKGETSIZE64, &disk_size) < 0) { | ||
| 312 | LOGE("Could not get block device size (errno=%d)", errno); | ||
| 313 | goto fail; | ||
| 314 | } | ||
| 315 | /* XXX: we assume that the disk has < 2^32 sectors :-) */ | ||
| 316 | dinfo->num_lba = (uint32_t)(disk_size / (uint64_t)dinfo->sect_size); | ||
| 317 | } else | ||
| 318 | disk_size = (uint64_t)dinfo->num_lba * (uint64_t)dinfo->sect_size; | ||
| 319 | } else if (S_ISREG(stat.st_mode)) { | ||
| 320 | LOGI("Requesting operation on a regular file, not block device."); | ||
| 321 | if (!dinfo->sect_size) { | ||
| 322 | LOGE("Sector size for regular file images cannot be zero"); | ||
| 323 | goto fail; | ||
| 324 | } | ||
| 325 | if (dinfo->num_lba) | ||
| 326 | disk_size = (uint64_t)dinfo->num_lba * (uint64_t)dinfo->sect_size; | ||
| 327 | else { | ||
| 328 | dinfo->num_lba = (uint32_t)(stat.st_size / dinfo->sect_size); | ||
| 329 | disk_size = (uint64_t)stat.st_size; | ||
| 330 | } | ||
| 331 | } else { | ||
| 332 | LOGE("Device does not refer to a regular file or a block device!"); | ||
| 333 | goto fail; | ||
| 334 | } | ||
| 335 | |||
| 336 | #if 0 | ||
| 337 | LOGV("Device/file %s: size=%llu bytes, num_lba=%u, sect_size=%d", | ||
| 338 | dinfo->device, disk_size, dinfo->num_lba, dinfo->sect_size); | ||
| 339 | #endif | ||
| 340 | |||
| 341 | /* since this is our offset into the disk, we start off with that as | ||
| 342 | * our size of needed partitions */ | ||
| 343 | total_size = dinfo->skip_lba * dinfo->sect_size; | ||
| 344 | |||
| 345 | /* add up all the partition sizes and make sure it fits */ | ||
| 346 | for (cnt = 0; cnt < dinfo->num_parts; ++cnt) { | ||
| 347 | struct part_info *part = &dinfo->part_lst[cnt]; | ||
| 348 | if (part->len_kb != (uint32_t)-1) { | ||
| 349 | total_size += part->len_kb * 1024; | ||
| 350 | } else if (part->len_kb == 0) { | ||
| 351 | LOGE("Zero-size partition '%s' is invalid.", part->name); | ||
| 352 | goto fail; | ||
| 353 | } else { | ||
| 354 | /* the partition requests the rest of the disk. */ | ||
| 355 | if (cnt + 1 != dinfo->num_parts) { | ||
| 356 | LOGE("Only the last partition in the list can request to fill " | ||
| 357 | "the rest of disk."); | ||
| 358 | goto fail; | ||
| 359 | } | ||
| 360 | } | ||
| 361 | |||
| 362 | if (part->type != PC_PART_TYPE_LINUX) { | ||
| 363 | LOGE("Unknown partition type (0x%x) encountered for partition " | ||
| 364 | "'%s'\n", part->type, part->name); | ||
| 365 | goto fail; | ||
| 366 | } | ||
| 367 | } | ||
| 368 | |||
| 369 | /* only matters for disks, not files */ | ||
| 370 | if (S_ISBLK(stat.st_mode) && total_size > disk_size) { | ||
| 371 | LOGE("Total requested size of partitions (%llu) is greater than disk " | ||
| 372 | "size (%llu).", total_size, disk_size); | ||
| 373 | goto fail; | ||
| 374 | } | ||
| 375 | |||
| 376 | return fd; | ||
| 377 | |||
| 378 | fail: | ||
| 379 | close(fd); | ||
| 380 | return -1; | ||
| 381 | } | ||
| 382 | |||
| 383 | static int | ||
| 384 | validate_and_config(struct disk_info *dinfo, int *fd, struct write_list **lst) | ||
| 385 | { | ||
| 386 | *lst = NULL; | ||
| 387 | *fd = -1; | ||
| 388 | |||
| 389 | if ((*fd = validate(dinfo)) < 0) | ||
| 390 | return 1; | ||
| 391 | |||
| 392 | switch (dinfo->scheme) { | ||
| 393 | case PART_SCHEME_MBR: | ||
| 394 | *lst = config_mbr(dinfo); | ||
| 395 | return *lst == NULL; | ||
| 396 | case PART_SCHEME_GPT: | ||
| 397 | /* not supported yet */ | ||
| 398 | default: | ||
| 399 | LOGE("Uknown partition scheme."); | ||
| 400 | break; | ||
| 401 | } | ||
| 402 | |||
| 403 | close(*fd); | ||
| 404 | *lst = NULL; | ||
| 405 | return 1; | ||
| 406 | } | ||
| 407 | |||
| 408 | /* validate and process the disk layout configuration. | ||
| 409 | * This will cause an update to the partitions' start lba. | ||
| 410 | * | ||
| 411 | * Basically, this does the same thing as apply_disk_config in test mode, | ||
| 412 | * except that wlist_commit is not called to print out the data to be | ||
| 413 | * written. | ||
| 414 | */ | ||
| 415 | int | ||
| 416 | process_disk_config(struct disk_info *dinfo) | ||
| 417 | { | ||
| 418 | struct write_list *lst; | ||
| 419 | int fd; | ||
| 420 | |||
| 421 | if (validate_and_config(dinfo, &fd, &lst) != 0) | ||
| 422 | return 1; | ||
| 423 | |||
| 424 | close(fd); | ||
| 425 | wlist_free(lst); | ||
| 426 | return 0; | ||
| 427 | } | ||
| 428 | |||
| 429 | |||
| 430 | int | ||
| 431 | apply_disk_config(struct disk_info *dinfo, int test) | ||
| 432 | { | ||
| 433 | int fd; | ||
| 434 | struct write_list *wr_lst = NULL; | ||
| 435 | int rv; | ||
| 436 | |||
| 437 | if (validate_and_config(dinfo, &fd, &wr_lst) != 0) { | ||
| 438 | LOGE("Configuration is invalid."); | ||
| 439 | goto fail; | ||
| 440 | } | ||
| 441 | |||
| 442 | if ((rv = wlist_commit(fd, wr_lst, test)) >= 0) | ||
| 443 | rv = test ? 0 : sync_ptable(fd); | ||
| 444 | |||
| 445 | close(fd); | ||
| 446 | wlist_free(wr_lst); | ||
| 447 | return rv; | ||
| 448 | |||
| 449 | fail: | ||
| 450 | close(fd); | ||
| 451 | if (wr_lst) | ||
| 452 | wlist_free(wr_lst); | ||
| 453 | return 1; | ||
| 454 | } | ||
| 455 | |||
| 456 | int | ||
| 457 | dump_disk_config(struct disk_info *dinfo) | ||
| 458 | { | ||
| 459 | int cnt; | ||
| 460 | struct part_info *part; | ||
| 461 | |||
| 462 | printf("Device: %s\n", dinfo->device); | ||
| 463 | printf("Scheme: "); | ||
| 464 | switch (dinfo->scheme) { | ||
| 465 | case PART_SCHEME_MBR: | ||
| 466 | printf("MBR"); | ||
| 467 | break; | ||
| 468 | case PART_SCHEME_GPT: | ||
| 469 | printf("GPT (unsupported)"); | ||
| 470 | break; | ||
| 471 | default: | ||
| 472 | printf("Unknown"); | ||
| 473 | break; | ||
| 474 | } | ||
| 475 | printf ("\n"); | ||
| 476 | |||
| 477 | printf("Sector size: %d\n", dinfo->sect_size); | ||
| 478 | printf("Skip leading LBAs: %u\n", dinfo->skip_lba); | ||
| 479 | printf("Number of LBAs: %u\n", dinfo->num_lba); | ||
| 480 | printf("Partitions:\n"); | ||
| 481 | |||
| 482 | for (cnt = 0; cnt < dinfo->num_parts; ++cnt) { | ||
| 483 | part = &dinfo->part_lst[cnt]; | ||
| 484 | printf("\tname = %s\n", part->name); | ||
| 485 | printf("\t\tflags = %s\n", | ||
| 486 | part->flags & PART_ACTIVE_FLAG ? "Active" : "None"); | ||
| 487 | printf("\t\ttype = %s\n", | ||
| 488 | part->type == PC_PART_TYPE_LINUX ? "Linux" : "Unknown"); | ||
| 489 | if (part->len_kb == (uint32_t)-1) | ||
| 490 | printf("\t\tlen = rest of disk\n"); | ||
| 491 | else | ||
| 492 | printf("\t\tlen = %uKB\n", part->len_kb); | ||
| 493 | } | ||
| 494 | printf("Total number of partitions: %d\n", cnt); | ||
| 495 | printf("\n"); | ||
| 496 | |||
| 497 | return 0; | ||
| 498 | } | ||
| 499 | |||
| 500 | struct part_info * | ||
| 501 | find_part(struct disk_info *dinfo, const char *name) | ||
| 502 | { | ||
| 503 | struct part_info *pinfo; | ||
| 504 | int cnt; | ||
| 505 | |||
| 506 | for (cnt = 0; cnt < dinfo->num_parts; ++cnt) { | ||
| 507 | pinfo = &dinfo->part_lst[cnt]; | ||
| 508 | if (!strcmp(pinfo->name, name)) | ||
| 509 | return pinfo; | ||
| 510 | } | ||
| 511 | |||
| 512 | return NULL; | ||
| 513 | } | ||
| 514 | |||
| 515 | /* NOTE: If the returned ptr is non-NULL, it must be freed by the caller. */ | ||
| 516 | char * | ||
| 517 | find_part_device(struct disk_info *dinfo, const char *name) | ||
| 518 | { | ||
| 519 | switch (dinfo->scheme) { | ||
| 520 | case PART_SCHEME_MBR: | ||
| 521 | return find_mbr_part(dinfo, name); | ||
| 522 | case PART_SCHEME_GPT: | ||
| 523 | LOGE("GPT is presently not supported"); | ||
| 524 | break; | ||
| 525 | default: | ||
| 526 | LOGE("Unknown partition table scheme"); | ||
| 527 | break; | ||
| 528 | } | ||
| 529 | |||
| 530 | return NULL; | ||
| 531 | } |
libdiskconfig/diskconfig.h
(120 / 0)
|   | |||
| 1 | /* libs/diskconfig/diskconfig.h | ||
| 2 | * | ||
| 3 | * Copyright 2008, The Android Open Source Project | ||
| 4 | * | ||
| 5 | * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 6 | * you may not use this file except in compliance with the License. | ||
| 7 | * You may obtain a copy of the License at | ||
| 8 | * | ||
| 9 | * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 10 | * | ||
| 11 | * Unless required by applicable law or agreed to in writing, software | ||
| 12 | * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 14 | * See the License for the specific language governing permissions and | ||
| 15 | * limitations under the License. | ||
| 16 | */ | ||
| 17 | |||
| 18 | #ifndef __LIBS_DISKCONFIG_H | ||
| 19 | #define __LIBS_DISKCONFIG_H | ||
| 20 | |||
| 21 | #include <stdint.h> | ||
| 22 | |||
| 23 | #define MAX_NAME_LEN 512 | ||
| 24 | #define MAX_NUM_PARTS 16 | ||
| 25 | |||
| 26 | /* known partition schemes */ | ||
| 27 | #define PART_SCHEME_MBR 0x1 | ||
| 28 | #define PART_SCHEME_GPT 0x2 | ||
| 29 | |||
| 30 | /* PC Bios partition status */ | ||
| 31 | #define PC_PART_ACTIVE 0x80 | ||
| 32 | #define PC_PART_NORMAL 0x0 | ||
| 33 | |||
| 34 | /* Known (rather, used by us) partition types */ | ||
| 35 | #define PC_PART_TYPE_LINUX 0x83 | ||
| 36 | #define PC_PART_TYPE_EXTENDED 0x05 | ||
| 37 | |||
| 38 | #define PC_NUM_BOOT_RECORD_PARTS 4 | ||
| 39 | |||
| 40 | #define PC_EBR_LOGICAL_PART 0 | ||
| 41 | #define PC_EBR_NEXT_PTR_PART 1 | ||
| 42 | |||
| 43 | #define PC_BIOS_BOOT_SIG 0xAA55 | ||
| 44 | |||
| 45 | #define PC_MBR_DISK_OFFSET 0 | ||
| 46 | #define PC_MBR_SIZE 512 | ||
| 47 | |||
| 48 | #define PART_ACTIVE_FLAG 0x1 | ||
| 49 | |||
| 50 | struct chs { | ||
| 51 | uint8_t head; | ||
| 52 | uint8_t sector; | ||
| 53 | uint8_t cylinder; | ||
| 54 | } __attribute__((__packed__)); | ||
| 55 | |||
| 56 | /* 16 byte pc partition descriptor that sits in MBR and EPBR. | ||
| 57 | * Note: multi-byte entities have little-endian layout on disk */ | ||
| 58 | struct pc_partition { | ||
| 59 | uint8_t status; /* byte 0 */ | ||
| 60 | struct chs start; /* bytes 1-3 */ | ||
| 61 | uint8_t type; /* byte 4 */ | ||
| 62 | struct chs end; /* bytes 5-7 */ | ||
| 63 | uint32_t start_lba; /* bytes 8-11 */ | ||
| 64 | uint32_t len_lba; /* bytes 12-15 */ | ||
| 65 | } __attribute__((__packed__)); | ||
| 66 | |||
| 67 | struct pc_boot_record { | ||
| 68 | uint8_t code[440]; /* bytes 0-439 */ | ||
| 69 | uint32_t disk_sig; /* bytes 440-443 */ | ||
| 70 | uint16_t pad; /* bytes 444-445 */ | ||
| 71 | struct pc_partition ptable[PC_NUM_BOOT_RECORD_PARTS]; /* bytes 446-509 */ | ||
| 72 | uint16_t mbr_sig; /* bytes 510-511 */ | ||
| 73 | } __attribute__((__packed__)); | ||
| 74 | |||
| 75 | struct part_info { | ||
| 76 | char *name; | ||
| 77 | uint8_t flags; | ||
| 78 | uint8_t type; | ||
| 79 | uint32_t len_kb; /* in 1K-bytes */ | ||
| 80 | uint32_t start_lba; /* the LBA where this partition begins */ | ||
| 81 | }; | ||
| 82 | |||
| 83 | struct disk_info { | ||
| 84 | char *device; | ||
| 85 | uint8_t scheme; | ||
| 86 | int sect_size; /* expected sector size in bytes. MUST BE POWER OF 2 */ | ||
| 87 | uint32_t skip_lba; /* in sectors (1 unit of LBA) */ | ||
| 88 | uint32_t num_lba; /* the size of the disk in LBA units */ | ||
| 89 | struct part_info *part_lst; | ||
| 90 | int num_parts; | ||
| 91 | }; | ||
| 92 | |||
| 93 | struct write_list { | ||
| 94 | struct write_list *next; | ||
| 95 | loff_t offset; | ||
| 96 | uint32_t len; | ||
| 97 | uint8_t data[0]; | ||
| 98 | }; | ||
| 99 | |||
| 100 | |||
| 101 | struct write_list *alloc_wl(uint32_t data_len); | ||
| 102 | void free_wl(struct write_list *item); | ||
| 103 | struct write_list *wlist_add(struct write_list **lst, struct write_list *item); | ||
| 104 | void wlist_free(struct write_list *lst); | ||
| 105 | int wlist_commit(int fd, struct write_list *lst, int test); | ||
| 106 | |||
| 107 | struct disk_info *load_diskconfig(const char *fn, char *path_override); | ||
| 108 | int dump_disk_config(struct disk_info *dinfo); | ||
| 109 | int apply_disk_config(struct disk_info *dinfo, int test); | ||
| 110 | char *find_part_device(struct disk_info *dinfo, const char *name); | ||
| 111 | int process_disk_config(struct disk_info *dinfo); | ||
| 112 | struct part_info *find_part(struct disk_info *dinfo, const char *name); | ||
| 113 | |||
| 114 | int write_raw_image(const char *dst, const char *src, loff_t offset, int test); | ||
| 115 | |||
| 116 | /* For MBR partition schemes */ | ||
| 117 | struct write_list *config_mbr(struct disk_info *dinfo); | ||
| 118 | char *find_mbr_part(struct disk_info *dinfo, const char *name); | ||
| 119 | |||
| 120 | #endif /* __LIBS_DISKCONFIG_H */ |
libdiskconfig/diskutils.c
(117 / 0)
|   | |||
| 1 | /* libs/diskconfig/diskutils.c | ||
| 2 | * | ||
| 3 | * Copyright 2008, The Android Open Source Project | ||
| 4 | * | ||
| 5 | * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 6 | * you may not use this file except in compliance with the License. | ||
| 7 | * You may obtain a copy of the License at | ||
| 8 | * | ||
| 9 | * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 10 | * | ||
| 11 | * Unless required by applicable law or agreed to in writing, software | ||
| 12 | * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 14 | * See the License for the specific language governing permissions and | ||
| 15 | * limitations under the License. | ||
| 16 | */ | ||
| 17 | |||
| 18 | #define LOG_TAG "diskutils" | ||
| 19 | |||
| 20 | #include <errno.h> | ||
| 21 | #include <fcntl.h> | ||
| 22 | #include <stdio.h> | ||
| 23 | #include <stdlib.h> | ||
| 24 | #include <string.h> | ||
| 25 | #include <unistd.h> | ||
| 26 | #include <sys/stat.h> | ||
| 27 | |||
| 28 | #include <cutils/log.h> | ||
| 29 | |||
| 30 | #include "diskconfig.h" | ||
| 31 | |||
| 32 | int | ||
| 33 | write_raw_image(const char *dst, const char *src, loff_t offset, int test) | ||
| 34 | { | ||
| 35 | int dst_fd = -1; | ||
| 36 | int src_fd = -1; | ||
| 37 | uint8_t buffer[2048]; | ||
| 38 | int nr_bytes; | ||
| 39 | int tmp; | ||
| 40 | int done = 0; | ||
| 41 | uint64_t total = 0; | ||
| 42 | |||
| 43 | LOGI("Writing RAW image '%s' to '%s' (offset=%llu)", src, dst, offset); | ||
| 44 | if ((src_fd = open(src, O_RDONLY)) < 0) { | ||
| 45 | LOGE("Could not open %s for reading (errno=%d).", src, errno); | ||
| 46 | goto fail; | ||
| 47 | } | ||
| 48 | |||
| 49 | if (!test) { | ||
| 50 | if ((dst_fd = open(dst, O_RDWR)) < 0) { | ||
| 51 | LOGE("Could not open '%s' for read/write (errno=%d).", dst, errno); | ||
| 52 | goto fail; | ||
| 53 | } | ||
| 54 | |||
| 55 | if (lseek64(dst_fd, offset, SEEK_SET) != offset) { | ||
| 56 | LOGE("Could not seek to offset %lld in %s.", offset, dst); | ||
| 57 | goto fail; | ||
| 58 | } | ||
| 59 | } | ||
| 60 | |||
| 61 | while (!done) { | ||
| 62 | if ((nr_bytes = read(src_fd, buffer, sizeof(buffer))) < 0) { | ||
| 63 | /* XXX: Should we not even bother with EINTR? */ | ||
| 64 | if (errno == EINTR) | ||
| 65 | continue; | ||
| 66 | LOGE("Error (%d) while reading from '%s'", errno, src); | ||
| 67 | goto fail; | ||
| 68 | } | ||
| 69 | |||
| 70 | if (!nr_bytes) { | ||
| 71 | /* we're done. */ | ||
| 72 | done = 1; | ||
| 73 | break; | ||
| 74 | } | ||
| 75 | |||
| 76 | total += nr_bytes; | ||
| 77 | |||
| 78 | /* skip the write loop if we're testing */ | ||
| 79 | if (test) | ||
| 80 | nr_bytes = 0; | ||
| 81 | |||
| 82 | while (nr_bytes > 0) { | ||
| 83 | if ((tmp = write(dst_fd, buffer, nr_bytes)) < 0) { | ||
| 84 | /* XXX: Should we not even bother with EINTR? */ | ||
| 85 | if (errno == EINTR) | ||
| 86 | continue; | ||
| 87 | LOGE("Error (%d) while writing to '%s'", errno, dst); | ||
| 88 | goto fail; | ||
| 89 | } | ||
| 90 | if (!tmp) | ||
| 91 | continue; | ||
| 92 | nr_bytes -= tmp; | ||
| 93 | } | ||
| 94 | } | ||
| 95 | |||
| 96 | if (!done) { | ||
| 97 | LOGE("Exited read/write loop without setting flag! WTF?!"); | ||
| 98 | goto fail; | ||
| 99 | } | ||
| 100 | |||
| 101 | if (dst_fd >= 0) | ||
| 102 | fsync(dst_fd); | ||
| 103 | |||
| 104 | LOGI("Wrote %llu bytes to %s @ %lld", total, dst, offset); | ||
| 105 | |||
| 106 | close(src_fd); | ||
| 107 | if (dst_fd >= 0) | ||
| 108 | close(dst_fd); | ||
| 109 | return 0; | ||
| 110 | |||
| 111 | fail: | ||
| 112 | if (dst_fd >= 0) | ||
| 113 | close(dst_fd); | ||
| 114 | if (src_fd >= 0) | ||
| 115 | close(src_fd); | ||
| 116 | return 1; | ||
| 117 | } |
libdiskconfig/dump_diskconfig.c
(41 / 0)
|   | |||
| 1 | /* libs/diskconfig/dump_diskconfig.c | ||
| 2 | * | ||
| 3 | * Copyright 2008, The Android Open Source Project | ||
| 4 | * | ||
| 5 | * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 6 | * you may not use this file except in compliance with the License. | ||
| 7 | * You may obtain a copy of the License at | ||
| 8 | * | ||
| 9 | * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 10 | * | ||
| 11 | * Unless required by applicable law or agreed to in writing, software | ||
| 12 | * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 14 | * See the License for the specific language governing permissions and | ||
| 15 | * limitations under the License. | ||
| 16 | */ | ||
| 17 | |||
| 18 | #define LOG_TAG "dump_diskconfig" | ||
| 19 | #include <stdio.h> | ||
| 20 | |||
| 21 | #include <cutils/log.h> | ||
| 22 | |||
| 23 | #include "diskconfig.h" | ||
| 24 | |||
| 25 | int | ||
| 26 | main(int argc, char *argv[]) | ||
| 27 | { | ||
| 28 | struct disk_info *dinfo; | ||
| 29 | |||
| 30 | if (argc < 2) { | ||
| 31 | LOGE("usage: %s <conf file>", argv[0]); | ||
| 32 | return 1; | ||
| 33 | } | ||
| 34 | |||
| 35 | if (!(dinfo = load_diskconfig(argv[1], NULL))) | ||
| 36 | return 1; | ||
| 37 | |||
| 38 | dump_disk_config(dinfo); | ||
| 39 | |||
| 40 | return 0; | ||
| 41 | } |
libdiskconfig/write_lst.c
(92 / 0)
|   | |||
| 1 | /* libs/diskconfig/write_lst.c | ||
| 2 | * | ||
| 3 | * Copyright 2008, The Android Open Source Project | ||
| 4 | * | ||
| 5 | * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 6 | * you may not use this file except in compliance with the License. | ||
| 7 | * You may obtain a copy of the License at | ||
| 8 | * | ||
| 9 | * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 10 | * | ||
| 11 | * Unless required by applicable law or agreed to in writing, software | ||
| 12 | * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 14 | * See the License for the specific language governing permissions and | ||
| 15 | * limitations under the License. | ||
| 16 | */ | ||
| 17 | |||
| 18 | #define LOG_TAG "write_lst" | ||
| 19 | #include <sys/types.h> | ||
| 20 | #include <stdint.h> | ||
| 21 | #include <stdio.h> | ||
| 22 | #include <stdlib.h> | ||
| 23 | #include <unistd.h> | ||
| 24 | |||
| 25 | #include <cutils/log.h> | ||
| 26 | |||
| 27 | #include "diskconfig.h" | ||
| 28 | |||
| 29 | struct write_list * | ||
| 30 | alloc_wl(uint32_t data_len) | ||
| 31 | { | ||
| 32 | struct write_list *item; | ||
| 33 | |||
| 34 | if (!(item = malloc(sizeof(struct write_list) + data_len))) { | ||
| 35 | LOGE("Unable to allocate memory."); | ||
| 36 | return NULL; | ||
| 37 | } | ||
| 38 | |||
| 39 | item->len = data_len; | ||
| 40 | return item; | ||
| 41 | } | ||
| 42 | |||
| 43 | void | ||
| 44 | free_wl(struct write_list *item) | ||
| 45 | { | ||
| 46 | if (item) | ||
| 47 | free(item); | ||
| 48 | } | ||
| 49 | |||
| 50 | struct write_list * | ||
| 51 | wlist_add(struct write_list **lst, struct write_list *item) | ||
| 52 | { | ||
| 53 | item->next = (*lst); | ||
| 54 | *lst = item; | ||
| 55 | return item; | ||
| 56 | } | ||
| 57 | |||
| 58 | void | ||
| 59 | wlist_free(struct write_list *lst) | ||
| 60 | { | ||
| 61 | struct write_list *temp_wr; | ||
| 62 | while (lst) { | ||
| 63 | temp_wr = lst->next; | ||
| 64 | free_wl(lst); | ||
| 65 | lst = temp_wr; | ||
| 66 | } | ||
| 67 | } | ||
| 68 | |||
| 69 | int | ||
| 70 | wlist_commit(int fd, struct write_list *lst, int test) | ||
| 71 | { | ||
| 72 | for(; lst; lst = lst->next) { | ||
| 73 | if (lseek64(fd, lst->offset, SEEK_SET) != (loff_t)lst->offset) { | ||
| 74 | LOGE("Cannot seek to the specified position (%lld).", lst->offset); | ||
| 75 | goto fail; | ||
| 76 | } | ||
| 77 | |||
| 78 | if (!test) { | ||
| 79 | if (write(fd, lst->data, lst->len) != (int)lst->len) { | ||
| 80 | LOGE("Failed writing %u bytes at position %lld.", lst->len, | ||
| 81 | lst->offset); | ||
| 82 | goto fail; | ||
| 83 | } | ||
| 84 | } else | ||
| 85 | LOGI("Would write %d bytes @ offset %lld.", lst->len, lst->offset); | ||
| 86 | } | ||
| 87 | |||
| 88 | return 0; | ||
| 89 | |||
| 90 | fail: | ||
| 91 | return -1; | ||
| 92 | } |

