blob: b5db25802353fb8ae18e833f4c1a5d1745ca2dc3 [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"
Stephen Barber8e3381b2018-01-30 13:04:44 -0800111 # Create container stateful and shared dirs.
112 sudo mkdir "${rootfs}"/mnt/{stateful,shared}
Stephen Barberf9e93a32017-05-17 15:41:57 -0700113 # Copy the dev_image into its location at /usr/local.
114 sudo cp -aT "${stateful}"/dev_image "${rootfs}/usr/local"
115
Stephen Barber1c6177a2017-07-20 22:11:09 -0700116 # 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 Barberf9e93a32017-05-17 15:41:57 -0700120 # 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 Barber8e3381b2018-01-30 13:04:44 -0800124 sudo cp "${rootfs}/opt/google/chrome/resources/about_os_credits.html" \
125 "${output_dir}/about_os_credits.html"
Stephen Barberf9e93a32017-05-17 15:41:57 -0700126
127 case "${fs_type}" in
128 squashfs)
129 sudo mksquashfs "${rootfs}" "${rootfs_img}" -comp lzo
130 ;;
131 ext4)
132 # Start with 300MB, then shrink.
133 local image_size=300
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
153upload() {
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 Barber3f7688f2017-02-16 11:23:44 -0800183}
184
185main() {
Stephen Barberf9e93a32017-05-17 15:41:57 -0700186 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 Barber3f7688f2017-02-16 11:23:44 -0800190 fi
191
Stephen Barberf9e93a32017-05-17 15:41:57 -0700192 if [[ "${FLAGS_arch}" != "amd64" && "${FLAGS_arch}" != "armv8" ]]; then
193 die_notrace "Architecture '${FLAGS_arch}' is not valid. Options are 'amd64' and 'armv8'"
Stephen Barber3f7688f2017-02-16 11:23:44 -0800194 fi
195
Stephen Barberf9e93a32017-05-17 15:41:57 -0700196 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 Barber3f7688f2017-02-16 11:23:44 -0800226}
227
228main "$@"