blob: 067e12406a8530a9e622138a6264adebf0875598 [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"
Stephen Barberf9e93a32017-05-17 15:41:57 -070020DEFINE_boolean test_image ${FLAGS_FALSE} \
21 "True if the image is a test image" t
Stephen Barber3f7688f2017-02-16 11:23:44 -080022
23FLAGS_HELP="USAGE: ${SCRIPT_NAME} [flags]
24
Stephen Barberf9e93a32017-05-17 15:41:57 -070025To build a tatl test image, try:
26$ ./build_image --board=tatl test
27$ ${SCRIPT_NAME} --image=../build/images/tatl/latest/chromiumos_test_image.bin --output=tatl
Stephen Barber3f7688f2017-02-16 11:23:44 -080028"
29FLAGS "$@" || exit 1
30eval set -- "${FLAGS_ARGV}"
31switch_to_strict_mode
32
Stephen Barberf9e93a32017-05-17 15:41:57 -070033get_version() {
34 local output_dir="$1"
35 awk -F'=' -v key='CHROMEOS_RELEASE_VERSION' '$1==key { print $2 }' \
36 "${output_dir}"/lsb-release
37}
Stephen Barber3f7688f2017-02-16 11:23:44 -080038
Stephen Barberf9e93a32017-05-17 15:41:57 -070039is_test_image() {
40 local output_dir="$1"
41 grep -q testimage-channel "${output_dir}"/lsb-release
42}
Stephen Barber3f7688f2017-02-16 11:23:44 -080043
Stephen Barberf9e93a32017-05-17 15:41:57 -070044read_le_int() {
45 local disk="$1"
46 local offset="$2"
47 local size="$3"
48
49 case "${size}" in
50 1 | 2 | 4 | 8)
51 ;;
52 *) die "${size} is not a valid int size to read"
53 ;;
Stephen Barber3f7688f2017-02-16 11:23:44 -080054 esac
55
Stephen Barberf9e93a32017-05-17 15:41:57 -070056 local raw="$(xxd -g ${size} -e -s ${offset} -l ${size} ${disk} | cut -d' ' -f2)"
57 local result="$(( 16#${raw} ))"
Stephen Barber3f7688f2017-02-16 11:23:44 -080058
Stephen Barberf9e93a32017-05-17 15:41:57 -070059 echo "${result}"
Stephen Barber3f7688f2017-02-16 11:23:44 -080060}
61
Stephen Barberf9e93a32017-05-17 15:41:57 -070062extract_squashfs_partition() {
63 local src_disk="$1"
64 local src_part="$2"
65 local dst_file="$3"
66
67 local part_start_blks="$(cgpt show -i "${src_part}" -b "${src_disk}")"
68 local part_start_bytes="$(( part_start_blks * 512 ))"
69 local part_size_blks="$(cgpt show -i "${src_part}" -s "${src_disk}")"
70 local part_size_bytes="$(( part_size_blks * 512 ))"
71
72 # To be sure we're extracting a squashfs partition, verify the magic.
73 # See fs/squashfs/squashfs_fs.h.
74 local magic="$(read_le_int ${src_disk} ${part_start_bytes} 4)"
75 if [[ "${magic}" -ne 0x73717368 ]]; then
76 die "Partition ${src_part} doesn't look like a squashfs partition"
77 fi
78
79 dd if="${src_disk}" of="${dst_file}" skip="${part_start_bytes}c" \
80 count="${part_size_bytes}c" iflag=skip_bytes,count_bytes
Stephen Barber3f7688f2017-02-16 11:23:44 -080081}
82
Stephen Barberdebef582018-05-19 13:11:54 -070083can_hardlink() {
84 local file1=$1
85 local file2=$2
86
87 # The file is considered hard-linkable if the access rights, user, and group
88 # are the same.
89 [[ "$(sudo stat -c '%a' ${prev_line})" = "$(sudo stat -c '%a' ${line})" ]] && \
90 [[ "$(sudo stat -c '%u' ${prev_line})" = "$(sudo stat -c '%u' ${line})" ]] && \
91 [[ "$(sudo stat -c '%g' ${prev_line})" = "$(sudo stat -c '%g' ${line})" ]]
92}
93
94do_hardlinks() {
95 local dir=$1
96
97 local line prev_line=""
98 while read line; do
99 # Treat the first file as the original.
100 if [[ -n "${prev_line}" && -n "${line}" ]]; then
101 if can_hardlink "${prev_line}" "${line}"; then
102 sudo ln -f "${prev_line}" "${line}"
103 fi
104 fi
105
106 prev_line="${line}"
107 done < <(sudo fdupes --recurse ${dir})
108}
109
Stephen Barberf9e93a32017-05-17 15:41:57 -0700110# Repack termina rootfs.
111repack_rootfs() {
112 local output_dir="$1"
113 local fs_type="$2"
Stephen Barberdebef582018-05-19 13:11:54 -0700114 local arch="$3"
Stephen Barberf9e93a32017-05-17 15:41:57 -0700115 local rootfs_img="${output_dir}/vm_rootfs.img"
116 local stateful_img="${output_dir}/vm_stateful.img"
117
118 # Create image in a temporary directory to avoid the need for extra space
119 # on the final rootfs.
120 local rootfs="${output_dir}/rootfs"
121 local stateful="${output_dir}/stateful"
122
123 sudo unsquashfs -d "${rootfs}" "${rootfs_img}"
124 sudo unsquashfs -d "${stateful}" "${stateful_img}"
125
126 # Remove source images.
127 sudo rm -f "${rootfs_img}" "${stateful_img}"
128
129 # Fix up rootfs.
130
131 # Remove efi cruft.
132 sudo rm -rf "${rootfs}/boot"/{efi,syslinux}
133 # Don't need firmware if you don't have hardware!
134 sudo rm -rf "${rootfs}/lib/firmware"
135 # Get rid of stateful, it's not needed on termina.
136 sudo rm -rf "${rootfs}/mnt/stateful_partition"
Stephen Barber8e3381b2018-01-30 13:04:44 -0800137 # Create container stateful and shared dirs.
138 sudo mkdir "${rootfs}"/mnt/{stateful,shared}
Stephen Barberf9e93a32017-05-17 15:41:57 -0700139 # Copy the dev_image into its location at /usr/local.
140 sudo cp -aT "${stateful}"/dev_image "${rootfs}/usr/local"
141
Stephen Barberdebef582018-05-19 13:11:54 -0700142 if [[ "${arch}" == "arm" ]]; then
143 cp "${rootfs}"/boot/Image-* "${output_dir}/vm_kernel"
144 else
145 "${CHROOT_TRUNK_DIR}"/src/third_party/kernel/v4.4/scripts/extract-vmlinux \
146 "${rootfs}"/boot/vmlinuz > "${output_dir}/vm_kernel"
147 fi
148
149 # Remove vmlinuz from the rootfs; it's not necessary.
Stephen Barberf9e93a32017-05-17 15:41:57 -0700150 sudo rm -rf "${rootfs}"/boot/vmlinuz*
151
Stephen Barberdebef582018-05-19 13:11:54 -0700152 do_hardlinks "${rootfs}"
153
Stephen Barberf9e93a32017-05-17 15:41:57 -0700154 sudo cp "${rootfs}/etc/lsb-release" "${output_dir}/lsb-release"
Stephen Barber8e3381b2018-01-30 13:04:44 -0800155 sudo cp "${rootfs}/opt/google/chrome/resources/about_os_credits.html" \
156 "${output_dir}/about_os_credits.html"
Stephen Barberf9e93a32017-05-17 15:41:57 -0700157
158 case "${fs_type}" in
159 squashfs)
160 sudo mksquashfs "${rootfs}" "${rootfs_img}" -comp lzo
161 ;;
162 ext4)
Chirantan Ekbote694afec2018-04-23 16:53:19 -0700163 # Start with 400MB, then shrink.
164 local image_size=400
Stephen Barberf9e93a32017-05-17 15:41:57 -0700165 truncate --size "${image_size}M" "${rootfs_img}"
166 /sbin/mkfs.ext4 -F -m 0 -i 16384 -b 1024 -O "^has_journal" "${rootfs_img}"
167 local rootfs_mnt="$(mktemp -d)"
168 fs_mount "${rootfs_img}" "${rootfs_mnt}" ext4 rw
169 sudo cp -aT "${rootfs}" "${rootfs_mnt}"
170 fs_umount "${rootfs_img}" "${rootfs_mnt}" ext4 rw
171 # Shrink to minimum size.
172 /sbin/e2fsck -f "${rootfs_img}"
173 /sbin/resize2fs -M "${rootfs_img}"
174 rmdir "${rootfs_mnt}"
175 ;;
176 *)
177 die_notrace "Unsupported fs type ${fs_type}."
178 ;;
179 esac
180
181 sudo rm -rf "${rootfs}" "${stateful}"
182}
183
Stephen Barber3f7688f2017-02-16 11:23:44 -0800184main() {
Stephen Barberf9e93a32017-05-17 15:41:57 -0700185 if [[ -z "${FLAGS_image}" ]]; then
186 die_notrace "Please provide an image using --image"
187 elif [[ ! -f "${FLAGS_image}" ]]; then
188 die_notrace "'${FLAGS_image}' does not exist"
Stephen Barber3f7688f2017-02-16 11:23:44 -0800189 fi
190
Stephen Barberdebef582018-05-19 13:11:54 -0700191 if [[ "${FLAGS_arch}" != "amd64" && "${FLAGS_arch}" != "arm" ]]; then
192 die_notrace "Architecture '${FLAGS_arch}' is not valid. Options are 'amd64' and 'arm'"
Stephen Barber3f7688f2017-02-16 11:23:44 -0800193 fi
194
Stephen Barberf9e93a32017-05-17 15:41:57 -0700195 case "${FLAGS_filesystem}" in
196 squashfs|ext4)
197 ;;
198 *)
199 die_notrace "Filesystem '${FLAGS_filesystem}' is not valid. Options are 'squashfs' and 'ext4'"
200 ;;
201 esac
202
203 if [[ -z "${FLAGS_output}" ]]; then
204 die_notrace "Output directory was not specified"
205 elif [[ -e "${FLAGS_output}" ]]; then
206 die_notrace "${FLAGS_output} already exists"
207 fi
208
209 local output_dir="${FLAGS_output}"
210 local stateful_img="${output_dir}/vm_stateful.img"
211 local rootfs_img="${output_dir}/vm_rootfs.img"
212 local image="${FLAGS_image}"
213
214 mkdir -p "${output_dir}"
215 extract_squashfs_partition "${image}" "3" "${rootfs_img}"
216 extract_squashfs_partition "${image}" "1" "${stateful_img}"
217
Stephen Barberdebef582018-05-19 13:11:54 -0700218 repack_rootfs "${output_dir}" "${FLAGS_filesystem}" "${FLAGS_arch}"
Stephen Barberf9e93a32017-05-17 15:41:57 -0700219
220 info "Done! The resulting image is in '${output_dir}'"
Stephen Barber3f7688f2017-02-16 11:23:44 -0800221}
222
223main "$@"