add resize support, tested with 1 disk so far
[automated-distro-installer] / fresize
1 #!/bin/bash
2
3 shopt -s extdebug
4 bash-trace() {
5 # shows function args when: shopt -s extdebug
6 local -i argc_index=0 arg frame i start=${1:-1} max_indent=8 indent
7 local source
8 local extdebug=false
9 if [[ $(shopt -p extdebug) == *-s* ]]; then
10 extdebug=true
11 fi
12
13 for ((frame=0; frame < ${#FUNCNAME[@]}-1; frame++)); do
14 argc=${BASH_ARGC[frame]}
15 argc_index+=$argc
16 ((frame < start)) && continue
17 if (( ${#BASH_SOURCE[@]} > 1 )); then
18 source="${BASH_SOURCE[frame+1]}:${BASH_LINENO[frame]}:"
19 fi
20 indent=$((frame-start+1))
21 indent=$((indent < max_indent ? indent : max_indent))
22 printf "%${indent}s↳%sin \`%s" '' "$source" "${FUNCNAME[frame]}"
23 if $extdebug; then
24 for ((i=argc_index-1; i >= argc_index-argc; i--)); do
25 printf " %s" "${BASH_ARGV[i]}"
26 done
27 fi
28 echo \'
29 done
30 }
31
32
33 errcatch() {
34 set -E; shopt -s extdebug
35 _err-trap() {
36 err=$?
37 exec >&2
38 echo "${BASH_SOURCE[1]}:${BASH_LINENO[0]}:in \`$BASH_COMMAND' returned $err"
39 bash-trace 2
40 echo "$0: exiting with code $err"
41 exit $err
42 }
43 trap _err-trap ERR
44 set -o pipefail
45 }
46
47 errcatch
48
49 [[ $EUID == 0 ]] || sudo "$BASH_SOURCE" "$@"
50
51 usage() {
52 cat <<EOF
53 Usage: ${0##*/} [-n] +/-SIZE[g] swap|boot
54
55 Assuming Ian Kelling's partition scheme,
56 Resize swap or boot, expanding or shrinking the root fs and partition to compensate.
57
58 -n Dry run
59
60 SIZE is MiB, or if g is specified, GiB.
61
62 If using multiple devices, SIZE is applied to each device, so total change is
63 SIZE * devices.
64
65
66 EOF
67 exit $1
68 }
69
70 dry_run=false
71 case $1 in
72 -n) dry_run=true; shift ;;
73 -h|--help) usage ;;
74 esac
75
76 #### begin arg error checking ####
77
78 if [[ $# != 2 ]]; then
79 echo "$0: error: expected 2 arguments"
80 usage 1
81 fi
82
83 if [[ $1 != [+-][0-9]* ]]; then
84 echo "$0: error: bad 1st arg: $1"
85 usage 1
86 fi
87
88 if ! which parted &>/dev/null; then
89 echo "$0: error: install parted"
90 exit 1
91 fi
92
93 case $2 in swap|boot) : ;; *) echo "$0: error: bad 2nd arg"; usage 1 ;; esac
94
95 #### end arg error checking ####
96
97
98 boot=true
99 [[ $2 == boot ]] || boot=false
100
101 #size=${x#[+-]}
102 op_size=$1 # operator plus size
103 [[ $op_size != *g ]] || op_size=$(( ${op_size%g} / 1024 ))
104 size=${op_size#[+-]}
105
106 if [[ $op_size == +* ]]; then
107 op_size_rev=-$size # rev = reverse
108 grow=true
109 else
110 op_size_rev=+$size
111 grow=false
112 fi
113
114 ##### end command line parsing ########
115
116 rootn=1
117 swapn=2
118 bootn=3
119 needs_reboot=false
120
121 pmk() {
122 part=$1
123 start_op=$2
124 end_op=$3
125 read start end < <(echo ${ptable[$part]})
126 p mkpart primary "$4" $((start $start_op)) $((end $end_op))
127 }
128
129
130 swapoff -a
131
132 while read devid dev; do
133 if $dry_run; then
134 e() { echo "+ $@"; }
135 else
136 e() { echo "+ $@"; "$@"; }
137 fi
138 ptable=()
139 while IFS=: read id start end _; do
140 [[ $id == [0-9] ]] || continue
141 end=${end%MiB}
142 start=${start%MiB}
143 start=${start%.*} # small enough number that parted uses a decimal
144 ptable[$id]="$start $end"
145 done < <(parted -m /dev/$dev unit MiB print)
146 parted /dev/$dev unit MiB print | tee /root/backup_partition
147 p() { e parted -a optimal -s -- /dev/$dev unit MiB "$@"; }
148 e systemctl stop systemd-cryptsetup@crypt_swap_$dev$swapn
149 sleep 1
150 # These partition comments seems a little verbose now, but I bet they
151 # will be helpfull if I read this in more than a week from now.
152 # <> = deleted partition, () = partition
153 p rm $swapn # ( root )< swap >( boot )
154
155 root_resize_cmd="e btrfs fi resize $devid:$op_size_rev /"
156 if $grow; then $root_resize_cmd; fi
157 # if $grow; then
158 # < root >< swap >( boot )
159 # ( root ) >< swap >( boot )
160 # else
161 # < root >< swap >( boot )
162 # ( root >< ) swap >( boot )
163
164 out=$(p rm $rootn 2>&1)
165 echo "$out"
166
167 pmk $rootn "" $op_size_rev
168 if ! $grow; then
169 if echo "$out" | \
170 grep "but we have been unable to inform the kernel" &>/dev/null; then
171 e systemctl mask dev-mapper-crypt_swap_$dev$swapn.swap
172 e systemctl mask systemd-cryptsetup@crypt_swap_$dev$swapn.service
173 if ! $needs_reboot; then
174 needs_reboot=true
175 echo "$0: reboot and run /root/finish-resize to finish.
176 The following commands are what will be executed:"
177 rm -rf /root/finish-resize
178 cat >/root/finish-resize <<'EOF'
179 #!/bin/bash -x
180 set -eE -o pipefail
181 trap 'echo "$0:$LINENO:error: \"$BASH_COMMAND\" returned $?"' ERR
182 EOF
183 chmod +x /root/finish-resize
184 fi
185 e() { echo "$@" | tee -a /root/finish-resize; }
186 e systemctl unmask systemd-cryptsetup@crypt_swap_$dev$swapn.service
187 e systemctl unmask dev-mapper-crypt_swap_$dev$swapn.swap
188 # todo, disable swap for the next boot. currently have to wait
189 # 1:30 for it to fail.
190 fi
191 $root_resize_cmd
192 fi
193 if $boot; then
194 boot_devid=$(btrfs fi show /boot | \
195 sed -rn "s#^\s*devid\s+(\S+)\s.*$dev[0-9]#\1#p")
196
197 if ! $grow; then
198 # shrink boot, move it to a temp file
199 e btrfs fi resize $boot_devid:$op_size /boot
200 temp_boot=/root/temp_boot_dd
201 e dd bs=1M if=/dev/$dev$bootn of=$temp_boot count=$size
202 fi
203 # if $grow; then
204 # ( root ) >< swap >< boot >
205 # ( root )( >< swap ) >< boot >
206 # ( root )( >< swap )( >< boot )
207 # else
208 # ( root >< ) swap >< boot >
209 # ( root >< )( swap >< ) boot >
210 # ( root >< )( swap >< )( boot )
211 e umount /boot
212 p rm $bootn
213 pmk $swapn $op_size_rev $op_size_rev "linux-swap"
214 pmk $bootn $op_size_rev ""
215
216 if $grow; then
217 e dd bs=1M if=/dev/$dev$bootn of=/dev/$dev$bootn skip=$size
218 e btrfs fi resize $boot_devid:$op_size /boot
219 else
220 e dd bs=1M if=$temp_boot of=/dev/$dev$bootn
221 fi
222 e mount /boot
223 else
224 # if $grow; then ( root )( >< swap )( boot )
225 # else ( root >< )( swap )( boot )
226 pmk $swapn $op_size_rev "" "linux-swap"
227 fi
228 done < <(btrfs fi show / | \
229 sed -nr 's#^\s*devid\s*(\S+)\s.*_([^_ ]+)[0-9]\s*$#\1 \2#p')
230
231 if $boot; then
232 e rm -rf $temp_boot
233 e rm /root/finish-resize
234 fi
235
236 if $needs_reboot; then
237 echo "$0: reminder, reboot then /root/finish-resize"
238 fi
239
240 #for dev in ${devs[@]}; do