blob: 1770084fc0d8abb196ac60aefab4f34bd69b2a4a [file] [log] [blame]
Stephen Barber3f7688f2017-02-16 11:23:44 -08001#!/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
6SCRIPT_ROOT=$(dirname "$(readlink -f "$0")")
7. "${SCRIPT_ROOT}/build_library/build_common.sh" || exit 1
Stephen Barberf9e93a32017-05-17 15:41:57 -07008. "${SCRIPT_ROOT}/build_library/filesystem_util.sh" || exit 1
Stephen Barber3f7688f2017-02-16 11:23:44 -08009
10assert_inside_chroot "$@"
11
Stephen Barberf9e93a32017-05-17 15:41:57 -070012DEFINE_string arch "amd64" \
13 "Architecture of the VM image"
14DEFINE_string filesystem "ext4" \
15 "Filesystem for the rootfs image"
16DEFINE_string image "" \
17 "Chromium OS disk image to build the Termina image from"
18DEFINE_string output "" \
19 "Output directory"
20DEFINE_boolean upload ${FLAGS_FALSE} \
21 "Upload resulting image to Google Storage" u
22DEFINE_boolean test_image ${FLAGS_FALSE} \
23 "True if the image is a test image" t
Stephen Barber3f7688f2017-02-16 11:23:44 -080024
25FLAGS_HELP="USAGE: ${SCRIPT_NAME} [flags]
26
Stephen Barberf9e93a32017-05-17 15:41:57 -070027To 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 Barber3f7688f2017-02-16 11:23:44 -080030"
31FLAGS "$@" || exit 1
32eval set -- "${FLAGS_ARGV}"
33switch_to_strict_mode
34
Stephen Barberf9e93a32017-05-17 15:41:57 -070035get_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 Barber3f7688f2017-02-16 11:23:44 -080040
Stephen Barberf9e93a32017-05-17 15:41:57 -070041is_test_image() {
42 local output_dir="$1"
43 grep -q testimage-channel "${output_dir}"/lsb-release
44}
Stephen Barber3f7688f2017-02-16 11:23:44 -080045
Stephen Barberf9e93a32017-05-17 15:41:57 -070046read_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 Barber3f7688f2017-02-16 11:23:44 -080056 esac
57
Stephen Barberf9e93a32017-05-17 15:41:57 -070058 local raw="$(xxd -g ${size} -e -s ${offset} -l ${size} ${disk} | cut -d' ' -f2)"
59 local result="$(( 16#${raw} ))"
Stephen Barber3f7688f2017-02-16 11:23:44 -080060
Stephen Barberf9e93a32017-05-17 15:41:57 -070061 echo "${result}"
Stephen Barber3f7688f2017-02-16 11:23:44 -080062}
63
Stephen Barberf9e93a32017-05-17 15:41:57 -070064extract_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 Barber3f7688f2017-02-16 11:23:44 -080083}
84
Stephen Barberf9e93a32017-05-17 15:41:57 -070085# Repack termina rootfs.
86repack_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"
111 # Create container rootfs and private dirs.
112 sudo mkdir "${rootfs}"/mnt/{container_rootfs,container_private}
113 # Copy the dev_image into its location at /usr/local.
114 sudo cp -aT "${stateful}"/dev_image "${rootfs}/usr/local"
115
116 # TODO(smbarber): Don't put kernel onto host rootfs once we can
117 # boot 64-bit kernels directly.
118 sudo cp "${rootfs}/boot/vmlinuz" "${output_dir}/vm_kernel"
119 # Remove vmlinuz from the rootfs since it's already on the host rootfs.
120 sudo rm -rf "${rootfs}"/boot/vmlinuz*
121
122 sudo cp "${rootfs}/etc/lsb-release" "${output_dir}/lsb-release"
123
124 case "${fs_type}" in
125 squashfs)
126 sudo mksquashfs "${rootfs}" "${rootfs_img}" -comp lzo
127 ;;
128 ext4)
129 # Start with 300MB, then shrink.
130 local image_size=300
131 truncate --size "${image_size}M" "${rootfs_img}"
132 /sbin/mkfs.ext4 -F -m 0 -i 16384 -b 1024 -O "^has_journal" "${rootfs_img}"
133 local rootfs_mnt="$(mktemp -d)"
134 fs_mount "${rootfs_img}" "${rootfs_mnt}" ext4 rw
135 sudo cp -aT "${rootfs}" "${rootfs_mnt}"
136 fs_umount "${rootfs_img}" "${rootfs_mnt}" ext4 rw
137 # Shrink to minimum size.
138 /sbin/e2fsck -f "${rootfs_img}"
139 /sbin/resize2fs -M "${rootfs_img}"
140 rmdir "${rootfs_mnt}"
141 ;;
142 *)
143 die_notrace "Unsupported fs type ${fs_type}."
144 ;;
145 esac
146
147 sudo rm -rf "${rootfs}" "${stateful}"
148}
149
150upload() {
151 local output_dir="$1"
152 local gspath="gs://chromeos-localmirror/distfiles"
153
154 local release="$(get_version "${output_dir}")"
155 release="${release/-/_}"
156 if is_test_image "${output_dir}"; then
157 local pkg_name="termina-vm-image-test_${FLAGS_arch}"
158 else
159 local pkg_name="termina-vm-image_${FLAGS_arch}"
160 fi
161
162 local target_file="${pkg_name}-${release}.tar.xz"
163
164 info "Will upload: '${target_file}'"
165
166 if ! prompt_yesno "Are you sure you want to upload this image?"; then
167 echo "Not uploading image."
168 return
169 fi
170
171 info "Generating tarball..."
172 tar caf "${output_dir}/termina-vm-image.tar.xz" -C "${output_dir}" \
173 vm_kernel vm_rootfs.img
174
175 if ! gsutil cp -a public-read "${output_dir}/termina-vm-image.tar.xz" \
176 "${gspath}/${target_file}"; then
177 die_notrace "Couldn't upload '${gspath}/${target_file}'."
178 fi
179 info "Uploaded to ${gspath}/${target_file}"
Stephen Barber3f7688f2017-02-16 11:23:44 -0800180}
181
182main() {
Stephen Barberf9e93a32017-05-17 15:41:57 -0700183 if [[ -z "${FLAGS_image}" ]]; then
184 die_notrace "Please provide an image using --image"
185 elif [[ ! -f "${FLAGS_image}" ]]; then
186 die_notrace "'${FLAGS_image}' does not exist"
Stephen Barber3f7688f2017-02-16 11:23:44 -0800187 fi
188
Stephen Barberf9e93a32017-05-17 15:41:57 -0700189 if [[ "${FLAGS_arch}" != "amd64" && "${FLAGS_arch}" != "armv8" ]]; then
190 die_notrace "Architecture '${FLAGS_arch}' is not valid. Options are 'amd64' and 'armv8'"
Stephen Barber3f7688f2017-02-16 11:23:44 -0800191 fi
192
Stephen Barberf9e93a32017-05-17 15:41:57 -0700193 case "${FLAGS_filesystem}" in
194 squashfs|ext4)
195 ;;
196 *)
197 die_notrace "Filesystem '${FLAGS_filesystem}' is not valid. Options are 'squashfs' and 'ext4'"
198 ;;
199 esac
200
201 if [[ -z "${FLAGS_output}" ]]; then
202 die_notrace "Output directory was not specified"
203 elif [[ -e "${FLAGS_output}" ]]; then
204 die_notrace "${FLAGS_output} already exists"
205 fi
206
207 local output_dir="${FLAGS_output}"
208 local stateful_img="${output_dir}/vm_stateful.img"
209 local rootfs_img="${output_dir}/vm_rootfs.img"
210 local image="${FLAGS_image}"
211
212 mkdir -p "${output_dir}"
213 extract_squashfs_partition "${image}" "3" "${rootfs_img}"
214 extract_squashfs_partition "${image}" "1" "${stateful_img}"
215
216 repack_rootfs "${output_dir}" "${FLAGS_filesystem}"
217
218 if [[ "${FLAGS_upload}" -eq "${FLAGS_TRUE}" ]]; then
219 upload "${output_dir}"
220 fi
221
222 info "Done! The resulting image is in '${output_dir}'"
Stephen Barber3f7688f2017-02-16 11:23:44 -0800223}
224
225main "$@"