--- /dev/null
+#!/bin/sh
+# part2disk - wrap a partition image in a disk image
+#
+# Copyright (C) 2010 Canonical Ltd.
+#
+# Authors: Scott Moser <smoser@canonical.com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, version 3 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+DEF_SECTOR_SIZE=512
+DEBUG=0
+base_d=$(dirname $(readlink -f "${0}"))
+PATH="${PATH}:${base_d}"
+error() { echo "$@" 1>&2; }
+debug() {
+ [ "${DEBUG}" -ge "${1:-0}" ] && shift || return 0;
+ error "$@";
+}
+fail() { [ $# -eq 0 ] || error "$@"; exit 1; }
+getsize() {
+ local fname="$1" kname="" size=""
+ if [ -b "${fname}" ]; then
+ kname=$(readlink -f "${fname}") &&
+ size=$(awk '$4 == kn { print $3 * 1024 }' \
+ "kn=${kname##*/}" /proc/partitions) &&
+ [ -n "${size}" ] || {
+ error "failed to read size of ${fname} from /proc/partitions";
+ return 1;
+ }
+ else
+ size=$(stat --format "%s" "${fname}") || {
+ error "failed to get size of ${fname}"
+ return 1;
+ }
+ fi
+ _RET="$size"
+}
+
+Usage() {
+ cat <<EOF
+Usage: ${0##*/} [options] partition-image disk-image
+
+ create disk image 'disk-image' with 'partition-image' in a partition
+ inside it.
+
+ options:
+ -G | --grub install grub to disk image mbr
+ | --grub1 install grub1 to disk image mbr
+ -s | --size S create the disk image of size 'S'.
+ default is large enough to fit partition-image
+ -v | --verbose increase verbosity
+EOF
+}
+bad_Usage() { Usage 1>&2; fail "$@"; }
+human2bytes() {
+ # converts size suitable for input to resize2fs to bytes
+ # s:512 byte sectors, K:kilobytes, M:megabytes, G:gigabytes
+ # none: block size of the image
+ local input=${1} defunit=${2:-1024}
+ local unit count;
+ case "$input" in
+ *s) count=${input%s}; unit=512;;
+ *K) count=${input%K}; unit=1024;;
+ *M) count=${input%M}; unit=$((1024*1024));;
+ *G) count=${input%G}; unit=$((1024*1024*1024));;
+ *) count=${input} ; unit=${2:-1024};;
+ esac
+ _RET=$((${count}*${unit}))
+}
+
+short_opts="b:c:Ghs:v"
+long_opts="grub1,grub,help,size:,verbose"
+getopt_out=$(getopt --name "${0##*/}" \
+ --options "${short_opts}" --long "${long_opts}" -- "$@") &&
+ eval set -- "${getopt_out}" ||
+ bad_Usage
+
+ssize=${DEF_SECTOR_SIZE}
+size_in=""
+grub_ptnum=1
+grub=0
+grub1=0
+while [ $# -ne 0 ]; do
+ cur=${1}; next=${2};
+ case "$cur" in
+ -G|--grub) grub=1;;
+ -G|--grub1) grub1=1;;
+ -h|--help) Usage; exit 0;;
+ -s|--size) size_in=$2; shift;;
+ -v|--verbose) DEBUG=$((${DEBUG}+1));;
+ --) shift; break;;
+ esac
+ shift;
+done
+
+[ $# -eq 2 ] || bad_Usage "must supply partition image and output file"
+
+pimg=${1}
+dimg=${2}
+
+{ [ ${grub} -eq 0 ] || phelper=$(command -v part2disk-grubhelper); } ||
+ fail "no part2disk-grubhelper in PATH"
+[ $grub1 -eq 0 ] || command -v grub >/dev/null || fail "no 'grub' in path";
+
+[ -f "${pimg}" -o -b "${pimg}" ] || fail "${pimg}: not a file or block device"
+
+getsize "$pimg" ||
+ fail "failed to get size of $pimg"
+pimg_s="$_RET"
+
+# end_pad_sectors: room for secondary gpt
+end_pad_sectors=33
+end_pad=$(($end_pad_sectors*$ssize))
+front_pad=$((1024*1024))
+front_pad_sectors=$(($front_pad/$ssize))
+padding=$(($front_pad+$end_pad))
+pt_sector_pad=$(($ssize-(${pimg_s}%$ssize)))
+if [ $pt_sector_pad -eq $ssize ]; then
+ pt_sector_pad=0
+fi
+pt_size=$(($pimg_s+$pt_sector_pad))
+pt_sectors=$(($pt_size/$ssize))
+tot_size=$(($front_pad+$pt_size+$end_pad))
+tot_size_sectors=$(($tot_size/$ssize))
+
+if [ -n "${size_in}" ]; then
+ human2bytes "${size_in}" 1 || fail "failed to convert ${size_in} to bytes"
+ size=${_RET}
+else
+ # no size specified, get enough to fit part_img, 1M header and 1M end.
+ size=$((${pimg_s}+$padding+$pt_sector_pad))
+fi
+
+if [ -e "$dimg" ]; then
+ getsize "$dimg" ||
+ fail "failed to get size of $dimg"
+ dimg_s="$_RET"
+else
+ dimg_s="$size"
+fi
+
+if [ "${dimg_s}" -lt "$size" ]; then
+ fail "size of $dimg ($dimg_s) not large enough to fit $size"
+fi
+
+debug 1 "input is ${pimg_s} bytes ($pt_sectors sectors of $ssize)."
+debug 1 "target is ${tot_size} bytes ($tot_size_sectors sectors)."
+debug 1 "padding $front_pad_sectors sectors at front," \
+ "$end_pad_sectors sectors at end."
+
+debug 2 "zeroing first $front_pad_sectors sectors $dimg"
+dd if=/dev/zero of="${dimg}" bs=$ssize "count=${front_pad_sectors}" \
+ 2>/dev/null ||
+ fail "failed to write to ${dimg}"
+
+# copy partition image. this writes $pimg bytes even if that is
+# not divivisble by $ssize
+debug 2 "copying ${pimg} to partition in ${dimg}"
+dd if="$pimg" of="$dimg" seek=${front_pad_sectors} bs=$ssize conv=notrunc \
+ 2>/dev/null ||
+ fail "failed to write ${pimg} into ${dimg}"
+
+# zero any unwritten portion of the final sector
+leftover=$(($ssize-(${pimg_s}%$ssize)))
+if [ $pt_sector_pad -ne 0 ]; then
+ debug 2 "finishing final sector with $pt_sector_pad bytes of zeros"
+ dd if=/dev/zero of="$dimg" bs=1 seek=$((${pimg_s}+${front_pad})) \
+ conv=notrunc count=$pt_sector_pad 2>/dev/null ||
+ fail "failed to finish final sector of $pimg"
+fi
+
+# we've now written front pad + round-sectors. now write end_pad
+debug 2 "writing final $end_pad_sectors sectors"
+dd if=/dev/zero "of=$dimg" bs=$ssize \
+ seek=$((($size/$ssize)-${end_pad_sectors})) count=${end_pad_sectors} \
+ conv=notrunc 2>/dev/null ||
+ fail "failed to write final 1M to $pimg"
+
+sfdisk_in="${front_pad_sectors},$pt_sectors,L,*"
+debug 2 "writing partition table to ${dimg} ($sfdisk_in)"
+sfdisk_out=$(echo "$sfdisk_in" | sfdisk --force --unit=S "${dimg}" 2>&1)
+
+[ $? -eq 0 ] || {
+ error "${sfdisk_out}";
+ fail "failed to create partition table";
+}
+
+if [ ${grub} -ne 0 ]; then
+ debug 2 "invoking part2disk-grubhelper ${dimg}"
+ sudo "${phelper}" "${dimg}" ||
+ fail "part2disk-grubhelper ${dimg} failed"
+fi
+
+if [ $grub1 -ne 0 ]; then
+ debug 2 "installing grub"
+ grub --no-floppy --batch <<EOF
+device (hd0) $dimg
+root (hd0,0)
+setup (hd0)
+quit
+EOF
+fi
+
+error "wrote to ${dimg}"
+# vi: ts=4 noexpandtab