10696600cSBjoern A. Zeeb#!/bin/sh 20696600cSBjoern A. Zeeb# 34a30d7bbSMike Karels# Copyright 2022 Michael J. Karels 40696600cSBjoern A. Zeeb# Copyright 2014 John-Mark Gurney 50696600cSBjoern A. Zeeb# All rights reserved. 60696600cSBjoern A. Zeeb# 70696600cSBjoern A. Zeeb# Redistribution and use in source and binary forms, with or without 80696600cSBjoern A. Zeeb# modification, are permitted provided that the following conditions 90696600cSBjoern A. Zeeb# are met: 100696600cSBjoern A. Zeeb# 1. Redistributions of source code must retain the above copyright 110696600cSBjoern A. Zeeb# notice, this list of conditions and the following disclaimer. 120696600cSBjoern A. Zeeb# 2. Redistributions in binary form must reproduce the above copyright 130696600cSBjoern A. Zeeb# notice, this list of conditions and the following disclaimer in the 140696600cSBjoern A. Zeeb# documentation and/or other materials provided with the distribution. 150696600cSBjoern A. Zeeb# 160696600cSBjoern A. Zeeb# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 170696600cSBjoern A. Zeeb# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 180696600cSBjoern A. Zeeb# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 190696600cSBjoern A. Zeeb# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 200696600cSBjoern A. Zeeb# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 210696600cSBjoern A. Zeeb# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 220696600cSBjoern A. Zeeb# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 230696600cSBjoern A. Zeeb# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 240696600cSBjoern A. Zeeb# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 250696600cSBjoern A. Zeeb# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 260696600cSBjoern A. Zeeb# SUCH DAMAGE. 270696600cSBjoern A. Zeeb# 280696600cSBjoern A. Zeeb# $FreeBSD$ 290696600cSBjoern A. Zeeb# 300696600cSBjoern A. Zeeb 310696600cSBjoern A. Zeeb# PROVIDE: growfs 32b68e6569SEdward Tomasz Napierala# REQUIRE: fsck 33b68e6569SEdward Tomasz Napierala# BEFORE: root 340696600cSBjoern A. Zeeb# KEYWORD: firstboot 350696600cSBjoern A. Zeeb 364a30d7bbSMike Karels# Grow root partition to fill available space, optionally adding a swap 374a30d7bbSMike Karels# partition at the end. This allows us to distribute an image and 384a30d7bbSMike Karels# have it work on essentially any size drive. 39b68e6569SEdward Tomasz Napierala 40b68e6569SEdward Tomasz Napierala# Note that this uses awk(1), and thus will not work if /usr is on a separate 41b68e6569SEdward Tomasz Napierala# filesystem. We need to run early, because there might be not enough free 42b68e6569SEdward Tomasz Napierala# space on rootfs for the boot to succeed, and on images we ship - which are 43b68e6569SEdward Tomasz Napierala# the primary purpose of this script - there is no separate /usr anyway. 440696600cSBjoern A. Zeeb 450696600cSBjoern A. Zeeb. /etc/rc.subr 460696600cSBjoern A. Zeeb 470696600cSBjoern A. Zeebname="growfs" 480696600cSBjoern A. Zeebdesc="Grow root partition to fill device" 490696600cSBjoern A. Zeebstart_cmd="growfs_start" 500696600cSBjoern A. Zeebstop_cmd=":" 510696600cSBjoern A. Zeebrcvar="growfs_enable" 520696600cSBjoern A. Zeeb 5304500107SScott Longgrowfs_get_diskdev() 5404500107SScott Long{ 5504500107SScott Long local _search=${1} 5604500107SScott Long sysctl -b kern.geom.conftxt | 5704500107SScott Long while read x1 _type _dev line 5804500107SScott Long do 5904500107SScott Long if [ "${_type}" = "DISK" -a -n "$(echo ${_search} | grep ${_dev})" ]; then 6004500107SScott Long echo -n ${_dev} 6104500107SScott Long break 6204500107SScott Long fi 6304500107SScott Long done 6404500107SScott Long} 6504500107SScott Long 664a30d7bbSMike Karels# Compute upper bound on swap partition size (if added), based on physmem 674a30d7bbSMike Karels# and vm.swap_maxpages / 2 (the limit that elicits a warning). 684a30d7bbSMike Karels# Rule for swap size based on memory size: 694a30d7bbSMike Karels# up to 4 GB twice memory size 704a30d7bbSMike Karels# 4 GB - 8 GB 8 GB 714a30d7bbSMike Karels# over 8 GB memory size 724a30d7bbSMike Karelsgrowfs_swap_max() 734a30d7bbSMike Karels{ 744a30d7bbSMike Karels memsize=$(sysctl -n hw.physmem) 754a30d7bbSMike Karels memsizeMB=$(($memsize / (1024 * 1024))) 764a30d7bbSMike Karels 774a30d7bbSMike Karels if [ $memsizeMB -lt 4096 ] 784a30d7bbSMike Karels then 794a30d7bbSMike Karels swapmax=$(($memsize * 2)) 804a30d7bbSMike Karels elif [ $memsizeMB -lt 8192 ] 814a30d7bbSMike Karels then 824a30d7bbSMike Karels swapmax=$((8192 * 1024 * 1024)) 834a30d7bbSMike Karels else 844a30d7bbSMike Karels swapmax=$memsize 854a30d7bbSMike Karels fi 864a30d7bbSMike Karels 874a30d7bbSMike Karels pagesize=$(sysctl -n hw.pagesize) 884a30d7bbSMike Karels vm_swap_max=$(($(sysctl -n vm.swap_maxpages) / 2 * $pagesize)) 894a30d7bbSMike Karels 904a30d7bbSMike Karels if [ $swapmax -gt $vm_swap_max ] 914a30d7bbSMike Karels then 924a30d7bbSMike Karels $swapmax=$vm_swap_max 934a30d7bbSMike Karels fi 944a30d7bbSMike Karels echo -n "$swapmax" 954a30d7bbSMike Karels} 964a30d7bbSMike Karels 974a30d7bbSMike Karels# Find newly-added swap partition on parent device ($1). 984a30d7bbSMike Karelsgrowfs_last_swap() 994a30d7bbSMike Karels{ 1004a30d7bbSMike Karels swapdev=$(gpart list $1 | awk ' 1014a30d7bbSMike Karels $2 == "Name:" { dev = $3 } 1024a30d7bbSMike Karels $1 == "type:" && $2 == "freebsd-swap" { swapdev = dev } 1034a30d7bbSMike Karels END { print swapdev } 1044a30d7bbSMike Karels ') 1054a30d7bbSMike Karels echo -n $swapdev 1064a30d7bbSMike Karels} 1074a30d7bbSMike Karels 1080696600cSBjoern A. Zeebgrowfs_start() 1090696600cSBjoern A. Zeeb{ 1104a30d7bbSMike Karels verbose=0 1110696600cSBjoern A. Zeeb echo "Growing root partition to fill device" 112efed7c6dSColin Percival FSTYPE=$(mount -p | awk '{ if ( $2 == "/") { print $3 }}') 113efed7c6dSColin Percival FSDEV=$(mount -p | awk '{ if ( $2 == "/") { print $1 }}') 114efed7c6dSColin Percival case "$FSTYPE" in 115efed7c6dSColin Percival ufs) 116efed7c6dSColin Percival rootdev=${FSDEV#/dev/} 117efed7c6dSColin Percival ;; 118efed7c6dSColin Percival zfs) 119efed7c6dSColin Percival pool=${FSDEV%%/*} 120c65b552fSKurt Lidl rootdev=$(zpool list -v $pool | awk 'END { print $1 }') 121efed7c6dSColin Percival ;; 122efed7c6dSColin Percival *) 123efed7c6dSColin Percival echo "Don't know how to grow root filesystem type: $FSTYPE" 124efed7c6dSColin Percival return 125efed7c6dSColin Percival esac 1260696600cSBjoern A. Zeeb if [ x"$rootdev" = x"${rootdev%/*}" ]; then 1270696600cSBjoern A. Zeeb # raw device 1280696600cSBjoern A. Zeeb rawdev="$rootdev" 1290696600cSBjoern A. Zeeb else 13004500107SScott Long rawdev=$(glabel status | awk -v rootdev=$rootdev 'index(rootdev, $1) { print $3; }') 1310696600cSBjoern A. Zeeb if [ x"$rawdev" = x"" ]; then 1320696600cSBjoern A. Zeeb echo "Can't figure out device for: $rootdev" 1330696600cSBjoern A. Zeeb return 1340696600cSBjoern A. Zeeb fi 1350696600cSBjoern A. Zeeb fi 1360696600cSBjoern A. Zeeb 13704500107SScott Long if [ x"diskid" = x"${rootdev%/*}" ]; then 13804500107SScott Long search=$rootdev 13904500107SScott Long else 14004500107SScott Long search=$rawdev 14104500107SScott Long fi 14204500107SScott Long 14304500107SScott Long diskdev=$(growfs_get_diskdev ${search}) 14404500107SScott Long if [ -z "${diskdev}" ]; then 14504500107SScott Long diskdev=${rootdev} 14604500107SScott Long fi 14704500107SScott Long 1484a30d7bbSMike Karels # Check kenv for growfs_swap_size; if not present, 1494a30d7bbSMike Karels # check $growfs_swap_size from /etc/rc.conf. 1504a30d7bbSMike Karels # A value of 0 suppresses swap addition, 1514a30d7bbSMike Karels # "" (or unset) specifies the default; 1524a30d7bbSMike Karels # other values indicate the size in bytes. 1534a30d7bbSMike Karels # If default, check whether swap is already in fstab; 1544a30d7bbSMike Karels # if so, don't add another. 1554a30d7bbSMike Karels addswap=1 1564a30d7bbSMike Karels swapsize="$(kenv -q growfs_swap_size 2>/dev/null)" 1574a30d7bbSMike Karels case "$swapsize" in 1584a30d7bbSMike Karels "0") addswap=0 1594a30d7bbSMike Karels ;; 1604a30d7bbSMike Karels "") case "$growfs_swap_size" in 1614a30d7bbSMike Karels "0") addswap=0 1624a30d7bbSMike Karels ;; 1634a30d7bbSMike Karels "") 1644a30d7bbSMike Karels if ! awk ' 1654a30d7bbSMike Karels /^#/ { next } 1664a30d7bbSMike Karels $3 == "swap" { exit 1 } 1674a30d7bbSMike Karels ' < /etc/fstab 1684a30d7bbSMike Karels then 1694a30d7bbSMike Karels addswap=0 1704a30d7bbSMike Karels fi 1714a30d7bbSMike Karels ;; 1724a30d7bbSMike Karels *) swapsize="$growfs_swap_size" 1734a30d7bbSMike Karels ;; 1744a30d7bbSMike Karels esac 1754a30d7bbSMike Karels ;; 1764a30d7bbSMike Karels *) ;; 1774a30d7bbSMike Karels esac 1784a30d7bbSMike Karels 1794a30d7bbSMike Karels swaplim=$(growfs_swap_max) 1804a30d7bbSMike Karels 1814a30d7bbSMike Karels [ $verbose -eq 1 ] && { 1824a30d7bbSMike Karels echo "diskdev is $diskdev" 1834a30d7bbSMike Karels echo "search is $search" 1844a30d7bbSMike Karels echo "swapsize is $swapsize" 1854a30d7bbSMike Karels echo "swaplim is $swaplim" 1864a30d7bbSMike Karels } 1874a30d7bbSMike Karels 1880696600cSBjoern A. Zeeb sysctl -b kern.geom.conftxt | awk ' 1890696600cSBjoern A. Zeeb{ 1904a30d7bbSMike Karels verbose = 0 1910696600cSBjoern A. Zeeb lvl=$1 1920696600cSBjoern A. Zeeb device[lvl] = $3 1930696600cSBjoern A. Zeeb type[lvl] = $2 1940696600cSBjoern A. Zeeb idx[lvl] = $7 1954a30d7bbSMike Karels offset[lvl] = $9 1960696600cSBjoern A. Zeeb parttype[lvl] = $13 1974a30d7bbSMike Karels size[lvl] = $4 1984a30d7bbSMike Karels if (verbose) print lvl, type[lvl], $3 1994a30d7bbSMike Karels if (type[lvl] == "DISK") { 2004a30d7bbSMike Karels disksize = size[lvl] 2014a30d7bbSMike Karels if (verbose) 2024a30d7bbSMike Karels print "disksize ", disksize 203*4c8a2578SMike Karels # Do not add swap on disks under 15 GB (decimal) by default. 2044a30d7bbSMike Karels if (addswap == 1 && (size[lvl] > 15000000000 || swapsize > 0)) 2054a30d7bbSMike Karels doing_swap = 1 2064a30d7bbSMike Karels else 2074a30d7bbSMike Karels doing_swap = 0 2084a30d7bbSMike Karels } else if (type[lvl] == "PART" && $11 == "freebsd-swap" && \ 2094a30d7bbSMike Karels int(swapsize) == 0) { 2104a30d7bbSMike Karels # This finds swap only if it precedes root, e.g. preceding disk. 2114a30d7bbSMike Karels addswap = 0 2124a30d7bbSMike Karels doing_swap = 0 2134a30d7bbSMike Karels print "swap device exists, not adding swap" 2144a30d7bbSMike Karels } 2150696600cSBjoern A. Zeeb if (dev == $3) { 2160696600cSBjoern A. Zeeb for (i = 1; i <= lvl; i++) { 2170696600cSBjoern A. Zeeb # resize 2180696600cSBjoern A. Zeeb if (type[i] == "PART") { 2190696600cSBjoern A. Zeeb pdev = device[i - 1] 2204a30d7bbSMike Karels if (verbose) 2214a30d7bbSMike Karels print i, pdev, addswap, disksize, \ 2224a30d7bbSMike Karels doing_swap 2234a30d7bbSMike Karels swapcmd = "" 2244a30d7bbSMike Karels # Allow swap if current root is < 40% of disk. 2254a30d7bbSMike Karels if (parttype[i] != "MBR" && doing_swap == 1 && \ 2264a30d7bbSMike Karels (size[i] / disksize < 0.4 || \ 2274a30d7bbSMike Karels swapsize > 0)) { 2284a30d7bbSMike Karels print "Adding swap partition" 2294a30d7bbSMike Karels if (int(swapsize) == 0) { 2304a30d7bbSMike Karels swapsize = int(disksize / 10) 2314a30d7bbSMike Karels if (swapsize > swaplim) 2324a30d7bbSMike Karels swapsize = swaplim 2334a30d7bbSMike Karels } 2344a30d7bbSMike Karels sector = $5 2354a30d7bbSMike Karels swapsize /= sector 2364a30d7bbSMike Karels if (verbose) 2374a30d7bbSMike Karels print "swapsize sectors", 2384a30d7bbSMike Karels swapsize 2394a30d7bbSMike Karels align = 4 * 1024 * 1024 / sector 2404a30d7bbSMike Karels 2414a30d7bbSMike Karels # Estimate offset for swap; let 2424a30d7bbSMike Karels # gpart compute actual start and size. 2434a30d7bbSMike Karels # Assume expansion all goes into this 2444a30d7bbSMike Karels # partition for MBR case. 2454a30d7bbSMike Karels if (parttype[i - 1] == "MBR") { 2464a30d7bbSMike Karels if (verbose) 2474a30d7bbSMike Karels print "sz ", size[i - 1], \ 2484a30d7bbSMike Karels " off ", offset[i - 1] 2494a30d7bbSMike Karels expand = size[0] - \ 2504a30d7bbSMike Karels (size[i - 1] + offset[i - 1]) 2514a30d7bbSMike Karels } else { 2524a30d7bbSMike Karels if (verbose) 2534a30d7bbSMike Karels print "sz ", size[i], \ 2544a30d7bbSMike Karels " off ", offset[i] 2554a30d7bbSMike Karels expand = size[0] - \ 2564a30d7bbSMike Karels (size[i] + offset[i]) 2574a30d7bbSMike Karels } 2584a30d7bbSMike Karels if (verbose) 2594a30d7bbSMike Karels print "expand ", expand, \ 2604a30d7bbSMike Karels " sz ", size[i] 2614a30d7bbSMike Karels swapbase = (expand + size[i]) / sector 2624a30d7bbSMike Karels swapbase -= swapsize + align 2634a30d7bbSMike Karels swapcmd = "gpart add -t freebsd-swap -a " align " -b " swapbase " " pdev "; kenv growfs_swap_pdev=" pdev " >/dev/null; " 2644a30d7bbSMike Karels if (verbose) 2654a30d7bbSMike Karels swapcmd = "set -x; " swapcmd 2664a30d7bbSMike Karels } 2674a30d7bbSMike Karels cmd[i] = swapcmd "gpart resize -i " idx[i] " " pdev 2680696600cSBjoern A. Zeeb if (parttype[i] == "GPT") 2690696600cSBjoern A. Zeeb cmd[i] = "gpart recover " pdev " ; " cmd[i] 2700696600cSBjoern A. Zeeb } else if (type[i] == "LABEL") { 2710696600cSBjoern A. Zeeb continue 2720696600cSBjoern A. Zeeb } else { 2730696600cSBjoern A. Zeeb print "unhandled type: " type[i] 2740696600cSBjoern A. Zeeb exit 1 2750696600cSBjoern A. Zeeb } 2760696600cSBjoern A. Zeeb } 2770696600cSBjoern A. Zeeb for (i = 1; i <= lvl; i++) { 2780696600cSBjoern A. Zeeb if (cmd[i]) 2790696600cSBjoern A. Zeeb system(cmd[i]) 2800696600cSBjoern A. Zeeb } 2810696600cSBjoern A. Zeeb exit 0 2820696600cSBjoern A. Zeeb } 2834a30d7bbSMike Karels}' dev="$search" addswap="$addswap" swapsize="$swapsize" swaplim="$swaplim" 28404500107SScott Long gpart commit "$diskdev" 2> /dev/null 285efed7c6dSColin Percival case "$FSTYPE" in 286efed7c6dSColin Percival ufs) 2870696600cSBjoern A. Zeeb growfs -y /dev/"$rootdev" 288efed7c6dSColin Percival ;; 289efed7c6dSColin Percival zfs) 290efed7c6dSColin Percival zpool online -e $pool $rootdev 291efed7c6dSColin Percival ;; 292efed7c6dSColin Percival esac 2934a30d7bbSMike Karels 2944a30d7bbSMike Karels # Get parent device of swap partition if one was added; 2954a30d7bbSMike Karels # if so, find swap device and label it. 2964a30d7bbSMike Karels pdev=$(kenv -q growfs_swap_pdev) 2974a30d7bbSMike Karels if [ -n "$pdev" ] 2984a30d7bbSMike Karels then 2994a30d7bbSMike Karels dev=$(growfs_last_swap "$pdev") 3004a30d7bbSMike Karels if [ -z "$dev" ] 3014a30d7bbSMike Karels then 3024a30d7bbSMike Karels echo "Swap partition not found on $pdev" 3034a30d7bbSMike Karels exit 0 3044a30d7bbSMike Karels fi 3054a30d7bbSMike Karels glabel label -v growfs_swap $dev 3064a30d7bbSMike Karels fi 3070696600cSBjoern A. Zeeb} 3080696600cSBjoern A. Zeeb 3090696600cSBjoern A. Zeebload_rc_config $name 3100696600cSBjoern A. Zeebrun_rc_command "$1" 311