mkimage revision 1.11
11.1Sagc#! /bin/sh 21.1Sagc 31.11Schristos# $NetBSD: mkimage,v 1.11 2013/01/15 21:04:41 christos Exp $ 41.1Sagc 51.1Sagc# Copyright (c) 2012 Alistair Crooks <agc@NetBSD.org> 61.1Sagc# All rights reserved. 71.1Sagc# 81.1Sagc# Redistribution and use in source and binary forms, with or without 91.1Sagc# modification, are permitted provided that the following conditions 101.1Sagc# are met: 111.1Sagc# 1. Redistributions of source code must retain the above copyright 121.1Sagc# notice, this list of conditions and the following disclaimer. 131.1Sagc# 2. Redistributions in binary form must reproduce the above copyright 141.1Sagc# notice, this list of conditions and the following disclaimer in the 151.1Sagc# documentation and/or other materials provided with the distribution. 161.1Sagc# 171.1Sagc# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 181.1Sagc# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 191.1Sagc# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 201.1Sagc# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 211.1Sagc# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 221.1Sagc# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 231.1Sagc# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 241.1Sagc# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 251.1Sagc# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 261.1Sagc# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 271.1Sagc# 281.1Sagc 291.1Sagc# find next available vnd, from kre 301.1Sagcnext_avail () 311.1Sagc{ 321.1Sagc local dev="$1" 331.1Sagc local N=$(( ${#dev} + 1 )) 341.1Sagc local unit units 351.1Sagc 361.1Sagc units=$( 371.1Sagc sysctl -n hw.disknames | 381.1Sagc tr ' ' '\012' | 391.1Sagc grep '^'"${dev}"'[0-9]' | 401.1Sagc sort -n -k 1.$N ) 411.1Sagc 421.1Sagc test -z "${units}" && { 431.1Sagc test -e "/dev/${dev}0a" || { 441.1Sagc echo >&2 "No ${dev}s available!" 451.1Sagc return 1 461.1Sagc } 471.1Sagc echo "${dev}0" 481.1Sagc return 491.1Sagc } 501.1Sagc 511.1Sagc N=0 521.1Sagc for unit in ${units} 531.1Sagc do 541.1Sagc if [ "${unit}" = "${dev}${N}" ] 551.1Sagc then 561.1Sagc N=$(( N + 1 )) 571.1Sagc else 581.1Sagc echo "${dev}${N}" 591.1Sagc return 601.1Sagc fi 611.1Sagc done 621.1Sagc 631.1Sagc test -e /dev/"${dev}${N}a" || { 641.1Sagc echo >&2 "All ${dev}s in use" 651.1Sagc return 1 661.1Sagc } 671.1Sagc 681.1Sagc echo "${dev}${N}" 691.1Sagc} 701.1Sagc 711.1Sagc# find the size of the gzipped files in a .tgz archive 721.11Schristos# Directories appear as 0, so count them as one block 731.11Schristos# and round up files to a fragment. 741.1Sagcsizeone() { 751.4Schristos if [ ! -f "$1" ] 761.4Schristos then 771.4Schristos echo "$PROG: Missing set $1" 1>&2 781.4Schristos echo 0 791.4Schristos return; 801.4Schristos fi 811.1Sagc case "$1" in 821.11Schristos *.tgz|*.tar.gz|*.tbz|*.tar.bz2|*.txz|*.tar.xz) 831.11Schristos tar tvzf "$1" | 841.11Schristos awk -v fsize=${fsize} -v bsize=${bsize} ' 851.11Schristos { 861.11Schristos if ($5 == 0) 871.11Schristos tot += bsize; 881.11Schristos else 891.11Schristos tot += ((int)(($5 + fsize - 1) / fsize)) * fsize; 901.11Schristos } 911.11Schristos END { 921.11Schristos printf("%d\n", tot); 931.11Schristos }' 941.1Sagc ;; 951.1Sagc *) 961.1Sagc echo 0 971.1Sagc ;; 981.1Sagc esac 991.1Sagc} 1001.1Sagc 1011.4Schristosusage() { 1021.4Schristos cat << EOF 1>&2 1031.4SchristosUsage: $PROG [-S <setsdir>] [-c <custom-files-dir>] [-h <host-arch>] [-s <size>] 1041.4SchristosEOF 1051.4Schristosexit 1 1061.4Schristos} 1071.4Schristos 1081.11Schristos# Return the usable filesystem size in bytes, given the total size in bytes, 1091.11Schristos# and optionally block and fragment sizes 1101.11Schristosgetffssize() { 1111.11Schristos local bytes="$1" 1121.11Schristos local barg 1131.11Schristos local farg 1141.11Schristos local overhead 1151.11Schristos 1161.11Schristos if [ -n "$2" ] 1171.11Schristos then 1181.11Schristos barg="-b $2" 1191.11Schristos if [ -n "$3" ] 1201.11Schristos then 1211.11Schristos farg="-f $3" 1221.11Schristos fi 1231.11Schristos fi 1241.11Schristos 1251.11Schristos overhead=$(newfs -N ${farg} ${barg} -s "${bytes}b" -F /dev/null | 1261.11Schristos awk '/using/ { 1271.11Schristos printf("%d\n", substr($6, 1, length($6) - 3) * 1024 * 1024); 1281.11Schristos }' 1291.11Schristos ) 1301.11Schristos echo $(( ${bytes} - ${overhead} )) 1311.11Schristos} 1321.11Schristos 1331.11Schristos# Compute the size of an ffs filesystem that can fit x bytes. 1341.11Schristos# Instead of duplicating the newfs calculations here we let 1351.11Schristos# it do the job, using binary search. 1361.11Schristosmakeffssize() { 1371.11Schristos local bytes=$1 1381.11Schristos local bsize=$2 1391.11Schristos local fsize=$3 1401.11Schristos local max=$(( 2 * ${bytes} )) 1411.11Schristos local min="${bytes}" 1421.11Schristos local cur 1431.11Schristos local res 1441.11Schristos while true; do 1451.11Schristos cur="$(( ( ${max} + ${min} ) / 2 ))" 1461.11Schristos res="$(getffssize "${cur}" ${bsize} ${fsize})" 1471.11Schristos# echo ${min} ${cur} ${max} ${res} ${bytes} 1>&2 1481.11Schristos if [ "${res}" -eq "${bytes}" ] 1491.11Schristos then 1501.11Schristos break 1511.11Schristos elif [ "$(( ${min} + 1 ))" -ge "${max}" ] 1521.11Schristos then 1531.11Schristos break 1541.11Schristos elif [ "${res}" -lt "${bytes}" ] 1551.11Schristos then 1561.11Schristos min="${cur}" 1571.11Schristos elif [ "${res}" -gt "${bytes}" ] 1581.11Schristos then 1591.11Schristos max="${cur}" 1601.11Schristos fi 1611.11Schristos done 1621.11Schristos echo "${cur}" 1631.11Schristos} 1641.11Schristos 1651.4Schristosfinish() { 1661.4Schristos cleanup 1671.4Schristos ${sudo} umount ${mnt} 1681.4Schristos ${sudo} vnconfig -u ${vnddev} 1691.4Schristos} 1701.4Schristos 1711.11Schristos 1721.4SchristosDIR="$(dirname "$0")" 1731.4SchristosPROG="$(basename "$0")" 1741.2Sagcbar="===" 1751.4Schristossudo= 1761.7Schristosmnt="${TMPDIR:-/tmp}/image.$$" 1771.4Schristossrc="/usr/src" 1781.4Schristosobj="/usr/obj" 1791.4Schristos 1801.11Schristos 1811.11Schristos# Presumable block and fragment size. 1821.11Schristosbsize=16384 1831.11Schristosfsize=2048 1841.11Schristos 1851.4Schristos# First pass for options to get the host 1861.4SchristosOPTS="S:c:h:s:x" 1871.4Schristoswhile getopts "$OPTS" f 1881.4Schristosdo 1891.4Schristos case $f in 1901.4Schristos h) h="$OPTARG";; 1911.4Schristos *) ;; 1921.4Schristos esac 1931.4Schristosdone 1941.4Schristos 1951.4Schristosif [ -z "$h" ] 1961.4Schristosthen 1971.4Schristos usage 1981.4Schristosfi 1991.4Schristos 2001.5Schristosif [ ! -f "${DIR}/conf/${h}.conf" ] 2011.4Schristosthen 2021.5Schristos echo $PROG: ${DIR}/conf/${h}.conf is not present 1>&2 2031.4Schristos exit 1 2041.4Schristosfi 2051.4Schristos 2061.5Schristos. "${DIR}/conf/${h}.conf" 2071.4Schristos 2081.4SchristosOPTIND=1 2091.4Schristoswhile getopts "$OPTS" f 2101.4Schristosdo 2111.4Schristos case $f in 2121.4Schristos S) setsdir="$OPTARG";; 2131.4Schristos c) custom="$OPTARG";; 2141.4Schristos h) ;; 2151.4Schristos s) size="$OPTARG";; 2161.4Schristos x) set -x;; 2171.6Schristos *) usage;; 2181.1Sagc esac 2191.1Sagcdone 2201.1Sagc 2211.8Sjmcneilltrap finish 0 1 2 3 15 2221.8Sjmcneill 2231.4Schristosshift $(( "$OPTIND" - 1 )) 2241.4Schristosif [ -n "$1" ]; then 2251.1Sagc # take the next argument as being the image name 2261.1Sagc image="$1" 2271.1Sagc shift 2281.1Sagcfi 2291.1Sagc 2301.11Schristos# calculate the set bytes 2311.11Schristossetbytes=0 2321.11Schristosecho -n "${bar} computing set sizes (" 2331.1Sagcfor s in ${sets}; do 2341.4Schristos one="$(sizeone ${setsdir}/${s}.tgz)" 2351.11Schristos echo -n " $s=$(( ${one} / 1024 / 1024 ))MB" 2361.11Schristos setbytes=$(( ${setbytes} + ${one} )) 2371.1Sagcdone 2381.11Schristosecho "): $(( ${setbytes} / 1024 / 1024 ))MB ${bar}" 2391.11Schristos 2401.2Sagc# calculate size of custom files 2411.11Schristoscustbytes=0 2421.2Sagcif [ -d "${custom}" ]; then 2431.11Schristos custbytes=$(ls -lR "${custom}" | 2441.11Schristos awk 'NF == 9 { tot += $5 } END { print tot }') 2451.2Sagcfi 2461.11Schristosecho "${bar} computing custom sizes: $(( ${custbytes} / 1024 / 1024 ))MB ${bar}" 2471.11Schristos 2481.11Schristos# how many bytes 2491.11Schristosrawbytes="$(( ${setbytes} + ${custbytes} ))" 2501.11Schristosecho -n "${bar} computing ffs filesystem size for $(( ${rawbytes} / 1024 / 1024 ))MB: " 2511.11Schristosffsbytes="$(makeffssize "${rawbytes}")" 2521.11Schristosffsmb=$(( ${ffsbytes} / 1024 / 1024 )) 2531.11Schristosecho " ${ffsmb}MB ${bar}" 2541.11Schristos 2551.11Schristos# total in MB 2561.11Schristostotal=$(( ${ffsmb} + ${overhead} )) 2571.11Schristosecho "${bar} overhead: ${overhead}MB ${bar}" 2581.11Schristos 2591.1Sagcif [ $size -eq 0 ]; then 2601.1Sagc # auto-size the pkgs fs 2611.10Schristos newsize=${total} 2621.1Sagcelse 2631.1Sagc # check that we've been given enough space 2641.1Sagc if [ ${total} -gt ${size} ]; then 2651.4Schristos echo "$PROG: Given size is ${size} MB, but we need ${total} MB" >&2 2661.1Sagc exit 1 2671.1Sagc fi 2681.10Schristos newsize=${size} 2691.1Sagcfi 2701.1Sagc 2711.10Schristosecho "${bar} making a new ${newsize} MB image in ${image} ${bar}" 2721.10Schristosdd if=/dev/zero of=${image} bs=1m count=${newsize} conv=sparse 2731.1Sagc 2741.1Sagcvnddev=$(next_avail vnd) 2751.1Sagcecho "${bar} mounting image via vnd ${vnddev} ${bar}" 2761.2Sagc${sudo} vnconfig ${vnddev} ${image} 2771.4Schristos${sudo} mkdir -p ${mnt} 2781.4Schristosmake_filesystems 2791.4Schristos 2801.4Schristos${sudo} mkdir -p ${mnt}/etc ${mnt}/dev 2811.1Sagc 2821.10Schristosecho -n "${bar} installing sets:" 2831.4Schristos(cd ${mnt} && 2841.1Sagc for s in ${sets}; do 2851.10Schristos ss="${setsdir}/${s}.tgz" 2861.10Schristos if [ -f "${ss}" ]; then 2871.10Schristos echo -n " ${s}" 2881.10Schristos ${sudo} tar xpzf "${ss}" 2891.4Schristos fi 2901.1Sagc done 2911.1Sagc) 2921.10Schristosecho " ${bar}" 2931.1Sagc 2941.1Sagcecho "${bar} performing customisations ${bar}" 2951.1Sagc 2961.4Schristosmake_fstab 2971.1Sagc 2981.4Schristos${sudo} cat > ${mnt}/etc/rc.conf << EOF 2991.1Sagc# 3001.1Sagc# see rc.conf(5) for more information. 3011.1Sagc# 3021.1Sagc# Use program=YES to enable program, NO to disable it. program_flags are 3031.1Sagc# passed to the program on the command line. 3041.1Sagc# 3051.1Sagc 3061.1Sagc# Load the defaults in from /etc/defaults/rc.conf (if it's readable). 3071.1Sagc# These can be overridden below. 3081.1Sagc# 3091.1Sagcif [ -r /etc/defaults/rc.conf ]; then 3101.1Sagc . /etc/defaults/rc.conf 3111.1Sagcfi 3121.1Sagc 3131.1Sagc# If this is not set to YES, the system will drop into single-user mode. 3141.1Sagc# 3151.1Sagcrc_configured=YES 3161.1Sagc 3171.4Schristoshostname=${h} 3181.1Sagc 3191.4SchristosEOF 3201.1Sagc 3211.4Schristoscustomize 3221.1Sagc 3231.1Sagcfor d in ${specialdirs}; do 3241.4Schristos ${sudo} mkdir -p ${mnt}/${d} 3251.1Sagcdone 3261.1Sagc 3271.4Schristosif [ \( -n "${custom}" \) -a \( -d "${custom}" \) ]; then 3281.2Sagc echo "${bar} user customisations from files in ${custom} ${bar}" 3291.4Schristos (cd ${custom} && ${sudo} pax -rwpe . ${mnt}) 3301.2Sagcfi 3311.1Sagc 3321.1Sagcexit 0 333