Stephen Barber | 3f7688f | 2017-02-16 11:23:44 -0800 | [diff] [blame] | 1 | #!/bin/bash |
| 2 | # Copyright 2017 The Chromium OS Authors. All rights reserved. |
| 3 | # Use of this source code is governed by a BSD-style license that can be |
| 4 | # found in the LICENSE file. |
| 5 | |
| 6 | SCRIPT_ROOT=$(dirname "$(readlink -f "$0")") |
| 7 | . "${SCRIPT_ROOT}/build_library/build_common.sh" || exit 1 |
Stephen Barber | f9e93a3 | 2017-05-17 15:41:57 -0700 | [diff] [blame] | 8 | . "${SCRIPT_ROOT}/build_library/filesystem_util.sh" || exit 1 |
Stephen Barber | 3f7688f | 2017-02-16 11:23:44 -0800 | [diff] [blame] | 9 | |
| 10 | assert_inside_chroot "$@" |
| 11 | |
Stephen Barber | f9e93a3 | 2017-05-17 15:41:57 -0700 | [diff] [blame] | 12 | DEFINE_string arch "amd64" \ |
| 13 | "Architecture of the VM image" |
| 14 | DEFINE_string filesystem "ext4" \ |
| 15 | "Filesystem for the rootfs image" |
| 16 | DEFINE_string image "" \ |
| 17 | "Chromium OS disk image to build the Termina image from" |
| 18 | DEFINE_string output "" \ |
| 19 | "Output directory" |
| 20 | DEFINE_boolean upload ${FLAGS_FALSE} \ |
| 21 | "Upload resulting image to Google Storage" u |
| 22 | DEFINE_boolean test_image ${FLAGS_FALSE} \ |
| 23 | "True if the image is a test image" t |
Stephen Barber | 3f7688f | 2017-02-16 11:23:44 -0800 | [diff] [blame] | 24 | |
| 25 | FLAGS_HELP="USAGE: ${SCRIPT_NAME} [flags] |
| 26 | |
Stephen Barber | f9e93a3 | 2017-05-17 15:41:57 -0700 | [diff] [blame] | 27 | To build a tatl test image, try: |
| 28 | $ ./build_image --board=tatl test |
| 29 | $ ${SCRIPT_NAME} --image=../build/images/tatl/latest/chromiumos_test_image.bin --output=tatl |
Stephen Barber | 3f7688f | 2017-02-16 11:23:44 -0800 | [diff] [blame] | 30 | " |
| 31 | FLAGS "$@" || exit 1 |
| 32 | eval set -- "${FLAGS_ARGV}" |
| 33 | switch_to_strict_mode |
| 34 | |
Stephen Barber | f9e93a3 | 2017-05-17 15:41:57 -0700 | [diff] [blame] | 35 | get_version() { |
| 36 | local output_dir="$1" |
| 37 | awk -F'=' -v key='CHROMEOS_RELEASE_VERSION' '$1==key { print $2 }' \ |
| 38 | "${output_dir}"/lsb-release |
| 39 | } |
Stephen Barber | 3f7688f | 2017-02-16 11:23:44 -0800 | [diff] [blame] | 40 | |
Stephen Barber | f9e93a3 | 2017-05-17 15:41:57 -0700 | [diff] [blame] | 41 | is_test_image() { |
| 42 | local output_dir="$1" |
| 43 | grep -q testimage-channel "${output_dir}"/lsb-release |
| 44 | } |
Stephen Barber | 3f7688f | 2017-02-16 11:23:44 -0800 | [diff] [blame] | 45 | |
Stephen Barber | f9e93a3 | 2017-05-17 15:41:57 -0700 | [diff] [blame] | 46 | read_le_int() { |
| 47 | local disk="$1" |
| 48 | local offset="$2" |
| 49 | local size="$3" |
| 50 | |
| 51 | case "${size}" in |
| 52 | 1 | 2 | 4 | 8) |
| 53 | ;; |
| 54 | *) die "${size} is not a valid int size to read" |
| 55 | ;; |
Stephen Barber | 3f7688f | 2017-02-16 11:23:44 -0800 | [diff] [blame] | 56 | esac |
| 57 | |
Stephen Barber | f9e93a3 | 2017-05-17 15:41:57 -0700 | [diff] [blame] | 58 | local raw="$(xxd -g ${size} -e -s ${offset} -l ${size} ${disk} | cut -d' ' -f2)" |
| 59 | local result="$(( 16#${raw} ))" |
Stephen Barber | 3f7688f | 2017-02-16 11:23:44 -0800 | [diff] [blame] | 60 | |
Stephen Barber | f9e93a3 | 2017-05-17 15:41:57 -0700 | [diff] [blame] | 61 | echo "${result}" |
Stephen Barber | 3f7688f | 2017-02-16 11:23:44 -0800 | [diff] [blame] | 62 | } |
| 63 | |
Stephen Barber | f9e93a3 | 2017-05-17 15:41:57 -0700 | [diff] [blame] | 64 | extract_squashfs_partition() { |
| 65 | local src_disk="$1" |
| 66 | local src_part="$2" |
| 67 | local dst_file="$3" |
| 68 | |
| 69 | local part_start_blks="$(cgpt show -i "${src_part}" -b "${src_disk}")" |
| 70 | local part_start_bytes="$(( part_start_blks * 512 ))" |
| 71 | local part_size_blks="$(cgpt show -i "${src_part}" -s "${src_disk}")" |
| 72 | local part_size_bytes="$(( part_size_blks * 512 ))" |
| 73 | |
| 74 | # To be sure we're extracting a squashfs partition, verify the magic. |
| 75 | # See fs/squashfs/squashfs_fs.h. |
| 76 | local magic="$(read_le_int ${src_disk} ${part_start_bytes} 4)" |
| 77 | if [[ "${magic}" -ne 0x73717368 ]]; then |
| 78 | die "Partition ${src_part} doesn't look like a squashfs partition" |
| 79 | fi |
| 80 | |
| 81 | dd if="${src_disk}" of="${dst_file}" skip="${part_start_bytes}c" \ |
| 82 | count="${part_size_bytes}c" iflag=skip_bytes,count_bytes |
Stephen Barber | 3f7688f | 2017-02-16 11:23:44 -0800 | [diff] [blame] | 83 | } |
| 84 | |
Stephen Barber | f9e93a3 | 2017-05-17 15:41:57 -0700 | [diff] [blame] | 85 | # Repack termina rootfs. |
| 86 | repack_rootfs() { |
| 87 | local output_dir="$1" |
| 88 | local fs_type="$2" |
| 89 | local rootfs_img="${output_dir}/vm_rootfs.img" |
| 90 | local stateful_img="${output_dir}/vm_stateful.img" |
| 91 | |
| 92 | # Create image in a temporary directory to avoid the need for extra space |
| 93 | # on the final rootfs. |
| 94 | local rootfs="${output_dir}/rootfs" |
| 95 | local stateful="${output_dir}/stateful" |
| 96 | |
| 97 | sudo unsquashfs -d "${rootfs}" "${rootfs_img}" |
| 98 | sudo unsquashfs -d "${stateful}" "${stateful_img}" |
| 99 | |
| 100 | # Remove source images. |
| 101 | sudo rm -f "${rootfs_img}" "${stateful_img}" |
| 102 | |
| 103 | # Fix up rootfs. |
| 104 | |
| 105 | # Remove efi cruft. |
| 106 | sudo rm -rf "${rootfs}/boot"/{efi,syslinux} |
| 107 | # Don't need firmware if you don't have hardware! |
| 108 | sudo rm -rf "${rootfs}/lib/firmware" |
| 109 | # Get rid of stateful, it's not needed on termina. |
| 110 | sudo rm -rf "${rootfs}/mnt/stateful_partition" |
Stephen Barber | 8e3381b | 2018-01-30 13:04:44 -0800 | [diff] [blame] | 111 | # Create container stateful and shared dirs. |
| 112 | sudo mkdir "${rootfs}"/mnt/{stateful,shared} |
Stephen Barber | f9e93a3 | 2017-05-17 15:41:57 -0700 | [diff] [blame] | 113 | # Copy the dev_image into its location at /usr/local. |
| 114 | sudo cp -aT "${stateful}"/dev_image "${rootfs}/usr/local" |
| 115 | |
Stephen Barber | 1c6177a | 2017-07-20 22:11:09 -0700 | [diff] [blame] | 116 | # TODO(smbarber): Remove vmlinuz once kvmtool is removed. |
| 117 | cp "${rootfs}/boot/vmlinuz" "${output_dir}/vmlinuz" |
| 118 | "${CHROOT_TRUNK_DIR}"/src/third_party/kernel/v4.4/scripts/extract-vmlinux \ |
| 119 | "${output_dir}/vmlinuz" > "${output_dir}/vm_kernel" |
Stephen Barber | f9e93a3 | 2017-05-17 15:41:57 -0700 | [diff] [blame] | 120 | # Remove vmlinuz from the rootfs since it's already on the host rootfs. |
| 121 | sudo rm -rf "${rootfs}"/boot/vmlinuz* |
| 122 | |
| 123 | sudo cp "${rootfs}/etc/lsb-release" "${output_dir}/lsb-release" |
Stephen Barber | 8e3381b | 2018-01-30 13:04:44 -0800 | [diff] [blame] | 124 | sudo cp "${rootfs}/opt/google/chrome/resources/about_os_credits.html" \ |
| 125 | "${output_dir}/about_os_credits.html" |
Stephen Barber | f9e93a3 | 2017-05-17 15:41:57 -0700 | [diff] [blame] | 126 | |
| 127 | case "${fs_type}" in |
| 128 | squashfs) |
| 129 | sudo mksquashfs "${rootfs}" "${rootfs_img}" -comp lzo |
| 130 | ;; |
| 131 | ext4) |
Chirantan Ekbote | 694afec | 2018-04-23 16:53:19 -0700 | [diff] [blame^] | 132 | # Start with 400MB, then shrink. |
| 133 | local image_size=400 |
Stephen Barber | f9e93a3 | 2017-05-17 15:41:57 -0700 | [diff] [blame] | 134 | truncate --size "${image_size}M" "${rootfs_img}" |
| 135 | /sbin/mkfs.ext4 -F -m 0 -i 16384 -b 1024 -O "^has_journal" "${rootfs_img}" |
| 136 | local rootfs_mnt="$(mktemp -d)" |
| 137 | fs_mount "${rootfs_img}" "${rootfs_mnt}" ext4 rw |
| 138 | sudo cp -aT "${rootfs}" "${rootfs_mnt}" |
| 139 | fs_umount "${rootfs_img}" "${rootfs_mnt}" ext4 rw |
| 140 | # Shrink to minimum size. |
| 141 | /sbin/e2fsck -f "${rootfs_img}" |
| 142 | /sbin/resize2fs -M "${rootfs_img}" |
| 143 | rmdir "${rootfs_mnt}" |
| 144 | ;; |
| 145 | *) |
| 146 | die_notrace "Unsupported fs type ${fs_type}." |
| 147 | ;; |
| 148 | esac |
| 149 | |
| 150 | sudo rm -rf "${rootfs}" "${stateful}" |
| 151 | } |
| 152 | |
| 153 | upload() { |
| 154 | local output_dir="$1" |
| 155 | local gspath="gs://chromeos-localmirror/distfiles" |
| 156 | |
| 157 | local release="$(get_version "${output_dir}")" |
| 158 | release="${release/-/_}" |
| 159 | if is_test_image "${output_dir}"; then |
| 160 | local pkg_name="termina-vm-image-test_${FLAGS_arch}" |
| 161 | else |
| 162 | local pkg_name="termina-vm-image_${FLAGS_arch}" |
| 163 | fi |
| 164 | |
| 165 | local target_file="${pkg_name}-${release}.tar.xz" |
| 166 | |
| 167 | info "Will upload: '${target_file}'" |
| 168 | |
| 169 | if ! prompt_yesno "Are you sure you want to upload this image?"; then |
| 170 | echo "Not uploading image." |
| 171 | return |
| 172 | fi |
| 173 | |
| 174 | info "Generating tarball..." |
| 175 | tar caf "${output_dir}/termina-vm-image.tar.xz" -C "${output_dir}" \ |
| 176 | vm_kernel vm_rootfs.img |
| 177 | |
| 178 | if ! gsutil cp -a public-read "${output_dir}/termina-vm-image.tar.xz" \ |
| 179 | "${gspath}/${target_file}"; then |
| 180 | die_notrace "Couldn't upload '${gspath}/${target_file}'." |
| 181 | fi |
| 182 | info "Uploaded to ${gspath}/${target_file}" |
Stephen Barber | 3f7688f | 2017-02-16 11:23:44 -0800 | [diff] [blame] | 183 | } |
| 184 | |
| 185 | main() { |
Stephen Barber | f9e93a3 | 2017-05-17 15:41:57 -0700 | [diff] [blame] | 186 | if [[ -z "${FLAGS_image}" ]]; then |
| 187 | die_notrace "Please provide an image using --image" |
| 188 | elif [[ ! -f "${FLAGS_image}" ]]; then |
| 189 | die_notrace "'${FLAGS_image}' does not exist" |
Stephen Barber | 3f7688f | 2017-02-16 11:23:44 -0800 | [diff] [blame] | 190 | fi |
| 191 | |
Stephen Barber | f9e93a3 | 2017-05-17 15:41:57 -0700 | [diff] [blame] | 192 | if [[ "${FLAGS_arch}" != "amd64" && "${FLAGS_arch}" != "armv8" ]]; then |
| 193 | die_notrace "Architecture '${FLAGS_arch}' is not valid. Options are 'amd64' and 'armv8'" |
Stephen Barber | 3f7688f | 2017-02-16 11:23:44 -0800 | [diff] [blame] | 194 | fi |
| 195 | |
Stephen Barber | f9e93a3 | 2017-05-17 15:41:57 -0700 | [diff] [blame] | 196 | case "${FLAGS_filesystem}" in |
| 197 | squashfs|ext4) |
| 198 | ;; |
| 199 | *) |
| 200 | die_notrace "Filesystem '${FLAGS_filesystem}' is not valid. Options are 'squashfs' and 'ext4'" |
| 201 | ;; |
| 202 | esac |
| 203 | |
| 204 | if [[ -z "${FLAGS_output}" ]]; then |
| 205 | die_notrace "Output directory was not specified" |
| 206 | elif [[ -e "${FLAGS_output}" ]]; then |
| 207 | die_notrace "${FLAGS_output} already exists" |
| 208 | fi |
| 209 | |
| 210 | local output_dir="${FLAGS_output}" |
| 211 | local stateful_img="${output_dir}/vm_stateful.img" |
| 212 | local rootfs_img="${output_dir}/vm_rootfs.img" |
| 213 | local image="${FLAGS_image}" |
| 214 | |
| 215 | mkdir -p "${output_dir}" |
| 216 | extract_squashfs_partition "${image}" "3" "${rootfs_img}" |
| 217 | extract_squashfs_partition "${image}" "1" "${stateful_img}" |
| 218 | |
| 219 | repack_rootfs "${output_dir}" "${FLAGS_filesystem}" |
| 220 | |
| 221 | if [[ "${FLAGS_upload}" -eq "${FLAGS_TRUE}" ]]; then |
| 222 | upload "${output_dir}" |
| 223 | fi |
| 224 | |
| 225 | info "Done! The resulting image is in '${output_dir}'" |
Stephen Barber | 3f7688f | 2017-02-16 11:23:44 -0800 | [diff] [blame] | 226 | } |
| 227 | |
| 228 | main "$@" |