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