1#!/bin/bash 2 3# 4# Copyright (C) 2015 The Android Open Source Project 5# 6# Licensed under the Apache License, Version 2.0 (the "License"); 7# you may not use this file except in compliance with the License. 8# You may obtain a copy of the License at 9# 10# http://www.apache.org/licenses/LICENSE-2.0 11# 12# Unless required by applicable law or agreed to in writing, software 13# distributed under the License is distributed on an "AS IS" BASIS, 14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15# See the License for the specific language governing permissions and 16# limitations under the License. 17# 18 19# This script generates some sample images used in unittests and packages them 20# in the sample_images.tar.bz2 file. The list of generated images and their 21# options are described in the main() function. You need to manually run this 22# script to update the generated images whenever you modify this script. 23 24set -e 25 26# cleanup <path> 27# Unmount and remove the mountpoint <path> 28cleanup() { 29 local path="$1" 30 if ! sudo umount "${path}" 2>/dev/null; then 31 if mountpoint -q "${path}"; then 32 sync && sudo umount "${path}" 33 fi 34 fi 35 if [ -n "${path}" ]; then 36 sudo rm -rf "${path}" 37 fi 38} 39 40# add_files_default <mntdir> <block_size> 41# Add several test files to the image mounted in <mntdir>. 42add_files_default() { 43 local mntdir="$1" 44 local block_size="$2" 45 46 ### Generate the files used in unittest with descriptive names. 47 sudo touch "${mntdir}"/empty-file 48 49 # regular: Regular files. 50 echo "small file" | sudo dd of="${mntdir}"/regular-small status=none 51 dd if=/dev/zero bs=1024 count=16 status=none | tr '\0' '\141' | 52 sudo dd of="${mntdir}"/regular-16k status=none 53 sudo dd if=/dev/zero of="${mntdir}"/regular-32k-zeros bs=1024 count=16 \ 54 status=none 55 56 echo "with net_cap" | sudo dd of="${mntdir}"/regular-with_net_cap status=none 57 sudo setcap cap_net_raw=ep "${mntdir}"/regular-with_net_cap 58 59 # sparse_empty: Files with no data blocks at all (only sparse holes). 60 sudo truncate --size=10240 "${mntdir}"/sparse_empty-10k 61 sudo truncate --size=$(( block_size * 2 )) "${mntdir}"/sparse_empty-2blocks 62 63 # sparse: Files with some data blocks but also sparse holes. 64 echo -n "foo" | 65 sudo dd of="${mntdir}"/sparse-16k-last_block bs=1 \ 66 seek=$(( 16 * 1024 - 3)) status=none 67 68 # ext2 inodes have 12 direct blocks, one indirect, one double indirect and 69 # one triple indirect. 10000 should be enough to have an indirect and double 70 # indirect block. 71 echo -n "foo" | 72 sudo dd of="${mntdir}"/sparse-10000blocks bs=1 \ 73 seek=$(( block_size * 10000 )) status=none 74 75 sudo truncate --size=16384 "${mntdir}"/sparse-16k-first_block 76 echo "first block" | sudo dd of="${mntdir}"/sparse-16k-first_block status=none 77 78 sudo truncate --size=16384 "${mntdir}"/sparse-16k-holes 79 echo "a" | sudo dd of="${mntdir}"/sparse-16k-holes bs=1 seek=100 status=none 80 echo "b" | sudo dd of="${mntdir}"/sparse-16k-holes bs=1 seek=10000 status=none 81 82 # link: symlinks and hardlinks. 83 sudo ln -s "broken-link" "${mntdir}"/link-short_symlink 84 sudo ln -s $(dd if=/dev/zero bs=256 count=1 status=none | tr '\0' '\141') \ 85 "${mntdir}"/link-long_symlink 86 sudo ln "${mntdir}"/regular-16k "${mntdir}"/link-hard-regular-16k 87 88 # Directories. 89 sudo mkdir -p "${mntdir}"/dir1/dir2/dir1 90 echo "foo" | sudo tee "${mntdir}"/dir1/dir2/file >/dev/null 91 echo "bar" | sudo tee "${mntdir}"/dir1/file >/dev/null 92 93 # FIFO 94 sudo mkfifo "${mntdir}"/fifo 95 96 # character special file 97 sudo mknod "${mntdir}"/cdev c 2 3 98 99 # removed: removed files that should not be listed. 100 echo "We will remove this file so it's contents will be somewhere in the " \ 101 "empty space data but it won't be all zeros." | 102 sudo dd of="${mntdir}"/removed conv=fsync status=none 103 sudo rm "${mntdir}"/removed 104} 105 106# add_files_ue_settings <mntdir> <block_size> 107# Add the update_engine.conf settings file. This file contains the 108add_files_ue_settings() { 109 local mntdir="$1" 110 111 sudo mkdir -p "${mntdir}"/etc >/dev/null 112 sudo tee "${mntdir}"/etc/update_engine.conf >/dev/null <<EOF 113PAYLOAD_MINOR_VERSION=1234 114EOF 115 # Example of a real lsb-release file released on link stable. 116 sudo tee "${mntdir}"/etc/lsb-release >/dev/null <<EOF 117CHROMEOS_AUSERVER=https://tools.google.com/service/update2 118CHROMEOS_BOARD_APPID={F26D159B-52A3-491A-AE25-B23670A66B32} 119CHROMEOS_CANARY_APPID={90F229CE-83E2-4FAF-8479-E368A34938B1} 120CHROMEOS_DEVSERVER= 121CHROMEOS_RELEASE_APPID={F26D159B-52A3-491A-AE25-B23670A66B32} 122CHROMEOS_RELEASE_BOARD=link-signed-mp-v4keys 123CHROMEOS_RELEASE_BRANCH_NUMBER=63 124CHROMEOS_RELEASE_BUILD_NUMBER=6946 125CHROMEOS_RELEASE_BUILD_TYPE=Official Build 126CHROMEOS_RELEASE_CHROME_MILESTONE=43 127CHROMEOS_RELEASE_DESCRIPTION=6946.63.0 (Official Build) stable-channel link 128CHROMEOS_RELEASE_NAME=Chrome OS 129CHROMEOS_RELEASE_PATCH_NUMBER=0 130CHROMEOS_RELEASE_TRACK=stable-channel 131CHROMEOS_RELEASE_VERSION=6946.63.0 132GOOGLE_RELEASE=6946.63.0 133EOF 134} 135 136add_files_postinstall() { 137 local mntdir="$1" 138 139 sudo mkdir -p "${mntdir}"/bin >/dev/null 140 141 # A postinstall bash program. 142 sudo tee "${mntdir}"/bin/postinst_example >/dev/null <<EOF 143#!/etc/../bin/sh 144echo "I'm a postinstall program and I know how to write to stdout" 145echo "My call was $@" 146exit 0 147EOF 148 149 # A symlink to another program. This should also work. 150 sudo ln -s "postinst_example" "${mntdir}"/bin/postinst_link 151 152 sudo tee "${mntdir}"/bin/postinst_fail3 >/dev/null <<EOF 153#!/etc/../bin/sh 154exit 3 155EOF 156 157 sudo tee "${mntdir}"/bin/postinst_fail1 >/dev/null <<EOF 158#!/etc/../bin/sh 159exit 1 160EOF 161 162 # A program that succeeds if it is suspended during the first 5 minutes. 163 sudo tee "${mntdir}"/bin/postinst_suspend >/dev/null <<EOF 164#!/etc/../bin/sh 165trap "{ echo Got a SIGCONT; exit 0; }" CONT 166# Signal that we are ready to receive the signal by redirecting our stdin to 167# /dev/zero, the test can detect that. 168exec </dev/zero 169# Allow the signal handler to run every 100 ms. 170i=3000 171while [ \$i -ge 0 ]; do 172 sleep 0.1 173 i=\$((i-1)) 174done 175exit 1 176EOF 177 178 # A program that reports back progress. 179 sudo tee "${mntdir}"/bin/postinst_progress >/dev/null <<EOF 180#!/etc/../bin/sh 181# These values have exact representation in IEEE 754 so we avoid rounding 182# errors. 183echo global_progress 0.25 >&3 184echo global_progress 0.5 >&3 185echo global_progress 1.0 >&3 186exit 0 187EOF 188 189 # An unlabeled postinstall bash program. 190 sudo tee "${mntdir}"/bin/self_check_default_context >/dev/null <<EOF 191#!/etc/../bin/sh 192echo "This is my context:" 193ls -lZ "\$0" 194ls -lZ "\$0" | grep -F ' u:object_r:postinstall_file:s0 ' || exit 5 195exit 0 196EOF 197 198 # A postinstall bash program. 199 sudo tee "${mntdir}"/bin/self_check_context >/dev/null <<EOF 200#!/etc/../bin/sh 201echo "This is my context:" 202ls -lZ "\$0" 203ls -lZ "\$0" | grep -F ' u:object_r:postinstall_exec:s0 ' || exit 5 204exit 0 205EOF 206 207 # Give the test function the context we expect the postinstall-executable to have. 208 sudo setfattr -n security.selinux -v u:object_r:postinstall_exec:s0 "${mntdir}"/bin/self_check_context 209 210 sudo tee "${mntdir}"/postinst >/dev/null <<EOF 211#!/etc/../bin/sh 212echo "postinst" 213exit 0 214EOF 215 216 sudo chmod +x "${mntdir}"/postinst "${mntdir}"/bin/* 217} 218 219# generate_fs <filename> <kind> <size> [block_size] [block_groups] 220generate_fs() { 221 local filename="$1" 222 local type="$2" 223 local kind="$3" 224 local size="$4" 225 local block_size="${5:-4096}" 226 local block_groups="${6:-}" 227 228 local mkfs_opts=( -q -F -b "${block_size}" -L "ROOT-TEST" -t ext2 ) 229 if [[ -n "${block_groups}" ]]; then 230 mkfs_opts+=( -G "${block_groups}" ) 231 fi 232 233 local mntdir=$(mktemp --tmpdir -d generate_ext2.XXXXXX) 234 trap 'cleanup "${mntdir}"; rm -f "${filename}"' INT TERM EXIT 235 # Cleanup old image. 236 if [[ -e "${filename}" ]]; then 237 rm -f "${filename}" 238 fi 239 240 if [[ "${type}" == "ext2" ]]; then 241 truncate --size="${size}" "${filename}" 242 243 mkfs.ext2 "${mkfs_opts[@]}" "${filename}" 244 sudo mount "${filename}" "${mntdir}" -o loop 245 fi 246 case "${kind}" in 247 unittest) 248 add_files_ue_settings "${mntdir}" "${block_size}" 249 add_files_postinstall "${mntdir}" "${block_size}" 250 ;; 251 default) 252 add_files_default "${mntdir}" "${block_size}" 253 ;; 254 empty) 255 ;; 256 esac 257 258 if [[ "${type}" == "sqfs" ]]; then 259 mksquashfs "${mntdir}" "${filename}" 260 fi 261 262 cleanup "${mntdir}" 263 trap - INT TERM EXIT 264} 265 266OUTPUT_DIR=$(dirname "$0") 267IMAGES=() 268 269# generate_image <image_name> [<image args> ...] 270generate_image() { 271 echo "Generating image $1.img" 272 IMAGES+=( "$1.img" ) 273 generate_fs "${OUTPUT_DIR}/$1.img" "${@:2}" 274} 275 276main() { 277 # Add more sample images here. 278 generate_image disk_ext2_1k ext2 default $((1024 * 1024)) 1024 279 generate_image disk_ext2_4k ext2 default $((1024 * 4096)) 4096 280 generate_image disk_ext2_4k_empty ext2 empty $((1024 * 4096)) 4096 281 generate_image disk_ext2_unittest ext2 unittest $((1024 * 4096)) 4096 282 283 # Add squashfs sample images. 284 generate_image disk_sqfs_empty sqfs empty $((1024 * 4096)) 4096 285 generate_image disk_sqfs_default sqfs default $((1024 * 4096)) 4096 286 generate_image disk_sqfs_unittest sqfs unittest $((1024 * 4096)) 4096 287 288 # Generate the tarball and delete temporary images. 289 echo "Packing tar file sample_images.tar.bz2" 290 tar -jcf "${OUTPUT_DIR}/sample_images.tar.bz2" -C "${OUTPUT_DIR}" \ 291 --sparse "${IMAGES[@]}" 292 cd "${OUTPUT_DIR}" 293 rm "${IMAGES[@]}" 294} 295 296main 297