diff -Nru cloud-utils-0.29/debian/changelog cloud-utils-0.29/debian/changelog --- cloud-utils-0.29/debian/changelog 2016-10-04 16:36:21.000000000 +0000 +++ cloud-utils-0.29/debian/changelog 2016-10-25 14:50:41.000000000 +0000 @@ -1,3 +1,18 @@ +cloud-utils (0.29-0ubuntu7) zesty; urgency=medium + + * debian/tests: fix tests to make program in tests/ executable + if it is not already. + + -- Scott Moser Tue, 25 Oct 2016 10:50:41 -0400 + +cloud-utils (0.29-0ubuntu6) zesty; urgency=medium + + * sync to trunk at revno 313 + * mount-image-callback: support 'chroot' to lxd containers + * debian/tests/control: run mount-image-callback test (test-mic) + + -- Scott Moser Mon, 24 Oct 2016 14:30:49 -0400 + cloud-utils (0.29-0ubuntu5) yakkety; urgency=medium * debian/control: update to Standards-Version 3.9.8 diff -Nru cloud-utils-0.29/debian/patches/sync-to-trunk.patch cloud-utils-0.29/debian/patches/sync-to-trunk.patch --- cloud-utils-0.29/debian/patches/sync-to-trunk.patch 2016-10-04 16:33:35.000000000 +0000 +++ cloud-utils-0.29/debian/patches/sync-to-trunk.patch 2016-10-24 18:29:26.000000000 +0000 @@ -1,5 +1,96 @@ Patch created with './debian/update-sync-to-main ../trunk' ------------------------------------------------------------ +revno: 313 +committer: Scott Moser +branch nick: trunk +timestamp: Mon 2016-10-24 11:42:55 -0400 +message: + mount-image-callback: update usage to mention 'mchroot' +------------------------------------------------------------ +revno: 312 [merge] +committer: Scott Moser +branch nick: trunk +timestamp: Mon 2016-10-24 11:31:49 -0400 +message: + mount-image-callback: support 'chroot' to lxd containers + + This adds support for mount-image-callback to do its magic on a + local lxd container. + + $ lxc init ubuntu-daily:xenial mic-test + $ sudo mount-image-callback mic-test -- apt-get update -q + $ sudo ./bin/mount-image-callback lxd:mic-test --system-resolvconf -- \ + mchroot apt-get update -q + + In doing so, it adds some helpers to the PATH before executing the + command provided: + uns-switch: switch to the appropriate lxc namespace + mchroot: chroot into the _MOUNTPOINT_ (automatically calls uns-switch) +------------------------------------------------------------ +revno: 311 +committer: Scott Moser +branch nick: trunk +timestamp: Wed 2016-10-05 10:35:51 -0400 +message: + test/test-mic: tests for unmounting unexpected mounts and system-mounts. + + these 2 tests verify that + - "unexpected" mounts under the mount point get unmounted. + mounts not made by mount-image-callback should still get unmounted. + - tests that --system-mounts works as expected. +------------------------------------------------------------ +revno: 310 +committer: Scott Moser +branch nick: trunk +timestamp: Wed 2016-10-05 09:59:29 -0400 +message: + bin/mount-image-callback: do unmounts in reverse order. + + This still uses umount_r which unmounts under the mount point + recursively. The reason for doing it in reverse order is so + that umount_r can be expected to have done something. + + Rather than + mount -o loop img my-dir + mount --bind /dev my-dir/dev + mount --bind /sys my-dir/sys + Then + umount_r my-dir + We do: + umount_r my-dir/sys + umount_r my-dir/dev + umount_r my-dir +------------------------------------------------------------ +revno: 309 +committer: Scott Moser +branch nick: trunk +timestamp: Wed 2016-10-05 09:59:13 -0400 +message: + remove unused variable +------------------------------------------------------------ +revno: 308 +committer: Scott Moser +branch nick: trunk +timestamp: Wed 2016-10-05 09:42:14 -0400 +message: + test: improve test-mic to create both GPT and MBR disks +------------------------------------------------------------ +revno: 307 [merge] +committer: Scott Moser +branch nick: trunk +timestamp: Wed 2016-10-05 09:15:20 -0400 +message: + separate nbd out of main. + + This is really all just cosmetic, moving the big hunk of main + that handled mounting a nbd device via qemu-nbd into a set of functions. + + Also: + - use 'mount -o loop,ro' on unpartitioned image mounts when + the user passed --read-only. + - only attempt mount -o loop if --part=0 or --part=auto + - remove 'mp_is_tmp'. Instead just clean up tempdir. +------------------------------------------------------------ revno: 306 committer: Scott Moser branch nick: trunk @@ -130,8 +221,8 @@ Use --include-merged or -n0 to see merged revisions. === modified file 'ChangeLog' --- old/ChangeLog 2016-06-01 16:40:45 +0000 -+++ new/ChangeLog 2016-09-28 01:17:47 +0000 -@@ -1,3 +1,14 @@ ++++ new/ChangeLog 2016-10-24 15:31:49 +0000 +@@ -1,3 +1,16 @@ +0.30 + - ubuntu-cloudimg-query: fix xenial default to be ebs-ssd + as traditional ebs are no longer available. (LP: #1593451) @@ -142,6 +233,8 @@ + (LP: #1619285) + - mount-image-callback: support 'overlay' filesystem type rather + than ubuntu specific 'overlayfs' (LP: #1493188) ++ - mount-image-callback: support 'chroot' to lxd containers and ++ correctly apply user namespace change. + 0.29 - cloud-localds: fix creation of vfat filesystems (LP: #1431537) @@ -196,7 +289,7 @@ === modified file 'bin/mount-image-callback' --- old/bin/mount-image-callback 2016-01-27 17:53:13 +0000 -+++ new/bin/mount-image-callback 2016-10-04 15:11:26 +0000 ++++ new/bin/mount-image-callback 2016-10-24 15:42:55 +0000 @@ -4,6 +4,8 @@ TEMP_D="" UMOUNTS=( ) @@ -206,10 +299,105 @@ error() { echo "$@" 1>&2; } -@@ -138,13 +140,46 @@ +@@ -14,6 +16,10 @@ + mount a file to a temporary mount point and then + invoke the provided cmd with args + ++ supported 'file' are: ++ lxd:name : the rootfs for lxd container 'name' ++ file : any disk format supported by qemu-nbd ++ + the temporary mountpoint will be put in an a environment variable + named MOUNTPOINT. + +@@ -21,6 +27,10 @@ + they will be replaced with the mount point. Example: + ${0##*/} my.img chroot _MOUNTPOINT_ /bin/sh + ++ Additionally, the helper program 'mchroot' will be added to the path ++ and can be used effectively as 'chroot _MOUNTPOINT_': ++ ${0##*/} my.img mchroot ++ + options: + -v | --verbose increase verbosity + --read-only use read-only mount. +@@ -65,6 +75,7 @@ + disconnect_qemu() { + [ -n "$QEMU_DISCONNECT" ] || return 0 + local out="" nbd="$QEMU_DISCONNECT" ++ debug 1 "disconnecting $nbd" + local pid="" pfile="/sys/block/${nbd#/dev/}/pid" + { read pid < "$pfile" ; } >/dev/null 2>&1 + [ -n "$pid" -a ! -d "/proc/$pid" ] && +@@ -78,8 +89,10 @@ + } + + do_umounts() { +- local um="" fails=0 mydir="$PWD/" +- for um in "$@"; do ++ local um="" fails=0 mydir="$PWD/" mounts="" i=0 ++ mounts=( "$@" ) ++ for((i=${#mounts[@]}-1;i>=0;i--)); do ++ um=${mounts[$i]} + um=$(readlink -f "$um") || { + error "WARNING: failed to get full path to '$um'"; + fails=$(($fails+1)) +@@ -123,9 +136,8 @@ + + get_partition() { + # return in _RET the 'auto' partition for a image. +- # return partition number for a partitioned image +- # return 0 for unpartitioned +- # return 0 if image is partitioned, 1 if not ++ # _RET=partition number for a partitioned image ++ # _RET=0 for unpartitioned + local img="$1" + out=$(LANG=C sfdisk --list -uS "$img" 2>&1) || { + error "failed determining if partitioned: $out"; +@@ -138,13 +150,216 @@ fi } ++add_bin() { ++ cat > "$1" || { error "failed to write to $1"; return 1; } ++ chmod 755 "$1" || { error "failed to set perms on $1"; return 1; } ++} ++ ++add_helpers() { ++ local d="$1" ++ local umap="$1" gmap="$2" ++ [ -d "$1" ] || mkdir -p "$1" ++ add_bin "$d/uns-switch" <<"EOF" || return 1 ++#!/bin/sh ++[ $# -eq 0 ] && set -- ${SHELL:-/bin/bash} ++if [ -z "$MIC_SUBUID" -a -z "$MIC_SUBGID" ]; then ++ exec "$@" ++fi ++exec lxc-usernsexec ${MIC_SUBUID:+-m u:0:${MIC_SUBUID}} \ ++ ${MIC_SUBGID:+-m g:0:${MIC_SUBGID}} -- "$@" ++EOF ++ add_bin "$d/mchroot" <<"EOF" || return 1 ++#!/bin/sh ++exec uns-switch chroot "$MOUNTPOINT" "$@" ++EOF ++ return ++} ++ ++get_subid() { ++ local id="$1" file="$2" ++ awk -F: '$1 == id { printf("%s:%s\n", $2, $3); exit(0); }' \ ++ id="$id" "$file" ++} ++ ++set_subids() { ++ local id="$1" out="" muid="" mgid="" ++ [ -z "$id" ] && return 0 ++ muid=$(get_subid "$id" /etc/subuid) && ++ mgid=$(get_subid "$id" /etc/subgid) && ++ [ -n "$muid" -a -n "$mgid" ] && ++ export MIC_SUBGID="$muid" MIC_SUBUID="$mgid" && return ++} ++ +mount_overlay() { + local lower="$1" upper="$2" workdir="$3" + local olayopts="lowerdir=$lower,upperdir=$upper" @@ -243,19 +431,151 @@ + return $ret +} + ++assert_nbd_support() { ++ if [ ! -e /sys/block/nbd0 ] && ! grep -q nbd /proc/modules; then ++ debug 1 "trying to load nbd module" ++ modprobe nbd >/dev/null 2>&1 ++ udevadm settle >/dev/null 2>&1 ++ fi ++ [ -e /sys/block/nbd0 ] || { ++ error "Cannot use nbd: no nbd kernel support." ++ return 1; ++ } ++} ++ ++find_unused_nbd() { ++ # return a path to an unused nbd device (/dev/nbd?) ++ local f roflag="" ++ for f in /sys/block/nbd*; do ++ [ -d "$f" -a ! -f "$f/pid" ] && ++ { _RET="/dev/${f##*/}"; return 0; } ++ done ++ error "failed to find an nbd device" ++ return 1; ++} ++ ++connect_nbd() { ++ local img="$1" fmt="$2" ptnum="${3:-auto}" rwmode="${3:-ro}" ++ local nbd="" pidfile="" pid="" cmd="" ret="" roflag="" nptnum="" i="" ++ if [ "$rwmode" = "ro" ]; then ++ roflag="--read-only" ++ fi ++ # yes, there is a race condition here. ++ find_unused_nbd || return ++ nbd="$_RET" ++ cmd=( qemu-nbd $roflag "--format=$fmt" --connect "$nbd" "$img" ) ++ "${cmd[@]}" && QEMU_DISCONNECT="$nbd" ++ ret=$? ++ if [ $ret -ne 0 ]; then ++ error "Failed [$ret]: $*" ++ return $ret ++ fi ++ pidfile="/sys/block/${nbd#/dev/}/pid" ++ if [ ! -f "$pidfile" ]; then ++ debug 1 "waiting on pidfile for $nbd in $pidfile" ++ i=0 ++ while [ ! -f "$pidfile" ] && i=$(($i+1)); do ++ if [ $i -eq 200 ]; then ++ error "giving up on pidfile $pidfile for $nbd" ++ disconnect_qemu ++ return 1 ++ fi ++ sleep .1 ++ debug 2 "." ++ done ++ fi ++ read pid < "$pidfile" && debug 2 "pid for $nbd is $pidfile" || { ++ error "reading pid from $pidfile for $nbd failed!"; ++ disconnect_qemu ++ return 1 ++ } ++ ++ debug 1 "connected $img_in ($fmt) ${rwmode} to $nbd. waiting for device." ++ i=0 ++ while i=$(($i+1)):; do ++ get_partition "$nbd" && nptnum="$_RET" && break ++ [ $i -eq 40 ] && { ++ error "gave up on $nbd" ++ disconnect_qemu ++ return 1 ++ } ++ [ $(($i%10)) -eq 0 ] && ++ debug 1 "waiting for $nbd to be ready." ++ sleep .1 ++ done ++ ++ if [ "${ptnum}" = "auto" ]; then ++ if [ "$nptnum" = "0" ]; then ++ debug 1 "unpartitioned disk." ++ else ++ debug 1 "partitioned disk." ++ fi ++ ptnum=$nptnum ++ else ++ if [ "$nptnum" = "0" -a "$ptnum" != "0" ]; then ++ error "img $img does not appear partitioned but ptnum=$ptnum provided." ++ return 1 ++ fi ++ fi ++ if [ "$ptnum" -ne 0 ]; then ++ mdev="${nbd}p${ptnum}" ++ else ++ mdev="${nbd}" ++ fi ++ ++ i=0 ++ while :; do ++ [ -b "$mdev" ] && break ++ i=$(($i+1)) ++ [ $i -eq 100 ] && { ++ error "gave up on waiting for $mdev" ++ disconnect_qemu ++ return 1 ++ } ++ [ $(($i%10)) -eq 0 ] && ++ debug 1 "waiting for $mdev part=$ptnum to be ready." ++ sleep .1 ++ done ++ ++ _RET_NBD="$nbd" ++ _RET_PT="$ptnum" ++ _RET_DEV="$mdev" ++} ++ ++mount_nbd() { ++ local img="$1" mp="$2" fmt="$3" ptnum="$4" rwmode="${5:-rw}" opts="$6" ++ if [ -z "$fmt" ]; then ++ get_image_format "$img" && fmt="$_RET" || { ++ error "failed to get image format for '$img' (try --format)" ++ return 1 ++ } ++ fi ++ assert_nbd_support || return ++ connect_nbd "$img" "$fmt" "$ptnum" "$rwmode" || return ++ local ptnum="$_RET_PT" mdev="$_RET_DEV" nbd="$_RET_NBD" ++ if ( set -f; mount -o "$romode" $opts "$mdev" "$img_mp" ); then ++ debug 1 "mounted $mdev via qemu-nbd $nbd" ++ else ++ error "failed to mount $mdev" ++ return 1 ++ fi ++} ++ mount_callback_umount() { - local img_in="$1" dev="" out="" mp="" ret="" img="" readonly="" +- local img_in="$1" dev="" out="" mp="" ret="" img="" readonly="" ++ local img_in="$1" dev="" out="" mp="" ret="" img="" readonly=false local opts="" bmounts="" system_resolvconf=false ptnum=auto - local cd_mountpoint=false fmt="" mp_is_tmp=false overlay=false +- local cd_mountpoint=false fmt="" mp_is_tmp=false overlay=false - local img_mp="" workd="" -+ local img_mp="" workd="" overlay_fstype="" ++ local cd_mountpoint=false fmt="" overlay=false rwmode="rw" ++ local img_mp="" - short_opts="CdhmPpsv" + short_opts="Cdhm:P:psSv" long_opts="cd-mountpoint,dev,help,format:,mountpoint:,overlay,partition:,proc,read-only,sys,system-mounts,system-resolvconf,verbose" getopt_out=$(getopt --name "${0##*/}" \ --options "${short_opts}" --long "${long_opts}" -- "$@") && -@@ -160,10 +195,10 @@ +@@ -160,14 +375,14 @@ -h|--help) Usage ; exit 0;; -m|--mountpoint) mp=$next;; -P|--partition) ptnum=$next;; @@ -268,25 +588,233 @@ --system-resolvconf) system_resolvconf=true;; -v|--verbose) VERBOSITY=$((${VERBOSITY}+1));; --opts) opts="${opts} $next"; shift;; -@@ -302,7 +337,7 @@ - fi +- --read-only) readonly="ro";; ++ --read-only) readonly=true; rwmode="ro";; + --) shift; break;; + esac + shift; +@@ -175,20 +390,44 @@ - local nptnum="" -- debug 1 "connected $img_in ($fmt) to $nbd. waiting for device." -+ debug 1 "connected ${readonly:+${readonly} }$img_in ($fmt) to $nbd. waiting for device." - i=0 - while i=$(($i+1)):; do - get_partition "$nbd" && nptnum="$_RET" && break -@@ -342,7 +377,7 @@ - sleep .1 - done + [ $# -ge 2 ] || { bad_Usage "must provide image and cmd"; return 1; } + +- [ -n "$readonly" ] && { $system_resolvconf && ! $overlay; } && { ++ $readonly && { $system_resolvconf && ! $overlay; } && { + error "--read-only is incompatible with system-resolvconf"; + error "maybe try with --overlay" + return 1; + } + ++ local uns_switch=false + img_in="$1" + shift 1 +- img=$(readlink -f "$img_in") || +- { error "failed to get full path to $img_in"; return 1; } +- +- [ -f "$img" ] || +- { error "$img: not a file"; return 1; } ++ case "$img_in" in ++ lxd:*) ++ if [ "$ptnum" != "auto" ]; then ++ error "--ptnum incompatible with directory" ++ return 1; ++ fi ++ command -v lxc-usernsexec >/dev/null 2>&1 || { ++ error "lxd support needs lxc-usernsexec"; ++ return 1; ++ } ++ img="/var/lib/lxd/containers/${img_in#lxd:}/rootfs" ++ [ -d "$img" ] || { ++ error "${img_in}: no rootfs in $img. Not a container?" ++ return 1; ++ } ++ set_subids lxd || { ++ error "failed reading subids for lxd"; ++ return 1; ++ } ++ uns_switch=true ++ ;; ++ *) ++ img=$(readlink -f "$img_in") || ++ { error "failed to get full path to $img_in"; return 1; } ++ [ -f "$img" ] || ++ { error "$img: not a file"; return 1; } ++ ;; ++ esac + + [ "$(id -u)" = "0" ] || + { error "sorry, must be root"; return 1; } +@@ -199,7 +438,6 @@ + if [ -z "$mp" ]; then + mp="${TEMP_D}/mp" + mkdir "$mp" || return +- mp_is_tmp=true + else + [ -d "$mp" ] || + { error "mountpoint '$mp': not a directory"; return 1; } +@@ -215,169 +453,53 @@ + img_mp=$mp + fi + +- local cmd="" arg="" found=false +- cmd=( ) +- for arg in "$@"; do +- if [ "${arg}" = "_MOUNTPOINT_" ]; then +- debug 1 "replaced string _MOUNTPOINT_ in arguments arg ${#cmd[@]}" +- arg=$mp ++ out="" ++ if [ -d "$img" ]; then ++ debug 1 "using directory at $img for $img_in" ++ mp=$img ++ if $overlay; then ++ debug 1 "mounting $img to $mp" ++ mount --bind "$img" "$img_mp" || ++ { error "failed to mount --bind '$img' '$mp'"; return 1; } ++ UMOUNTS[${#UMOUNTS[@]}]="$mp" + fi +- cmd[${#cmd[@]}]="$arg" +- done +- +- if [ "${cmd[0]##*/}" = "bash" -o "${cmd[0]##*/}" = "sh" ] && +- [ ${#cmd[@]} -eq 0 ]; then +- debug 1 "invoking shell ${cmd[0]}" +- error "MOUNTPOINT=$mp" +- fi +- +- local hasqemu=false +- command -v "qemu-nbd" >/dev/null 2>&1 && hasqemu=true +- +- if out=$(set -f; mount -o loop${readonly:+,$readonly} $opts \ +- "$img" "$img_mp" 2>&1); then +- debug 1 "mounted simple filesystem image '$img_in'" ++ elif [ "$ptnum" = "auto" -o "$ptnum" = "0" ] && ++ out=$(set -f; mount -o "loop,$rwmode" $opts "$img" "$img_mp" 2>&1); then ++ debug 1 "mounted simple filesystem image $rwmode in '$img_in'" + UMOUNTS[${UMOUNT[@]}]="$img_mp" + else ++ local hasqemu=false ++ command -v "qemu-nbd" >/dev/null 2>&1 && hasqemu=true + if ! $hasqemu; then + error "simple mount of '$img_in' failed." +- error "if this not a raw image, or it is partitioned" ++ error "if this is not a simple unpartitioned raw image, then" + error "you must have qemu-nbd (apt-get install qemu-utils)" +- error "mount failed with: $out" +- return 1 +- fi +- fi +- +- if [ "${#UMOUNTS[@]}" -eq 0 ]; then +- if [ ! -e /sys/block/nbd0 ] && ! grep -q nbd /proc/modules; then +- debug 1 "trying to load nbd module" +- modprobe nbd >/dev/null 2>&1 +- udevadm settle >/dev/null 2>&1 +- fi +- [ -e /sys/block/nbd0 ] || { +- error "no nbd kernel support, but simple mount failed" +- return 1; +- } +- +- if [ -z "$fmt" ]; then +- get_image_format "$img" && fmt="$_RET" || { +- error "failed to get image format for '$img' (try --format)" +- return 1 +- } +- fi +- +- local f nbd="" pidfile="" pid="" roflag="" +- for f in /sys/block/nbd*; do +- [ -d "$f" -a ! -f "$f/pid" ] && nbd=${f##*/} && break +- done +- if [ -z "$nbd" ]; then +- error "failed to find an nbd device" +- return 1; +- fi +- nbd="/dev/$nbd" +- +- [ -n "$readonly" ] && roflag="--read-only" +- pidfile="/sys/block/${nbd##*/}/pid" pid="" +- if ! qemu-nbd $roflag "--format=$fmt" --connect "$nbd" "$img"; then +- { read pid < "$pidfile"; } >/dev/null 2>&1 && +- [ -d "/proc/$pid" ] && +- QEMU_DISCONNECT="$nbd" +- error "failed to qemu-nbd connect $img to $nbd" +- return 1 +- fi +- QEMU_DISCONNECT="$nbd" +- +- local pfile="/sys/block/${nbd#/dev/}/pid" +- if [ ! -f "$pfile" ]; then +- debug 1 "waiting on pidfile for $nbd in $pfile" +- local i=0 +- while [ ! -f "$pfile" ] && i=$(($i+1)); do +- if [ $i -eq 200 ]; then +- error "giving up on pidfile $pfile for $nbd" +- return 1 +- fi +- sleep .1 +- debug 2 "." +- done +- fi +- +- local nptnum="" +- debug 1 "connected $img_in ($fmt) to $nbd. waiting for device." +- i=0 +- while i=$(($i+1)):; do +- get_partition "$nbd" && nptnum="$_RET" && break +- [ $i -eq 40 ] && { +- error "gave up on $nbd" +- return 1 +- } +- [ $(($i%10)) -eq 0 ] && +- debug 1 "waiting for $nbd to be ready." +- sleep .1 +- done +- +- udevadm settle >/dev/null 2>&1 +- if [ "${ptnum}" = "auto" ]; then +- if [ "$nptnum" = "0" ]; then +- debug 1 "unpartitioned disk." +- else +- debug 1 "partitioned disk." ++ if [ -n "$out" ]; then ++ error "mount failed with: $out" + fi +- ptnum=$nptnum +- fi +- if [ "$ptnum" -ne 0 ]; then +- mdev="${nbd}p${ptnum}" +- else +- mdev="${nbd}" +- fi +- i=0 +- while :; do +- [ -b "$mdev" ] && break +- i=$(($i+1)) +- [ $i -eq 100 ] && { +- error "gave up on waiting for $mdev" +- return 1 +- } +- [ $(($i%10)) -eq 0 ] && +- debug 1 "waiting for $mdev part=$ptnum to be ready." +- sleep .1 +- done +- - if ( set -f; mount ${ro:+-o ${ro}} $opts "$mdev" "$img_mp" ) && -+ if ( set -f; mount ${readonly:+-o ${readonly}} $opts "$mdev" "$img_mp" ) && - UMOUNTS[${#UMOUNTS[@]}]="$img_mp"; then - debug 1 "mounted $mdev via qemu-nbd $nbd" - else -@@ -358,20 +393,14 @@ +- UMOUNTS[${#UMOUNTS[@]}]="$img_mp"; then +- debug 1 "mounted $mdev via qemu-nbd $nbd" +- else +- local pid="" pfile="/sys/block/${nbd#/dev/}/pid" +- { read pid < "$pfile" ; } >/dev/null 2>&1 +- [ -n "$pid" -a ! -d "/proc/$pid" ] || +- error "qemu-nbd process seems to have died. was '$pid'" +- +- error "failed to mount $mdev" + return 1 + fi +- ++ mount_nbd "$img" "$img_mp" "$fmt" "$ptnum" "$opts" || return ++ UMOUNTS[${#UMOUNTS[@]}]="$img_mp" fi if $overlay; then @@ -312,6 +840,62 @@ local bindmp="" for bindmp in $bmounts; do [ -d "$mp${bindmp}" ] || mkdir "$mp${bindmp}" || + { error "failed mkdir $bindmp in mount"; return 1; } +- mount --bind "$bindmp" "$mp/${bindmp}" || ++ mount --bind "$bindmp" "$mp${bindmp}" || + { error "failed bind mount '$bindmp'"; return 1; } ++ UMOUNTS[${#UMOUNTS[@]}]="$mp${bindmp}" ++ debug 1 "mounted $bindmp to $mp${bindmp}" + done + + if ${system_resolvconf}; then +@@ -393,6 +515,34 @@ + { error "failed copy /etc/resolv.conf"; return 1; } + fi + ++ local cmd="" arg="" found=false ++ cmd=( ) ++ for arg in "$@"; do ++ if [ "${arg}" = "_MOUNTPOINT_" ]; then ++ debug 1 "replaced string _MOUNTPOINT_ in arguments arg ${#cmd[@]}" ++ arg=$mp ++ fi ++ cmd[${#cmd[@]}]="$arg" ++ done ++ if [ "${cmd[0]##*/}" = "bash" -o "${cmd[0]##*/}" = "sh" ] && ++ [ ${#cmd[@]} -eq 0 ]; then ++ debug 1 "invoking shell ${cmd[0]}" ++ error "MOUNTPOINT=$mp" ++ fi ++ ++ if $uns_switch && [ "$1" = "chroot" ]; then ++ debug 1 "added helper uns-switch for chroot" ++ cmd=( uns-switch "${cmd[@]}" ) ++ elif $uns_switch; then ++ debug 1 "uns-switch helper can be used for changing user map" ++ fi ++ ++ add_helpers "$TEMP_D/bin" "$SUBUID" "$SUBGID" || { ++ error "failed to add helpers to $TEMP_D"; ++ return 1; ++ } ++ PATH="$TEMP_D/bin:$PATH" ++ + local startwd="$PWD" + debug 1 "invoking: MOUNTPOINT=$mp" "${cmd[@]}" + +@@ -414,11 +564,6 @@ + do_umounts "${UMOUNTS[@]}" && UMOUNTS=( ) || + { error "failed umount $img"; return 1; } + +- if "$mp_is_tmp"; then +- rmdir "$img_mp" || error "WARN: failed 'rmdir $img_mp'" +- fi +- +- + if [ -n "$QEMU_DISCONNECT" ]; then + disconnect_qemu || return 1; + fi === modified file 'bin/ubuntu-cloudimg-query' --- old/bin/ubuntu-cloudimg-query 2016-05-11 13:31:22 +0000 @@ -406,8 +990,8 @@ === added file 'test/test-mic' --- old/test/test-mic 1970-01-01 00:00:00 +0000 -+++ new/test/test-mic 2016-09-27 21:05:03 +0000 -@@ -0,0 +1,157 @@ ++++ new/test/test-mic 2016-10-05 14:35:51 +0000 +@@ -0,0 +1,200 @@ +#!/bin/bash + +set -e @@ -429,18 +1013,21 @@ +msg() { error "$@"; } + +do_pt_test() { -+ local img="$1" pt="$2" data="$3" info="$4" out="" -+ msg "testing partition $pt in $img" ++ local img="$1" pt="$2" data="$3" info="$4" premsg="$5" out="" ++ msg "${premsg}testing partition $pt in $img" + mount-image-callback --cd "--part=$pt" "$img" -- \ -+ sh -c "echo '$data' > data.txt" || -+ { error "failed writing to partition $pt in $img"; return 1; } ++ sh -c "echo '$data' > data.txt" || { ++ error "${premsg}failed writing to partition $pt in $img"; ++ return 1; ++ } + local expected=$(printf "%s\n%s\n" "$info" "$data") + out=$(mount-image-callback --read-only --cd "--part=$pt" "$img" -- \ -+ sh -c "cat info.txt data.txt") || -+ { error "failed mounting part1 for verification"; return 1; } -+ ++ sh -c "cat info.txt data.txt") || { ++ error "${premsg}failed mounting part $pt for verification"; ++ return 1; ++ } + [ "$expected" = "$out" ] || { -+ error "expected $pt to have:" ++ error "${premsg}expected $pt to have:" + error "$expected" + error "found:" + error "$out" @@ -455,7 +1042,8 @@ +pt2="${TEMP_D}/pt2.img" +prept="${TEMP_D}/header.img" +postpt="${TEMP_D}/foot.img" -+img="${TEMP_D}/disk.img" ++img_mbr="${TEMP_D}/disk-mbr.img" ++img_gpt="${TEMP_D}/disk-gpt.img" +MB=$((1024*1024)) +SSIZE=512 +pt1_size=$((100*$MB)) @@ -468,6 +1056,7 @@ +mkdir -p "$pt1_d" "$pt2_d" +echo "partition 1" > "$pt1_d/info.txt" +echo "file 1" > "$pt1_d/file1.txt" ++mkdir "$pt1_d/dev" "$pt1_d/sys" "$pt1_d/proc" "$pt1_d/mnt" +echo "partition 2" > "$pt2_d/info.txt" + +## Stage 1 @@ -522,17 +1111,49 @@ +[ "$out" = "file 1" ] || + fail "found unexpected contents in pt1:file1.txt after overlay: $out" + ++## Stage 1.7: Test --system-mounts ++msg "testing --system-mounts on pt1" ++mount-image-callback --read-only --system-mounts --cd -- "$pt1" sh -c ' ++ fails=0 ++ logfail() { fails=$(($fails+1)); echo "$@" 1>&2; } ++ [ -e proc/1 ] || logfail "proc/1 did not exist: proc not mounted" ++ [ -d sys/class ] || logfail "sys/class did not exist: sys not mounted" ++ [ -e dev/null ] || logfail "dev/null did not exist: dev not mounted"' || ++ fail "testing system-mounts failed." ++ ++## Stage 1.8: Test unmounts of unexpected mounted dirs. ++msg "testing unexpected mount get unmounted." ++mount-image-callback --read-only --cd -- "$pt1" sh -ec ' ++ fail() { echo "$@" 1>&2; } ++ mount -t tmpfs none -o size=10240 mnt/ || fail "failed tmpfs mount" ++ echo "hi mom" > mnt/file-on-tmpfs || ++ fail "failed write to file-on-tmpfs" ++ mkdir mnt/mnt2 || faili "failed mkdir mnt/mnt2" ++ mount -t tmpfs none -o size=4096 mnt/mnt2 || fail "failed 2nd tmpfs mount" ++ echo "hi again" > mnt/mnt2/second-file-on-tmpfs' || ++ fail "testing unexpected mounts failed." ++ ++# verify the file is not there. ++mount-image-callback --read-only --cd -- "$pt1" sh -c ' ++ fail() { echo "$@" 1>&2; exit 1; } ++ [ ! -e mnt/file-on-tmpfs ] || fail mnt/file-on-tmpfs existed ++ [ ! -e mnt/mnt2 ] || fail mnt/mnt2 existed ++ [ ! -e mnt/mnt2/second-file-on-tmpfs ] || fail second file existed ++ exit 0' || ++ fail "file mnt/file-on-tmpfs existed." ++ +## Stage 2 +## Create a full disk image with those 2 partition images inside -+## Then add a GPT partition table that points at the 2 partitions. ++## and a partition table that points to them. Do one for MBR and GPT. +truncate "--size=$prept_size" "$prept" +truncate "--size=$postpt_size" "$postpt" + -+msg "writing hunks to disk image $img" ++msg "writing hunks to disk image ${img_mbr}" +for hunk in "$prept" "$pt1" "$pt2" "$postpt"; do -+ rq dd bs=1M conv=notrunc oflag=append "if=$hunk" "of=$img" || ++ rq dd bs=1M conv=notrunc oflag=append "if=$hunk" "of=${img_mbr}" || + fail "failed adding $hunk to disk image" +done ++cp "${img_mbr}" "$img_gpt" + +# +sfdisk_in="$TEMP_D/ptable_in" @@ -549,18 +1170,24 @@ +done +) > "$sfdisk_in" + -+msg "partitioning disk image $img" -+rq sfdisk "$img" <"$sfdisk_in" ++msg "partitioning MBR disk image ${img_mbr}" ++(echo "label: dos"; cat "$sfdisk_in"; ) | rq sfdisk "${img_mbr}" + ++msg "partitioning GPT disk image ${img_gpt}" ++(echo "label: gpt"; cat "$sfdisk_in"; ) | rq sfdisk "${img_gpt}" + +## Stage 2.5 +## Verify we can mount each partition with '--part=N' +## write data, then remount and read expected data. -+do_pt_test "$img" 1 "foo1" "partition 1" || -+ fail "failed testing partition 1 on $img" ++for toks in "MBR:${img_mbr}" "GPT:${img_gpt}"; do ++ img=${toks#*:} ++ prefix=${toks%%:*} ++ do_pt_test "$img" 1 "foo1" "partition 1" "$prefix: " || ++ fail "${prefix}failed testing partition 1 on $img" + -+do_pt_test "$img" 2 "foo2" "partition 2" || -+ fail "failed testing partition 2 on $img" ++ do_pt_test "$img" 2 "foo2" "partition 2" "$prefix: " || ++ fail "${prefix}failed testing partition 2 on $img" ++done + +error "Finished tests." + diff -Nru cloud-utils-0.29/debian/tests/control cloud-utils-0.29/debian/tests/control --- cloud-utils-0.29/debian/tests/control 2016-10-04 16:33:28.000000000 +0000 +++ cloud-utils-0.29/debian/tests/control 2016-10-24 18:22:07.000000000 +0000 @@ -3,3 +3,6 @@ Tests: growpart-fsimage Restrictions: allow-stderr + +Tests: mic +Restrictions: needs-root isolation-machine allow-stderr diff -Nru cloud-utils-0.29/debian/tests/functions cloud-utils-0.29/debian/tests/functions --- cloud-utils-0.29/debian/tests/functions 2016-10-04 16:33:28.000000000 +0000 +++ cloud-utils-0.29/debian/tests/functions 2016-10-25 14:49:29.000000000 +0000 @@ -6,6 +6,7 @@ runtests() { local mytest="$1" pt_type resizer + make_executable "$1" for pt_type in gpt dos; do for resizer in auto sfdisk gdisk; do case "$pt_type:$resizer" in @@ -24,4 +25,18 @@ done } +make_executable() { + # in case the thing to be executed is not executable + # as it might be created by a patch application. + if [ -f "$1" -a ! -x "$1" ]; then + chmod +x "$1" || return + fi + return 0 +} + +execute() { + make_executable "$1" + "$@" +} + # vi: ts=4 expandtab diff -Nru cloud-utils-0.29/debian/tests/mic cloud-utils-0.29/debian/tests/mic --- cloud-utils-0.29/debian/tests/mic 1970-01-01 00:00:00.000000000 +0000 +++ cloud-utils-0.29/debian/tests/mic 2016-10-25 14:46:18.000000000 +0000 @@ -0,0 +1,6 @@ +#!/bin/sh +my_d=$(dirname "$0") +. "$my_d/functions" || { echo "failed to source functions" 1>&2; exit 1; } + +execute ./test/test-mic +# vi: ts=4 expandtab