The cirros image was rebuilt against the 3.13.0-83 kernel, drivers e1000e, igbvf...
[packages/trusty/cirros-testvm.git] / cirros-testvm / src-cirros / src / sbin / growpart
1 #!/bin/sh
2 #    Copyright (C) 2011 Canonical Ltd.
3 #    Copyright (C) 2013 Hewlett-Packard Development Company, L.P.
4 #
5 #    Authors: Scott Moser <smoser@canonical.com>
6 #             Juerg Haefliger <juerg.haefliger@hp.com>
7 #
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.
11 #
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.
16 #
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/>.
19
20 # the fudge factor. if within this many bytes dont bother
21 FUDGE=${GROWPART_FUDGE:-$((1024*1024))}
22 TEMP_D=""
23 RESTORE_FUNC=""
24 RESTORE_HUMAN=""
25 VERBOSITY=0
26 DISK=""
27 PART=""
28 PT_UPDATE=false
29 DRY_RUN=0
30
31 SFDISK_VERSION=""
32 SFDISK_2_26="22600"
33 MBR_BACKUP=""
34 GPT_BACKUP=""
35 _capture=""
36
37 error() {
38         echo "$@" 1>&2
39 }
40
41 fail() {
42         [ $# -eq 0 ] || echo "FAILED:" "$@"
43         exit 2
44 }
45
46 nochange() {
47         echo "NOCHANGE:" "$@"
48         exit 1
49 }
50
51 changed() {
52         echo "CHANGED:" "$@"
53         exit 0
54 }
55
56 change() {
57         echo "CHANGE:" "$@"
58         exit 0
59 }
60
61 cleanup() {
62         if [ -n "${RESTORE_FUNC}" ]; then
63                 error "***** WARNING: Resize failed, attempting to revert ******"
64                 if ${RESTORE_FUNC} ; then
65                         error "***** Appears to have gone OK ****"
66                 else
67                         error "***** FAILED! ******"
68                         if [ -n "${RESTORE_HUMAN}" -a -f "${RESTORE_HUMAN}" ]; then
69                                 error "**** original table looked like: ****"
70                                 cat "${RESTORE_HUMAN}" 1>&2
71                         else
72                                 error "We seem to have not saved the partition table!"
73                         fi
74                 fi
75         fi
76         [ -z "${TEMP_D}" -o ! -d "${TEMP_D}" ] || rm -Rf "${TEMP_D}"
77 }
78
79 debug() {
80         local level=${1}
81         shift
82         [ "${level}" -gt "${VERBOSITY}" ] && return
83         if [ "${DEBUG_LOG}" ]; then
84                 echo "$@" >>"${DEBUG_LOG}"
85         else
86                 error "$@"
87         fi
88 }
89
90 debugcat() {
91         local level="$1"
92         shift;
93         [ "${level}" -gt "$VERBOSITY" ] && return
94         if [ "${DEBUG_LOG}" ]; then
95                 cat "$@" >>"${DEBUG_LOG}"
96         else
97                 cat "$@" 1>&2
98         fi
99 }
100
101 mktemp_d() {
102         # just a mktemp -d that doens't need mktemp if its not there.
103         _RET=$(mktemp -d "${TMPDIR:-/tmp}/${0##*/}.XXXXXX" 2>/dev/null) &&
104                 return
105         _RET=$(umask 077 && t="${TMPDIR:-/tmp}/${0##*/}.$$" &&
106                 mkdir "${t}" && echo "${t}")
107         return
108 }
109
110 Usage() {
111         cat <<EOF
112 ${0##*/} disk partition
113    rewrite partition table so that partition takes up all the space it can
114    options:
115     -h | --help       print Usage and exit
116          --fudge F    if part could be resized, but change would be
117                       less than 'F' bytes, do not resize (default: ${FUDGE})
118     -N | --dry-run    only report what would be done, show new 'sfdisk -d'
119     -v | --verbose    increase verbosity / debug
120     -u | --update  R  update the the kernel partition table info after growing
121                       this requires kernel support and 'partx --update'
122                       R is one of:
123                        - 'auto'  : [default] update partition if possible
124                        - 'force' : try despite sanity checks (fail on failure)
125                        - 'off'   : do not attempt
126                        - 'on'    : fail if sanity checks indicate no support
127
128    Example:
129     - ${0##*/} /dev/sda 1
130       Resize partition 1 on /dev/sda
131 EOF
132 }
133
134 bad_Usage() {
135         Usage 1>&2
136         error "$@"
137         exit 2
138 }
139
140 sfdisk_restore_legacy() {
141         sfdisk --no-reread "${DISK}" -I "${MBR_BACKUP}"
142 }
143
144 sfdisk_restore() {
145         # files are named: sfdisk-<device>-<offset>.bak
146         local f="" offset="" fails=0
147         for f in "${MBR_BACKUP}"*.bak; do
148                 [ -f "$f" ] || continue
149                 offset=${f##*-}
150                 offset=${offset%.bak}
151                 [ "$offset" = "$f" ] && {
152                         error "WARN: confused by file $f";
153                         continue;
154                 }
155                 dd "if=$f" "of=${DISK}" seek=$(($offset)) bs=1 conv=notrunc ||
156                         { error "WARN: failed restore from $f"; fails=$(($fails+1)); }
157         done
158         return $fails
159 }
160
161 sfdisk_worked_but_blkrrpart_failed() {
162         local ret="$1" output="$2"
163         # exit code found was just 1, but dont insist on that
164         #[ $ret -eq 1 ] || return 1
165         # Successfully wrote the new partition table
166         grep -qi "Success.* wrote.* new.* partition" "$output" &&
167                 grep -qi "BLKRRPART: Device or resource busy" "$output"
168         return
169 }
170
171 get_sfdisk_version() {
172         # set SFDISK_VERSION to MAJOR*10000+MINOR*100+MICRO
173         local out oifs="$IFS" ver=""
174         [ -n "$SFDISK_VERSION" ] && return 0
175         # expected output: sfdisk from util-linux 2.25.2
176         out=$(sfdisk --version) ||
177                 { error "failed to get sfdisk version"; return 1; }
178         set -- $out
179         ver=$4
180         case "$ver" in
181                 [0-9]*.[0-9]*.[0-9]|[0-9].[0-9]*)
182                         IFS="."; set -- $ver; IFS="$oifs"
183                         SFDISK_VERSION=$(($1*10000+$2*100+${3:-0}))
184                         return 0;;
185                 *) error "unexpected output in sfdisk --version [$out]"
186                         return 1;;
187         esac
188 }
189
190 resize_sfdisk() {
191         local humanpt="${TEMP_D}/recovery"
192         local mbr_backup="${TEMP_D}/orig.save"
193         local restore_func=""
194         local format="$1"
195
196         local change_out=${TEMP_D}/change.out
197         local dump_out=${TEMP_D}/dump.out
198         local new_out=${TEMP_D}/new.out
199         local dump_mod=${TEMP_D}/dump.mod
200         local tmp="${TEMP_D}/tmp.out"
201         local err="${TEMP_D}/err.out"
202         local mbr_max_512="4294967296"
203
204         local pt_start pt_size pt_end max_end new_size change_info dpart
205         local sector_num sector_size disk_size tot out
206
207         rqe sfd_list sfdisk --list --unit=S "$DISK" >"$tmp" ||
208                 fail "failed: sfdisk --list $DISK"
209         if [ "${SFDISK_VERSION}" -lt ${SFDISK_2_26} ]; then
210                 # exected output contains: Units: sectors of 512 bytes, ...
211                 out=$(awk '$1 == "Units:" && $5 ~ /bytes/ { print $4 }' "$tmp") ||
212                         fail "failed to read sfdisk output"
213                 if [ -z "$out" ]; then
214                         error "WARN: sector size not found in sfdisk output, assuming 512"
215                         sector_size=512
216                 else
217                         sector_size="$out"
218                 fi
219                 local _w _cyl _w1 _heads _w2 sectors _w3 t s
220                 # show-size is in units of 1024 bytes (same as /proc/partitions)
221                 t=$(sfdisk --show-size "${DISK}") ||
222                         fail "failed: sfdisk --show-size $DISK"
223                 disk_size=$((t*1024))
224                 sector_num=$(($disk_size/$sector_size))
225                 msg="disk size '$disk_size' not evenly div by sector size '$sector_size'"
226                 [ "$((${disk_size}%${sector_size}))" -eq 0 ] ||
227                         error "WARN: $msg"
228                 restore_func=sfdisk_restore_legacy
229         else
230                 # --list first line output:
231                 # Disk /dev/vda: 20 GiB, 21474836480 bytes, 41943040 sectors
232                 local _x
233                 read _x _x _x _x disk_size _x sector_num _x  < "$tmp"
234                 sector_size=$((disk_size/$sector_num))
235                 restore_func=sfdisk_restore
236         fi
237
238         debug 1 "$sector_num sectors of $sector_size. total size=${disk_size} bytes"
239         [ $(($disk_size/512)) -gt $mbr_max_512 ] &&
240                 debug 1 "WARN: disk is larger than 2TB. additional space will go unused."
241
242         rqe sfd_dump sfdisk --unit=S --dump "${DISK}" >"${dump_out}" ||
243                 fail "failed to dump sfdisk info for ${DISK}"
244         RESTORE_HUMAN="$dump_out"
245
246         {
247                 echo "## sfdisk --unit=S --dump ${DISK}"
248                 cat "${dump_out}"
249         }  >"$humanpt"
250
251         [ $? -eq 0 ] || fail "failed to save sfdisk -d output"
252         RESTORE_HUMAN="$humanpt"
253
254         debugcat 1 "$humanpt"
255
256         sed -e 's/,//g; s/start=/start /; s/size=/size /' "${dump_out}" \
257                 >"${dump_mod}" ||
258                 fail "sed failed on dump output"
259
260         dpart="${DISK}${PART}" # disk and partition number
261         if [ -b "${DISK}p${PART}" -a "${DISK%[0-9]}" != "${DISK}" ]; then
262                 # for block devices that end in a number (/dev/nbd0)
263                 # the partition is "<name>p<partition_number>" (/dev/nbd0p1)
264                 dpart="${DISK}p${PART}"
265         elif [ "${DISK#/dev/loop[0-9]}" != "${DISK}" ]; then
266                 # for /dev/loop devices, sfdisk output will be <name>p<number>
267                 # format also, even though there is not a device there.
268                 dpart="${DISK}p${PART}"
269         fi
270
271         pt_start=$(awk '$1 == pt { print $4 }' "pt=${dpart}" <"${dump_mod}") &&
272                 pt_size=$(awk '$1 == pt { print $6 }' "pt=${dpart}" <"${dump_mod}") &&
273                 [ -n "${pt_start}" -a -n "${pt_size}" ] &&
274                 pt_end=$((${pt_size}+${pt_start})) ||
275                 fail "failed to get start and end for ${dpart} in ${DISK}"
276
277         # find the minimal starting location that is >= pt_end 
278         max_end=$(awk '$3 == "start" { if($4 >= pt_end && $4 < min)
279                 { min = $4 } } END { printf("%s\n",min); }' \
280                 min=${sector_num} pt_end=${pt_end} "${dump_mod}") &&
281                 [ -n "${max_end}" ] ||
282                 fail "failed to get max_end for partition ${PART}"
283
284         mbr_max_sectors=$((mbr_max_512*$((sector_size/512))))
285         if [ "$max_end" -gt "$mbr_max_sectors" ]; then
286                 max_end=$mbr_max_sectors
287         fi
288
289         if [ "$format" = "gpt" ]; then
290                 # sfdisk respects 'last-lba' in input, and complains about
291                 # partitions that go past that.  without it, it does the right thing.
292                 sed -i '/^last-lba:/d' "$dump_out" ||
293                         fail "failed to remove last-lba from output"
294         fi
295
296         local gpt_second_size="33"
297         if [ "${max_end}" -gt "$((${sector_num}-${gpt_second_size}))" ]; then
298                 # if mbr allow subsequent conversion to gpt without shrinking the
299                 # partition.  safety net at cost of 33 sectors, seems reasonable.
300                 # if gpt, we can't write there anyway.
301                 debug 1 "padding ${gpt_second_size} sectors for gpt secondary header"
302                 max_end=$((${sector_num}-${gpt_second_size}))
303         fi
304
305         debug 1 "max_end=${max_end} tot=${sector_num} pt_end=${pt_end}" \
306                 "pt_start=${pt_start} pt_size=${pt_size}"
307         [ $((${pt_end})) -eq ${max_end} ] &&
308                 nochange "partition ${PART} is size ${pt_size}. it cannot be grown"
309         [ $((${pt_end}+(${FUDGE}/$sector_size))) -gt ${max_end} ] &&
310                 nochange "partition ${PART} could only be grown by" \
311                 "$((${max_end}-${pt_end})) [fudge=$((${FUDGE}/$sector_size))]"
312
313         # now, change the size for this partition in ${dump_out} to be the
314         # new size
315         new_size=$((${max_end}-${pt_start}))
316         sed "\|^\s*${dpart} |s/${pt_size},/${new_size},/" "${dump_out}" \
317                 >"${new_out}" ||
318                 fail "failed to change size in output"
319
320         change_info="partition=${PART} start=${pt_start} old: size=${pt_size} end=${pt_end} new: size=${new_size},end=${max_end}"
321         if [ ${DRY_RUN} -ne 0 ]; then
322                 echo "CHANGE: ${change_info}"
323                 {
324                         echo "# === old sfdisk -d ==="
325                         cat "${dump_out}"
326                         echo "# === new sfdisk -d ==="
327                         cat "${new_out}"
328                 } 1>&2
329                 exit 0
330         fi
331
332         MBR_BACKUP="${mbr_backup}"
333         LANG=C sfdisk --no-reread "${DISK}" --force \
334                 -O "${mbr_backup}" <"${new_out}" >"${change_out}" 2>&1
335         ret=$?
336         [ $ret -eq 0 ] || RESTORE_FUNC="${restore_func}"
337
338         if [ $ret -eq 0 ]; then
339                 :
340         elif $PT_UPDATE &&
341                 sfdisk_worked_but_blkrrpart_failed "$ret" "${change_out}"; then
342                 # if the command failed, but it looks like only because
343                 # the device was busy and we have pt_update, then go on
344                 debug 1 "sfdisk failed, but likely only because of blkrrpart"
345         else
346                 error "attempt to resize ${DISK} failed. sfdisk output below:"
347                 sed 's,^,| ,' "${change_out}" 1>&2
348                 fail "failed to resize"
349         fi
350
351         rq pt_update pt_update "$DISK" "$PART" ||
352                 fail "pt_resize failed"
353
354         RESTORE_FUNC=""
355
356         changed "${change_info}"
357
358         # dump_out looks something like:
359         ## partition table of /tmp/out.img
360         #unit: sectors
361         #
362         #/tmp/out.img1 : start=        1, size=    48194, Id=83
363         #/tmp/out.img2 : start=    48195, size=   963900, Id=83
364         #/tmp/out.img3 : start=  1012095, size=   305235, Id=82
365         #/tmp/out.img4 : start=  1317330, size=   771120, Id= 5
366         #/tmp/out.img5 : start=  1317331, size=   642599, Id=83
367         #/tmp/out.img6 : start=  1959931, size=    48194, Id=83
368         #/tmp/out.img7 : start=  2008126, size=    80324, Id=83
369 }
370
371 gpt_restore() {
372         sgdisk -l "${GPT_BACKUP}" "${DISK}"
373 }
374
375 resize_sgdisk() {
376         GPT_BACKUP="${TEMP_D}/pt.backup"
377
378         local pt_info="${TEMP_D}/pt.info"
379         local pt_pretend="${TEMP_D}/pt.pretend"
380         local pt_data="${TEMP_D}/pt.data"
381         local out="${TEMP_D}/out"
382
383         local dev="disk=${DISK} partition=${PART}"
384
385         local pt_start pt_end pt_size last pt_max code guid name new_size
386         local old new change_info sector_size
387         
388         # Dump the original partition information and details to disk. This is
389         # used in case something goes wrong and human interaction is required
390         # to revert any changes.
391         rqe sgd_info sgdisk "--info=${PART}" --print "${DISK}" >"${pt_info}" ||
392                 fail "${dev}: failed to dump original sgdisk info"
393         RESTORE_HUMAN="${pt_info}"
394
395         sector_size=$(awk '$0 ~ /^Logical sector size:.*bytes/ { print $4 }' \
396                 "$pt_info") && [ -n "$sector_size" ] || {
397                 sector_size=512
398                 error "WARN: did not find sector size, assuming 512"
399         }
400
401         debug 1 "$dev: original sgdisk info:"
402         debugcat 1 "${pt_info}"
403
404         # Pretend to move the backup GPT header to the end of the disk and dump
405         # the resulting partition information. We use this info to determine if
406         # we have to resize the partition.
407         rqe sgd_pretend sgdisk --pretend --move-second-header \
408                 --print "${DISK}" >"${pt_pretend}" ||
409                 fail "${dev}: failed to dump pretend sgdisk info"
410
411         debug 1 "$dev: pretend sgdisk info"
412         debugcat 1 "${pt_pretend}"
413
414         # Extract the partition data from the pretend dump
415         awk 'found { print } ; $1 == "Number" { found = 1 }' \
416                 "${pt_pretend}" >"${pt_data}" ||
417                 fail "${dev}: failed to parse pretend sgdisk info"
418
419         # Get the start and end sectors of the partition to be grown
420         pt_start=$(awk '$1 == '"${PART}"' { print $2 }' "${pt_data}") &&
421                 [ -n "${pt_start}" ] ||
422                 fail "${dev}: failed to get start sector"
423         pt_end=$(awk '$1 == '"${PART}"' { print $3 }' "${pt_data}") &&
424                 [ -n "${pt_end}" ] ||
425                 fail "${dev}: failed to get end sector"
426         pt_size="$((${pt_end} - ${pt_start}))"
427
428         # Get the last usable sector
429         last=$(awk '/last usable sector is/ { print $NF }' \
430                 "${pt_pretend}") && [ -n "${last}" ] ||
431                 fail "${dev}: failed to get last usable sector"
432
433         # Find the minimal start sector that is >= pt_end 
434         pt_max=$(awk '{ if ($2 >= pt_end && $2 < min) { min = $2 } } END \
435                 { print min }' min="${last}" pt_end="${pt_end}" \
436                 "${pt_data}") && [ -n "${pt_max}" ] ||
437                 fail "${dev}: failed to find max end sector"
438
439         debug 1 "${dev}: pt_start=${pt_start} pt_end=${pt_end}" \
440                 "pt_size=${pt_size} pt_max=${pt_max} last=${last}"
441
442         # Check if the partition can be grown
443         [ "${pt_end}" -eq "${pt_max}" ] &&
444                 nochange "${dev}: size=${pt_size}, it cannot be grown"
445         [ "$((${pt_end} + ${FUDGE}/${sector_size}))" -gt "${pt_max}" ] &&
446                 nochange "${dev}: could only be grown by" \
447                 "$((${pt_max} - ${pt_end})) [fudge=$((${FUDGE}/$sector_size))]"
448
449         # The partition can be grown if we made it here. Get some more info
450         # about it so we can do it properly.
451         # FIXME: Do we care about the attribute flags?
452         code=$(awk '/^Partition GUID code:/ { print $4 }' "${pt_info}")
453         guid=$(awk '/^Partition unique GUID:/ { print $4 }' "${pt_info}")
454         name=$(awk '/^Partition name:/ { gsub(/'"'"'/, "") ; \
455                 if (NF >= 3) print substr($0, index($0, $3)) }' "${pt_info}")
456         [ -n "${code}" -a -n "${guid}" ] ||
457                 fail "${dev}: failed to parse sgdisk details"
458
459         debug 1 "${dev}: code=${code} guid=${guid} name='${name}'"
460         local wouldrun=""
461         [ "$DRY_RUN" -ne 0 ] && wouldrun="would-run"
462
463         # Calculate the new size of the partition
464         new_size=$((${pt_max} - ${pt_start}))
465         old="old: size=${pt_size},end=${pt_end}"
466         new="new: size=${new_size},end=${pt_max}"
467         change_info="${dev}: start=${pt_start} ${old} ${new}"
468         
469         # Backup the current partition table, we're about to modify it
470         rq sgd_backup $wouldrun sgdisk "--backup=${GPT_BACKUP}" "${DISK}" ||
471                 fail "${dev}: failed to backup the partition table"
472
473         # Modify the partition table. We do it all in one go (the order is
474         # important!):
475         #  - move the GPT backup header to the end of the disk
476         #  - delete the partition
477         #  - recreate the partition with the new size
478         #  - set the partition code
479         #  - set the partition GUID
480         #  - set the partition name
481         rq sgdisk_mod $wouldrun sgdisk --move-second-header "--delete=${PART}" \
482                 "--new=${PART}:${pt_start}:${pt_max}" \
483                 "--typecode=${PART}:${code}" \
484                 "--partition-guid=${PART}:${guid}" \
485                 "--change-name=${PART}:${name}" "${DISK}" &&
486                 rq pt_update $wouldrun pt_update "$DISK" "$PART" || {
487                 RESTORE_FUNC=gpt_restore
488                 fail "${dev}: failed to repartition"
489         }
490
491         # Dry run
492         [ "${DRY_RUN}" -ne 0 ] && change "${change_info}"
493
494         changed "${change_info}"
495 }
496
497 kver_to_num() {
498         local kver="$1" maj="" min="" mic="0"
499         kver=${kver%%-*}
500         maj=${kver%%.*}
501         min=${kver#${maj}.}
502         min=${min%%.*}
503         mic=${kver#${maj}.${min}.}
504         [ "$kver" = "$mic" ] && mic=0
505         _RET=$(($maj*1000*1000+$min*1000+$mic))
506 }
507
508 kver_cmp() {
509         local op="$2" n1="" n2=""
510         kver_to_num "$1"
511         n1="$_RET"
512         kver_to_num "$3"
513         n2="$_RET"
514         [ $n1 $op $n2 ]
515 }
516
517 rq() {
518         # runquieterror(label, command)
519         # gobble stderr of a command unless it errors
520         local label="$1" ret="" efile=""
521         efile="$TEMP_D/$label.err"
522         shift;
523
524         local rlabel="running"
525         [ "$1" = "would-run" ] && rlabel="would-run" && shift
526
527         local cmd="" x=""
528         for x in "$@"; do
529                 [ "${x#* }" != "$x" -o "${x#* \"}" != "$x" ] && x="'$x'"
530                 cmd="$cmd $x"
531         done
532         cmd=${cmd# }
533
534         debug 2 "$rlabel[$label][$_capture]" "$cmd"
535         [ "$rlabel" = "would-run" ] && return 0
536
537         if [ "${_capture}" = "erronly" ]; then
538                 "$@" 2>"$TEMP_D/$label.err"
539                 ret=$?
540         else
541                 "$@" >"$TEMP_D/$label.err" 2>&1
542                 ret=$?
543         fi
544         if [ $ret -ne 0 ]; then
545                 error "failed [$label:$ret]" "$@"
546                 cat "$efile" 1>&2
547         fi
548         return $ret
549 }
550
551 rqe() {
552         local _capture="erronly"
553         rq "$@"
554 }
555
556 verify_ptupdate() {
557         local input="$1" found="" reason="" kver=""
558
559         # we can always satisfy 'off'
560         if [ "$input" = "off" ]; then
561                 _RET="false";
562                 return 0;
563         fi
564
565         if command -v partx >/dev/null 2>&1; then
566                 local out="" ret=0
567                 out=$(partx --help 2>&1)
568                 ret=$?
569                 if [ $ret -eq 0 ]; then
570                         echo "$out" | grep -q -- --update || {
571                                 reason="partx has no '--update' flag in usage."
572                                 found="off"
573                         }
574                 else
575                         reason="'partx --help' returned $ret. assuming it is old."
576                         found="off"
577                 fi
578         else
579                 reason="no 'partx' command"
580                 found="off"
581         fi
582
583         if [ -z "$found" ]; then
584                 if [ "$(uname)" != "Linux" ]; then
585                         reason="Kernel is not Linux per uname."
586                         found="off"
587                 fi
588         fi
589
590         if [ -z "$found" ]; then
591                 kver=$(uname -r) || debug 1 "uname -r failed!"
592
593                 if ! kver_cmp "${kver-0.0.0}" -ge 3.8.0; then
594                         reason="Kernel '$kver' < 3.8.0."
595                         found="off"
596                 fi
597         fi
598
599         if [ -z "$found" ]; then
600                 _RET="true"
601                 return 0
602         fi
603
604         case "$input" in
605                 on) error "$reason"; return 1;;
606                 auto)
607                         _RET="false";
608                         debug 1 "partition update disabled: $reason"
609                         return 0;;
610                 force)
611                         _RET="true"
612                         error "WARNING: ptupdate forced on even though: $reason"
613                         return 0;;
614         esac
615         error "unknown input '$input'";
616         return 1;
617 }
618
619 pt_update() {
620         local dev="$1" part="$2" update="${3:-$PT_UPDATE}"
621         if ! $update; then
622                 return 0
623         fi
624         # partx only works on block devices (do not run on file)
625         [ -b "$dev" ] || return 0
626         partx --update "$part" "$dev"
627 }
628
629 has_cmd() {
630         command -v "${1}" >/dev/null 2>&1
631 }
632
633 resize_sgdisk_gpt() {
634         resize_sgdisk gpt
635 }
636
637 resize_sgdisk_dos() {
638         fail "unable to resize dos label with sgdisk"
639 }
640
641 resize_sfdisk_gpt() {
642         resize_sfdisk gpt
643 }
644
645 resize_sfdisk_dos() {
646         resize_sfdisk dos
647 }
648
649 get_table_format() {
650         local out="" disk="$1"
651         if has_cmd blkid && out=$(blkid -o value -s PTTYPE "$disk") &&
652                 [ "$out" = "dos" -o "$out" = "gpt" ]; then
653                 _RET="$out"
654                 return
655         fi
656         _RET="dos"
657         if [ ${SFDISK_VERSION} -lt ${SFDISK_2_26} ] &&
658                 out=$(sfdisk --id --force "$disk" 1); then
659                 if [ "$out" = "ee" ]; then
660                         _RET="gpt"
661                 else
662                         _RET="dos"
663                 fi
664                 return
665         elif out=$(LANG=C sfdisk --list "$disk"); then
666                 out=$(echo "$out" | sed -e '/Disklabel type/!d' -e 's/.*: //')
667                 case "$out" in
668                         gpt|dos) _RET="$out";;
669                         *) error "WARN: unknown label $out";;
670                 esac
671         fi
672 }
673
674 get_resizer() {
675         local format="$1" user=${2:-"auto"}
676
677         case "$user" in
678                 sgdisk) _RET="resize_sgdisk_$format"; return;;
679                 sfdisk) _RET="resize_sfdisk_$format"; return;;
680                 auto) :;;
681                 *) error "unexpected input: '$user'";;
682         esac
683
684         if [ "$format" = "dos" ]; then
685                 _RET="resize_sfdisk_dos"
686                 return 0
687         fi
688
689         if [ "${SFDISK_VERSION}" -ge ${SFDISK_2_26} ]; then
690                 _RET="resize_sfdisk_gpt"
691         elif has_cmd sgdisk; then
692                 _RET="resize_sgdisk_$format"
693         else
694                 error "no tools available to resize disk with '$format'"
695                 return 1
696         fi
697         return 0
698 }
699
700 pt_update="auto"
701 resizer=${GROWPART_RESIZER:-"auto"}
702 while [ $# -ne 0 ]; do
703         cur=${1}
704         next=${2}
705         case "$cur" in
706                 -h|--help)
707                         Usage
708                         exit 0
709                         ;;
710                 --fudge)
711                         FUDGE=${next}
712                         shift
713                         ;;
714                 -N|--dry-run)
715                         DRY_RUN=1
716                         ;;
717                 -u|--update|--update=*)
718                         if [ "${cur#--update=}" != "$cur" ]; then
719                                 next="${cur#--update=}"
720                         else
721                                 shift
722                         fi
723                         case "$next" in
724                                 off|auto|force|on) pt_update=$next;;
725                                 *) fail "unknown --update option: $next";;
726                         esac
727                         ;;
728                 -v|--verbose)
729                         VERBOSITY=$(($VERBOSITY+1))
730                         ;;
731                 --)
732                         shift
733                         break
734                         ;;
735                 -*)
736                         fail "unknown option ${cur}"
737                         ;;
738                 *)
739                         if [ -z "${DISK}" ]; then
740                                 DISK=${cur}
741                         else
742                                 [ -z "${PART}" ] || fail "confused by arg ${cur}"
743                                 PART=${cur}
744                         fi
745                         ;;
746         esac
747         shift
748 done
749
750 [ -n "${DISK}" ] || bad_Usage "must supply disk and partition-number"
751 [ -n "${PART}" ] || bad_Usage "must supply partition-number"
752
753 has_cmd "sfdisk" || fail "sfdisk not found"
754 get_sfdisk_version || fail
755
756 [ -e "${DISK}" ] || fail "${DISK}: does not exist"
757
758 [ "${PART#*[!0-9]}" = "${PART}" ] || fail "partition-number must be a number"
759
760 verify_ptupdate "$pt_update" || fail
761 PT_UPDATE=$_RET
762
763 debug 1 "update-partition set to $PT_UPDATE"
764
765 mktemp_d && TEMP_D="${_RET}" || fail "failed to make temp dir"
766 trap cleanup 0 # EXIT - some shells may not like 'EXIT' but are ok with 0
767
768 # get the ID of the first partition to determine if it's MBR or GPT
769 get_table_format "$DISK" || fail
770 format=$_RET
771 get_resizer "$format" "$resizer" ||
772     fail "failed to get a resizer for id '$id'"
773 resizer=$_RET
774
775 debug 1 "resizing $PART on $DISK using $resizer"
776 "$resizer"
777
778 # vi: ts=4 noexpandtab