5 # shows function args when: shopt -s extdebug
6 local -i argc_index
=0 arg frame i start
=${1:-1} max_indent
=8 indent
9 if [[ $
(shopt -p extdebug
) == *-s* ]]; then
13 for ((frame
=0; frame
< ${#FUNCNAME[@]}-1; frame
++)); do
14 argc
=${BASH_ARGC[frame]}
16 ((frame
< start
)) && continue
17 if (( ${#BASH_SOURCE[@]} > 1 )); then
18 source="${BASH_SOURCE[frame+1]}:${BASH_LINENO[frame]}:"
20 indent
=$
((frame-start
+1))
21 indent
=$
((indent
< max_indent ? indent
: max_indent
))
22 printf "%${indent}s↳%sin \`%s" '' "$source" "${FUNCNAME[frame]}"
24 for ((i
=argc_index-1
; i
>= argc_index-argc
; i--
)); do
25 printf " %s" "${BASH_ARGV[i]}"
34 set -E; shopt -s extdebug
38 echo "${BASH_SOURCE[1]}:${BASH_LINENO[0]}:in \`$BASH_COMMAND' returned $err"
40 echo "$0: exiting with code $err"
49 [[ $EUID == 0 ]] ||
exec sudo
"$BASH_SOURCE" "$@"
53 Usage: ${0##*/} [OPTIONS] +/-SIZE[g] swap|boot
55 Assuming Ian Kelling's partition scheme and we are currently into one of
56 it's encrypted oses (we it's btrfs filesystem to be mounted), resize
57 swap or boot, expanding or shrinking the root fs and partition to
58 compensate. If it changes the partition tables incorrectly, the
59 originals are stored in /root/backup_partition_table_<device_names>.
61 TODO: only tested on stretch. deactivation of swap on reboot
62 probably needs to be fixed on other oses. Even on stretch,
63 we get 1.5 minutes of waiting for the crypt_dev and normal
66 Warning!!! Backup your data. This script could have bugs in it.
68 -n Dry run. Note, this likely won't be the exact commands,
69 for example, if you are running outside a vm, there will
70 probably be a reboot required in the middle so the kernel
71 can know about partition changes.
72 -r Reboot right away if it's needed.
73 -f Force running on a distro that has not been tested.
74 -h|--help Print help and exit.
76 SIZE is MiB, or if g is specified, GiB.
78 If using multiple devices, SIZE is applied to each device, so total change is
81 Note: Uses GNU getopt options parsing style
90 temp
=$
(getopt
-l help rnfh
"$@") || usage
1
94 -r) reboot_now
=true
; shift ;;
95 -n) dry_run
=true
; shift ;;
96 -f) force
=true
; shift ;;
99 *) echo "$0: Internal error!" ; exit 1 ;;
103 #### begin arg error checking ####
105 if [[ $# != 2 ]]; then
106 echo "$0: error: expected 2 arguments"
110 if [[ $1 != [+-][0-9]* ]]; then
111 echo "$0: error: bad 1st arg: $1"
115 if ! which parted
&>/dev
/null
; then
116 echo "$0: error: install parted"
120 case $2 in swap|boot
) : ;; *) echo "$0: error: bad 2nd arg"; usage
1 ;; esac
122 if ! $force && ! grep -q 'VERSION=.*stretch' /etc
/os-release
; then
123 echo "$0: error: This distro is untested. Only tested version atm is Stretch."
127 #### end arg error checking ####
131 [[ $2 == boot
]] || boot
=false
133 op_size
=$1 # operator plus size
134 if [[ $op_size == *g
]]; then
138 size
=$
(( $size * 1024 ))
144 if [[ $op_size == +* ]]; then
145 op_size_rev
=-$size # rev = reverse
152 ##### end command line parsing ########
158 reboot_script_initialized
=false
160 pmk
() { # partition make
166 # This fails outside a vm, but actually succeeds. also prints this
168 # Error: Partition(s) 2 on /dev/sda have been written, but
169 # we have been unable to inform the kernel of the change, probably
170 # because it/they are in use. As a result, the old partition(s)
171 # will remain in use. You should reboot now before making further
174 if ! p mkpart primary
"$fs_type" \
175 $
((${ptable[start$part]} $start_op)) $
((${ptable[end$part]} $end_op)); then
176 echo "$0: warning: ignoring failure return of mkpart"
184 e
() { echo "+ $@"; "$@"; }
191 while read devid dev
; do
194 # older oses, it points to /dev/dm-x
195 dev
=$
(dmsetup info
$dev |
sed -rn 's/^\s*Name:\s*(\S*)/\1/p')
198 dev
=${dev#/dev/mapper/}
201 echo "$0: error: could not find devicemapper root dev,
202 make sure you are running from a encrypted root this script is resizing"
206 if [[ $dev != crypt_dev_
*-part$rootn ]]; then
207 echo "$0: error: unexpected root device name,
208 make sure you are running from a encrypted root this script is resizing"
211 dev
=${dev#crypt_dev_}
212 dev
=${dev%-part$rootn}
213 devpath
=/dev
/disk
/by-id
/$dev
217 while IFS
=: read id start end psize _
; do
218 [[ $id == [0-9] ]] ||
continue
219 ptable
[start
$id]=start
=${start%%[^0-9]*}
220 ptable
[end
$id]=${end%%[^0-9]*}
221 ptable
[size
$id]=${psize%%[^0-9]*}
222 done < <(parted
-m $devpath unit MiB print
)
223 parted
$devpath unit MiB print |
tee /root
/backup_partition_table_
$dev
224 p
() { e parted
-a optimal
-s -- $devpath unit MiB
"$@"; }
225 unit
=systemd-cryptsetup@crypt_dev_
$dev-part$swapn
226 # note systemctl show can test if a unit exists.
227 if ! e systemctl stop
$unit; then
230 # there is a bug in jessie. this and the .swap unit are
231 # generated from /etc/fstab, and it escapes - to x2d, then doesn't escape it
232 # when looking for the file to use as swap. so, no swap is working on jessie
234 sleep 1 # dunno if this is needed,
235 # but systemd likes to do these kind of things in the background.
237 # These partition comments seems a little verbose now, but I bet they
238 # will be helpfull if I read this in more than a week from now.
239 # <> = deleted partition, () = partition
240 p
rm $swapn # ( root )< swap >( boot )
242 root_resize_cmd
="e btrfs fi resize $devid:${op_size_rev}M /"
243 if $grow; then $root_resize_cmd; fi
245 # < root >< swap >( boot )
246 # ( root ) >< swap >( boot )
248 # < root >< swap >( boot )
249 # ( root >< ) swap >( boot )
251 out
=$
(p
rm $rootn 2>&1)
254 pmk
$rootn "" $op_size_rev
257 grep "but we have been unable to inform the kernel" &>/dev
/null
; then
260 if $needs_reboot; then
261 # note: even if these units don't exist, this will succeed.
262 e systemctl mask dev-mapper-crypt_swap_
$dev$swapn.swap
263 e systemctl mask systemd-cryptsetup@crypt_swap_
$dev$swapn.service
264 e
() { echo "$@" >> /root
/finish-resize
; }
265 if ! $reboot_script_initialized; then
266 reboot_script_initialized
=true
267 rm -rf /root
/finish-resize
268 cat >/root
/finish-resize
<<'EOF'
271 trap 'echo "$0:$LINENO:error: \"$BASH_COMMAND\" returned $?" >&2' ERR
273 chmod +x
/root
/finish-resize
276 e systemctl unmask systemd-cryptsetup@crypt_swap_
$dev$swapn.service
277 e systemctl unmask dev-mapper-crypt_swap_
$dev$swapn.swap
283 # non by-id path, to match what btrfs fi show will tell us
284 boot_dev_path
=$
(readlink
-f $devpath-part$bootn)
285 boot_devid
=$
(btrfs
fi show
/boot | \
286 sed -rn "s#^\s*devid\s+(\S+)\s.*$boot_dev_path#\1#p")
289 # shrink boot, move it to a temp file
290 e btrfs
fi resize
$boot_devid:${op_size}M
/boot
292 temp_boot
=/root
/temp_boot_
$dev
293 e
dd bs
=1M
if=$boot_dev_path of
=$temp_boot \
294 count
=$
((${ptable[size$bootn]} $op_size))
299 # ( root ) >< swap >< boot >
300 # ( root )( >< swap ) >< boot >
301 # ( root )( >< swap )( >< boot )
303 # ( root >< ) swap >< boot >
304 # ( root >< )( swap >< ) boot >
305 # ( root >< )( swap >< )( boot )
307 pmk
$swapn $op_size_rev $op_size_rev "linux-swap"
308 pmk
$bootn $op_size_rev ""
311 e
dd bs
=1M
if=$boot_dev_path of
=$boot_dev_path skip
=$size
313 e btrfs
fi resize
$boot_devid:${op_size}M
/boot
315 e
dd bs
=1M
if=$temp_boot of
=$boot_dev_path
319 # if $grow; then ( root )( >< swap )( boot )
320 # else ( root >< )( swap )( boot )
321 pmk
$swapn $op_size_rev "" "linux-swap"
322 e systemctl start systemd-cryptsetup@crypt_swap_
$dev$swapn
324 done < <(btrfs
fi show
/ |
sed -nr 's#^\s*devid\s*(\S+).* path (.*)$#\1 \2#p')
328 e
rm -rf "/root/temp_boot_*"
329 e
rm -f /root
/finish-resize
332 if $needs_reboot; then
333 echo "$0: Reboot, run /root/finish-resize. It's contents:"
334 cat /root
/finish-resize
336 echo "$0: rebooting now"