# vi: ts=4 expandtab syntax=sh CLOUD_IMG_STR="# CLOUD_IMG: This file was created/modified by the Cloud Image build process" IMAGE_SIZE=$((2252*1024**2)) # 2.2G (the current size we ship) rootfs_dev_mapper= loop_device= loop_raw= backing_img= apt-get -qqy install dosfstools gdisk clean_loops() { if [ -z "${rootfs_dev_mapper}" ]; then return 0 fi if [ -n "${backing_img}" ]; then kpartx -v -d "${backing_img}" fi unset backing_img unset loop_device unset loop_raw unset rootfs_dev_mapper } create_empty_disk_image() { # Prepare an empty disk image dd if=/dev/zero of="$1" bs=1 count=0 seek="${IMAGE_SIZE}" } make_ext4_partition() { device="$1" mkfs.ext4 -F -b 4096 -i 8192 -m 0 -L cloudimg-rootfs -E resize=536870912 "$device" } mount_image() { apt-get install -qqy kpartx trap clean_loops EXIT backing_img="$1" loop_raw="$(kpartx -s -v -a "$1" )" loop_device="$(echo -e "${loop_raw}" | head -n1 | awk '{print($(NF-1))}')" rootfs_dev_mapper="/dev/mapper${loop_device///dev/}p1" [ ! -b "${rootfs_dev_mapper}" ] && echo "${rootfs_dev_mapper} is not a block device" && exit 1 return 0 } mount_partition() { partition="$1" mountpoint="$2" mount "$partition" "$mountpoint" mount --bind /dev "$mountpoint/dev" mount devpts-live -t proc "$mountpoint/dev/pts" mount proc-live -t proc "$mountpoint/proc" mount sysfs-live -t sysfs "$mountpoint/sys" mount -t tmpfs none "$mountpoint/tmp" mv "$mountpoint/etc/resolv.conf" resolv.conf.tmp cp /etc/resolv.conf "$mountpoint/etc/resolv.conf" } mount_disk_image() { local disk_image=${1} local mountpoint=${2} mount_image ${disk_image} mount_partition "${rootfs_dev_mapper}" $mountpoint local uefi_dev="/dev/mapper${loop_device///dev/}p15" if [ -b ${uefi_dev} -a -e $mountpoint/boot/efi ]; then mount "${uefi_dev}" $mountpoint/boot/efi fi # This is needed to allow for certain operations # such as updating grub and installing software cat > $mountpoint/usr/sbin/policy-rc.d << EOF #!/bin/sh # ${CLOUD_IMG_STR} echo "All runlevel operations denied by policy" >&2 exit 101 EOF chmod 0755 $mountpoint/usr/sbin/policy-rc.d } umount_settle() { # Unmount device, and let it settle umount $1 udevadm settle sleep 3 } umount_partition() { local mountpoint=${1} mv resolv.conf.tmp "$mountpoint/etc/resolv.conf" for submnt in proc sys dev/pts dev tmp; do umount_settle $mountpoint/$submnt done umount_settle $mountpoint if [ -n "${rootfs_dev_mapper}" -a -b "${rootfs_dev_mapper}" ]; then # buildd's don't have /etc/mtab symlinked # /etc/mtab is needed in order zerofree space for ext4 filesystems [ -e /etc/mtab ] || ln -s /proc/mounts /etc/mtab # both of these are likely overkill, but it does result in slightly # smaller ext4 filesystem apt-get -qqy install zerofree e2fsck -y -E discard ${rootfs_dev_mapper} zerofree ${rootfs_dev_mapper} fi } umount_disk_image() { mountpoint="$1" local uefi_dev="/dev/mapper${loop_device///dev/}p15" if [ -e "$mountpoint/boot/efi" -a -b "$uefi_dev" ]; then umount --detach-loop "$mountpoint/boot/efi" fi if [ -e $mountpoint/usr/sbin/policy-rc.d ]; then rm $mountpoint/usr/sbin/policy-rc.d fi umount_partition $mountpoint clean_loops } modify_vmdk_header() { # Modify the VMDK headers so that both VirtualBox _and_ VMware can # read the vmdk and import them. The vodoo here is _not_ documented # anywhere....so this will have to do. This is undocumented vodoo # that has been learned by the Cloud Image team. vmdk_name="${1}" descriptor=$(mktemp) newdescriptor=$(mktemp) # Extract the vmdk header for manipulation dd if="${vmdk_name}" of="${descriptor}" bs=1 skip=512 count=1024 # The sed lines below is where the magic is. Specifically: # ddb.toolsVersion: sets the open-vm-tools so that VMware shows # the tooling as current # ddb.virtualHWVersion: set the version to 7, which covers most # current versions of VMware # createType: make sure its set to stream Optimized # remove the vmdk-stream-converter comment and replace with # # Disk DescriptorFile. This is needed for Virtualbox # remove the comments from vmdk-stream-converter which causes # VirtualBox and others to fail VMDK validation sed -e 's|# Description file.*|# Disk DescriptorFile|' \ -e '/# Believe this is random*/d' \ -e '/# Indicates no parent/d' \ -e '/# The Disk Data Base/d' \ -e 's|ddb.comment.*|ddb.toolsVersion = "2147483647"|' \ "${descriptor}" > "${newdescriptor}" # The header is cannot be bigger than 1024 expr $(stat --format=%s ${newdescriptor}) \< 1024 > /dev/null 2>&1 || { echo "descriptor is too large, VMDK will be invalid!"; exit 1; } # Overwrite the vmdk header with our new, modified one dd conv=notrunc,nocreat \ if="${newdescriptor}" of="${vmdk_name}" \ bs=1 seek=512 count=1024 rm ${descriptor} ${newdescriptor} } create_vmdk() { # There is no real good way to create a _compressed_ VMDK using open source # tooling that works across multiple VMDK-capable platforms. This functions # uses vmdk-stream-converter and then calls modify_vmdk_header to produce a # compatible VMDK. src="$1" destination="$2" size="${3:-10240}" apt-get install -qqy qemu-utils vmdk-stream-converter streamconverter="/usr/share/pyshared/VMDKstream.py" scratch_d=$(mktemp -d) cp ${src} ${scratch_d}/resize.img truncate --size=${size}M ${scratch_d}/resize.img python ${streamconverter} ${scratch_d}/resize.img ${destination} modify_vmdk_header ${destination} qemu-img info ${destination} rm -rf ${scratch_d} } create_derivative() { # arg1 is the disk type # arg2 is the new name unset derivative_img case ${1} in uefi) disk_image="binary/boot/disk-uefi.ext4"; dname="${disk_image//-uefi/-$2-uefi}";; *) disk_image="binary/boot/disk.ext4"; dname="${disk_image//.ext4/-$2.ext4}";; esac if [ ! -e ${disk_image} ]; then echo "Did not find ${disk_image}!"; exit 1; fi cp ${disk_image} ${dname} export derivative_img=${dname} } convert_to_qcow2() { apt-get install -qqy qemu-utils src="$1" destination="$2" qemu-img convert -c -O qcow2 -o compat=0.10 "$src" "$destination" qemu-img info "$destination" }