2 # part2disk - wrap a partition image in a disk image
4 # Copyright (C) 2010 Canonical Ltd.
6 # Authors: Scott Moser <smoser@canonical.com>
8 # This program is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation, version 3 of the License.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
22 base_d=$(dirname $(readlink -f "${0}"))
23 PATH="${PATH}:${base_d}"
24 error() { echo "$@" 1>&2; }
26 [ "${DEBUG}" -ge "${1:-0}" ] && shift || return 0;
29 fail() { [ $# -eq 0 ] || error "$@"; exit 1; }
31 local fname="$1" kname="" size=""
32 if [ -b "${fname}" ]; then
33 kname=$(readlink -f "${fname}") &&
34 size=$(awk '$4 == kn { print $3 * 1024 }' \
35 "kn=${kname##*/}" /proc/partitions) &&
37 error "failed to read size of ${fname} from /proc/partitions";
41 size=$(stat --format "%s" "${fname}") || {
42 error "failed to get size of ${fname}"
51 Usage: ${0##*/} [options] partition-image disk-image
53 create disk image 'disk-image' with 'partition-image' in a partition
57 -G | --grub install grub to disk image mbr
58 | --grub1 install grub1 to disk image mbr
59 -s | --size S create the disk image of size 'S'.
60 default is large enough to fit partition-image
61 -v | --verbose increase verbosity
64 bad_Usage() { Usage 1>&2; fail "$@"; }
66 # converts size suitable for input to resize2fs to bytes
67 # s:512 byte sectors, K:kilobytes, M:megabytes, G:gigabytes
68 # none: block size of the image
69 local input=${1} defunit=${2:-1024}
72 *s) count=${input%s}; unit=512;;
73 *K) count=${input%K}; unit=1024;;
74 *M) count=${input%M}; unit=$((1024*1024));;
75 *G) count=${input%G}; unit=$((1024*1024*1024));;
76 *) count=${input} ; unit=${2:-1024};;
78 _RET=$((${count}*${unit}))
81 short_opts="b:c:Ghs:v"
82 long_opts="grub1,grub,help,size:,verbose"
83 getopt_out=$(getopt --name "${0##*/}" \
84 --options "${short_opts}" --long "${long_opts}" -- "$@") &&
85 eval set -- "${getopt_out}" ||
88 ssize=${DEF_SECTOR_SIZE}
93 while [ $# -ne 0 ]; do
98 -h|--help) Usage; exit 0;;
99 -s|--size) size_in=$2; shift;;
100 -v|--verbose) DEBUG=$((${DEBUG}+1));;
106 [ $# -eq 2 ] || bad_Usage "must supply partition image and output file"
111 { [ ${grub} -eq 0 ] || phelper=$(command -v part2disk-grubhelper); } ||
112 fail "no part2disk-grubhelper in PATH"
113 [ $grub1 -eq 0 ] || command -v grub >/dev/null || fail "no 'grub' in path";
115 [ -f "${pimg}" -o -b "${pimg}" ] || fail "${pimg}: not a file or block device"
118 fail "failed to get size of $pimg"
121 # end_pad_sectors: room for secondary gpt
123 end_pad=$(($end_pad_sectors*$ssize))
124 front_pad=$((1024*1024))
125 front_pad_sectors=$(($front_pad/$ssize))
126 padding=$(($front_pad+$end_pad))
127 pt_sector_pad=$(($ssize-(${pimg_s}%$ssize)))
128 if [ $pt_sector_pad -eq $ssize ]; then
131 pt_size=$(($pimg_s+$pt_sector_pad))
132 pt_sectors=$(($pt_size/$ssize))
133 tot_size=$(($front_pad+$pt_size+$end_pad))
134 tot_size_sectors=$(($tot_size/$ssize))
136 if [ -n "${size_in}" ]; then
137 human2bytes "${size_in}" 1 || fail "failed to convert ${size_in} to bytes"
140 # no size specified, get enough to fit part_img, 1M header and 1M end.
141 size=$((${pimg_s}+$padding+$pt_sector_pad))
144 if [ -e "$dimg" ]; then
146 fail "failed to get size of $dimg"
152 if [ "${dimg_s}" -lt "$size" ]; then
153 fail "size of $dimg ($dimg_s) not large enough to fit $size"
156 debug 1 "input is ${pimg_s} bytes ($pt_sectors sectors of $ssize)."
157 debug 1 "target is ${tot_size} bytes ($tot_size_sectors sectors)."
158 debug 1 "padding $front_pad_sectors sectors at front," \
159 "$end_pad_sectors sectors at end."
161 debug 2 "zeroing first $front_pad_sectors sectors $dimg"
162 dd if=/dev/zero of="${dimg}" bs=$ssize "count=${front_pad_sectors}" \
164 fail "failed to write to ${dimg}"
166 # copy partition image. this writes $pimg bytes even if that is
167 # not divivisble by $ssize
168 debug 2 "copying ${pimg} to partition in ${dimg}"
169 dd if="$pimg" of="$dimg" seek=${front_pad_sectors} bs=$ssize conv=notrunc \
171 fail "failed to write ${pimg} into ${dimg}"
173 # zero any unwritten portion of the final sector
174 leftover=$(($ssize-(${pimg_s}%$ssize)))
175 if [ $pt_sector_pad -ne 0 ]; then
176 debug 2 "finishing final sector with $pt_sector_pad bytes of zeros"
177 dd if=/dev/zero of="$dimg" bs=1 seek=$((${pimg_s}+${front_pad})) \
178 conv=notrunc count=$pt_sector_pad 2>/dev/null ||
179 fail "failed to finish final sector of $pimg"
182 # we've now written front pad + round-sectors. now write end_pad
183 debug 2 "writing final $end_pad_sectors sectors"
184 dd if=/dev/zero "of=$dimg" bs=$ssize \
185 seek=$((($size/$ssize)-${end_pad_sectors})) count=${end_pad_sectors} \
186 conv=notrunc 2>/dev/null ||
187 fail "failed to write final 1M to $pimg"
189 sfdisk_in="${front_pad_sectors},$pt_sectors,L,*"
190 debug 2 "writing partition table to ${dimg} ($sfdisk_in)"
191 sfdisk_out=$(echo "$sfdisk_in" | sfdisk --force --unit=S "${dimg}" 2>&1)
194 error "${sfdisk_out}";
195 fail "failed to create partition table";
198 if [ ${grub} -ne 0 ]; then
199 debug 2 "invoking part2disk-grubhelper ${dimg}"
200 sudo "${phelper}" "${dimg}" ||
201 fail "part2disk-grubhelper ${dimg} failed"
204 if [ $grub1 -ne 0 ]; then
205 debug 2 "installing grub"
206 grub --no-floppy --batch <<EOF
214 error "wrote to ${dimg}"
215 # vi: ts=4 noexpandtab