xref: /freebsd/libexec/rc/rc.d/growfs_postboot (revision a259b98fa211ed87bfee58c575de4e2de94ee0fa)
1#!/bin/sh
2#
3# Copyright 2026 Colin Percival
4#
5# SPDX-License-Identifier: BSD-2-Clause
6#
7# PROVIDE: growfs_postboot
8# KEYWORD: nostart
9
10# Triggered by /etc/devd/growfs_postboot.conf: When a disk grows, enlarge the
11# final partition to fill the available space (if the disk is partitioned) or
12# enlarge the UFS filesystem or ZFS zpool (if in use for one of those).  Note
13# that in the common case of "a disk is partitioned and one of the partitions
14# contains a filesystem" this script is invoked twice -- the repartitioning
15# done on the first call generates a new devd event which triggers the second
16# invocation.
17#
18# Enable by setting
19# growfs_postboot_enable=YES
20# in /etc/rc.conf.  Note that on systems with growfs_enable=YES, it will often
21# be necessary to set growfs_swap_size=0 in order to disable swap creation;
22# otherwise the "final partition" is the swap space, not the filesystem and
23# growing the disk will not cause the most likely desired results.
24
25. /etc/rc.subr
26
27name="growfs_postboot"
28desc="automatically grow file systems during runtime"
29rcvar="growfs_postboot_enable"
30start_cmd="growfs_postboot_start"
31
32disktype() {
33	sysctl -n kern.geom.conftxt | awk -v dev="$1" '
34	BEGIN {
35		partitioned=0
36		indev=0
37	}
38	{
39		if (dev == $3) {
40			indev=1
41			devlevel=$1
42		} else if (devlevel >= $1) {
43			indev=0
44		} else if (indev == 1) {
45			if ($2 == "PART") {
46				partitioned=1
47			}
48		}
49	}
50	END {
51		if (partitioned == 1) {
52			print "partitioned"
53		} else {
54			print "unpartitioned"
55		}
56	}'
57}
58
59growpart() {
60	# Run 'gpart recover' if GPT
61	if gpart list "$1" | grep -qi 'scheme: GPT'; then
62		gpart recover "$1" || true
63	fi
64
65	# Find the index of the last partition; note that we need to compare
66	# the starting sectors in case partitions are not numbered sensibly.
67	idx=$(gpart backup "$1" | awk '
68		NR == 1 { next }
69		$3 + 0 > start { start = $3 + 0; idx = $1 }
70		END { print idx }')
71	if [ -z "$idx" ]; then
72		echo "Error finding final partition of $1"
73		return 1
74	fi
75
76	# Expand that partition
77	gpart resize -i "$idx" "$1"
78}
79
80growfilesystem() {
81	case $(fstyp -u "/dev/$1" 2>/dev/null) in
82	ufs)	growfs -y "/dev/$1"
83		;;
84	zfs)	zpool list -H -o name |
85		    while read -r pool; do
86			zpool list -vH -o name "$pool" |
87			    grep "^[[:space:]]" |
88			    while read -r dev; do
89				if [ "$dev" = "$1" ]; then
90					zpool online -e "$pool" "$1"
91				fi
92			    done
93		    done
94		;;
95	"")	# Nothing to grow here
96		;;
97	*)	echo "Don't know how to grow filesystem on /dev/$1"
98		;;
99	esac
100}
101
102growfs_postboot_start() {
103	# Run either growpart or growfilesystem depending on whether this is
104	# something which is partitioned or not.
105	case $(disktype "$1") in
106	partitioned)
107		growpart "$1"
108		;;
109	unpartitioned)
110		growfilesystem "$1"
111		;;
112	esac
113}
114
115load_rc_config $name
116run_rc_command "$@"
117