1#!/bin/sh 2# 3# Copyright 2022 Michael J. Karels 4# Copyright 2014 John-Mark Gurney 5# All rights reserved. 6# 7# Redistribution and use in source and binary forms, with or without 8# modification, are permitted provided that the following conditions 9# are met: 10# 1. Redistributions of source code must retain the above copyright 11# notice, this list of conditions and the following disclaimer. 12# 2. Redistributions in binary form must reproduce the above copyright 13# notice, this list of conditions and the following disclaimer in the 14# documentation and/or other materials provided with the distribution. 15# 16# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26# SUCH DAMAGE. 27# 28# 29 30# PROVIDE: growfs 31# REQUIRE: fsck 32# BEFORE: root 33# KEYWORD: firstboot 34 35# Grow root partition to fill available space, optionally adding a swap 36# partition at the end. This allows us to distribute an image and 37# have it work on essentially any size drive. 38 39# Note that this uses awk(1), and thus will not work if /usr is on a separate 40# filesystem. We need to run early, because there might be not enough free 41# space on rootfs for the boot to succeed, and on images we ship - which are 42# the primary purpose of this script - there is no separate /usr anyway. 43 44. /etc/rc.subr 45 46name="growfs" 47desc="Grow root partition to fill device" 48start_cmd="growfs_start" 49stop_cmd=":" 50rcvar="growfs_enable" 51 52growfs_get_diskdev() 53{ 54 local _search=${1} 55 sysctl -b kern.geom.conftxt | 56 while read x1 _type _dev line 57 do 58 if [ "${_type}" = "DISK" -a -n "$(echo ${_search} | grep ${_dev})" ]; then 59 echo -n ${_dev} 60 break 61 fi 62 done 63} 64 65# Compute upper bound on swap partition size (if added), based on physmem 66# and vm.swap_maxpages / 2 (the limit that elicits a warning). 67# Rule for swap size based on memory size: 68# up to 4 GB twice memory size 69# 4 GB - 8 GB 8 GB 70# over 8 GB memory size 71growfs_swap_max() 72{ 73 memsize=$(sysctl -n hw.physmem) 74 memsizeMB=$(($memsize / (1024 * 1024))) 75 76 if [ $memsizeMB -lt 4096 ] 77 then 78 swapmax=$(($memsize * 2)) 79 elif [ $memsizeMB -lt 8192 ] 80 then 81 swapmax=$((8192 * 1024 * 1024)) 82 else 83 swapmax=$memsize 84 fi 85 86 pagesize=$(sysctl -n hw.pagesize) 87 vm_swap_max=$(($(sysctl -n vm.swap_maxpages) / 2 * $pagesize)) 88 89 if [ $swapmax -gt $vm_swap_max ] 90 then 91 swapmax=$vm_swap_max 92 fi 93 echo -n "$swapmax" 94} 95 96# Find newly-added swap partition on parent device ($1). 97growfs_last_swap() 98{ 99 swapdev=$(gpart list $1 | awk ' 100 $2 == "Name:" { dev = $3 } 101 $1 == "type:" && $2 == "freebsd-swap" { swapdev = dev } 102 END { print swapdev } 103 ') 104 echo -n $swapdev 105} 106 107growfs_start() 108{ 109 verbose=0 110 echo "Growing root partition to fill device" 111 FSTYPE=$(mount -p | awk '{ if ( $2 == "/") { print $3 }}') 112 FSDEV=$(mount -p | awk '{ if ( $2 == "/") { print $1 }}') 113 case "$FSTYPE" in 114 ufs) 115 rootdev=${FSDEV#/dev/} 116 ;; 117 zfs) 118 pool=${FSDEV%%/*} 119 rootdev=$(zpool list -v $pool | awk 'END { print $1 }') 120 ;; 121 *) 122 echo "Don't know how to grow root filesystem type: $FSTYPE" 123 return 124 esac 125 if [ x"$rootdev" = x"${rootdev%/*}" ]; then 126 # raw device 127 rawdev="$rootdev" 128 else 129 rawdev=$(glabel status | awk -v rootdev=$rootdev 'index(rootdev, $1) { print $3; }') 130 if [ x"$rawdev" = x"" ]; then 131 echo "Can't figure out device for: $rootdev" 132 return 133 fi 134 fi 135 136 if [ x"diskid" = x"${rootdev%/*}" ]; then 137 search=$rootdev 138 else 139 search=$rawdev 140 fi 141 142 diskdev=$(growfs_get_diskdev ${search}) 143 if [ -z "${diskdev}" ]; then 144 diskdev=${rootdev} 145 fi 146 147 # Check kenv for growfs_swap_size; if not present, 148 # check $growfs_swap_size from /etc/rc.conf. 149 # A value of 0 suppresses swap addition, 150 # "" (or unset) specifies the default; 151 # other values indicate the size in bytes. 152 # If default, check whether swap is already in fstab; 153 # if so, don't add another. 154 addswap=1 155 swapsize="$(kenv -q growfs_swap_size 2>/dev/null)" 156 case "$swapsize" in 157 "0") addswap=0 158 ;; 159 "") case "$growfs_swap_size" in 160 "0") addswap=0 161 ;; 162 "") 163 if ! awk ' 164 /^#/ { next } 165 $3 == "swap" { exit 1 } 166 ' < /etc/fstab 167 then 168 addswap=0 169 fi 170 ;; 171 *) swapsize="$growfs_swap_size" 172 ;; 173 esac 174 ;; 175 *) ;; 176 esac 177 178 swaplim=$(growfs_swap_max) 179 180 [ $verbose -eq 1 ] && { 181 echo "diskdev is $diskdev" 182 echo "search is $search" 183 echo "swapsize is $swapsize" 184 echo "swaplim is $swaplim" 185 } 186 187 sysctl -b kern.geom.conftxt | awk ' 188{ 189 verbose = 0 190 lvl=$1 191 device[lvl] = $3 192 type[lvl] = $2 193 idx[lvl] = $7 194 offset[lvl] = $9 195 parttype[lvl] = $13 196 size[lvl] = $4 197 if (verbose) print lvl, type[lvl], $3 198 if (type[lvl] == "DISK") { 199 disksize = size[lvl] 200 if (verbose) 201 print "disksize ", disksize 202 # Do not add swap on disks under 15 GB (decimal) by default. 203 if (addswap == 1 && (size[lvl] > 15000000000 || swapsize > 0)) 204 doing_swap = 1 205 else 206 doing_swap = 0 207 } else if (type[lvl] == "PART" && $11 == "freebsd-swap" && \ 208 int(swapsize) == 0) { 209 # This finds swap only if it precedes root, e.g. preceding disk. 210 addswap = 0 211 doing_swap = 0 212 print "swap device exists, not adding swap" 213 } 214 if (dev == $3) { 215 for (i = 1; i <= lvl; i++) { 216 # resize 217 if (type[i] == "PART") { 218 pdev = device[i - 1] 219 if (verbose) 220 print i, pdev, addswap, disksize, \ 221 doing_swap 222 swapcmd = "" 223 # Allow swap if current root is < 40% of disk. 224 if (parttype[i] != "MBR" && doing_swap == 1 && \ 225 (size[i] / disksize < 0.4 || \ 226 swapsize > 0)) { 227 print "Adding swap partition" 228 if (int(swapsize) == 0) { 229 swapsize = int(disksize / 10) 230 if (swapsize > swaplim) 231 swapsize = swaplim 232 } 233 sector = $5 234 swapsize /= sector 235 if (verbose) 236 print "swapsize sectors", 237 swapsize 238 align = 4 * 1024 * 1024 / sector 239 240 # Estimate offset for swap; let 241 # gpart compute actual start and size. 242 # Assume expansion all goes into this 243 # partition for MBR case. 244 if (parttype[i - 1] == "MBR") { 245 if (verbose) 246 print "sz ", size[i - 1], \ 247 " off ", offset[i - 1] 248 expand = size[0] - \ 249 (size[i - 1] + offset[i - 1]) 250 } else { 251 if (verbose) 252 print "sz ", size[i], \ 253 " off ", offset[i] 254 expand = size[0] - \ 255 (size[i] + offset[i]) 256 } 257 if (verbose) 258 print "expand ", expand, \ 259 " sz ", size[i] 260 swapbase = (expand + size[i]) / sector 261 swapbase -= swapsize + align 262 swapcmd = "gpart add -t freebsd-swap -a " align " -b " int(swapbase) " " pdev " && kenv growfs_swap_pdev=" pdev " >/dev/null; " 263 if (verbose) 264 swapcmd = "set -x; gpart show; " swapcmd 265 } 266 cmd[i] = swapcmd "gpart resize -i " idx[i] " " pdev 267 if (parttype[i] == "GPT") 268 cmd[i] = "gpart recover " pdev " ; " cmd[i] 269 } else if (type[i] == "LABEL") { 270 continue 271 } else { 272 print "unhandled type: " type[i] 273 exit 1 274 } 275 } 276 for (i = 1; i <= lvl; i++) { 277 if (cmd[i]) 278 system(cmd[i]) 279 } 280 exit 0 281 } 282}' dev="$search" addswap="$addswap" swapsize="$swapsize" swaplim="$swaplim" 283 gpart commit "$diskdev" 2> /dev/null 284 case "$FSTYPE" in 285 ufs) 286 growfs -y /dev/"$rootdev" 287 ;; 288 zfs) 289 zpool online -e $pool $rootdev 290 ;; 291 esac 292 293 # Get parent device of swap partition if one was added; 294 # if so, find swap device and label it. 295 pdev=$(kenv -q growfs_swap_pdev) 296 if [ -n "$pdev" ] 297 then 298 dev=$(growfs_last_swap "$pdev") 299 if [ -z "$dev" ] 300 then 301 echo "Swap partition not found on $pdev" 302 exit 0 303 fi 304 glabel label -v growfs_swap $dev 305 fi 306} 307 308load_rc_config $name 309run_rc_command "$1" 310