mkimage revision 1.12
11.1Sagc#! /bin/sh 21.1Sagc 31.12Schristos# $NetBSD: mkimage,v 1.12 2013/01/16 15:58:19 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.12Schristosusage() { 721.12Schristos cat << EOF 1>&2 731.12SchristosUsage: $PROG [-S <setsdir>] [-c <custom-files-dir>] [-h <host-arch>] [-s <size>] 741.12SchristosEOF 751.12Schristosexit 1 761.12Schristos} 771.12Schristos 781.12Schristos# Return the filesystem size for an ls -l or tar -xvf list 791.12Schristos# Directories and symlinks in tar are 0 size, we assume one block 801.12Schristos# (which is too much), we round up by the fragment size the rest. 811.12Schristosgetfssize() { 821.12Schristos local bsize="$1" 831.12Schristos local fsize="$2" 841.12Schristos 851.12Schristos awk -v fsize=${fsize} -v bsize=${bsize} ' 861.12Schristos NF >= 9 && $1 != "tar:" { 871.12Schristos if ($5 == 0) 881.12Schristos tot += bsize; 891.12Schristos else 901.12Schristos tot += ((int)(($5 + fsize - 1) / fsize)) * fsize; 911.12Schristos } 921.12Schristos END { 931.12Schristos printf("%d\n", tot); 941.12Schristos }' 951.12Schristos} 961.12Schristos 971.1Sagc# find the size of the gzipped files in a .tgz archive 981.11Schristos# Directories appear as 0, so count them as one block 991.11Schristos# and round up files to a fragment. 1001.1Sagcsizeone() { 1011.4Schristos if [ ! -f "$1" ] 1021.4Schristos then 1031.4Schristos echo "$PROG: Missing set $1" 1>&2 1041.4Schristos echo 0 1051.4Schristos return; 1061.4Schristos fi 1071.12Schristos 1081.1Sagc case "$1" in 1091.11Schristos *.tgz|*.tar.gz|*.tbz|*.tar.bz2|*.txz|*.tar.xz) 1101.12Schristos tar tvzf "$1" | getfssize ${bsize} ${fsize} 1111.1Sagc ;; 1121.1Sagc *) 1131.1Sagc echo 0 1141.1Sagc ;; 1151.1Sagc esac 1161.1Sagc} 1171.1Sagc 1181.4Schristos 1191.11Schristos# Return the usable filesystem size in bytes, given the total size in bytes, 1201.11Schristos# and optionally block and fragment sizes 1211.11Schristosgetffssize() { 1221.11Schristos local bytes="$1" 1231.11Schristos local barg 1241.11Schristos local farg 1251.11Schristos local overhead 1261.11Schristos 1271.11Schristos if [ -n "$2" ] 1281.11Schristos then 1291.11Schristos barg="-b $2" 1301.11Schristos if [ -n "$3" ] 1311.11Schristos then 1321.11Schristos farg="-f $3" 1331.11Schristos fi 1341.11Schristos fi 1351.11Schristos 1361.11Schristos overhead=$(newfs -N ${farg} ${barg} -s "${bytes}b" -F /dev/null | 1371.11Schristos awk '/using/ { 1381.11Schristos printf("%d\n", substr($6, 1, length($6) - 3) * 1024 * 1024); 1391.11Schristos }' 1401.11Schristos ) 1411.11Schristos echo $(( ${bytes} - ${overhead} )) 1421.11Schristos} 1431.11Schristos 1441.11Schristos# Compute the size of an ffs filesystem that can fit x bytes. 1451.11Schristos# Instead of duplicating the newfs calculations here we let 1461.11Schristos# it do the job, using binary search. 1471.11Schristosmakeffssize() { 1481.11Schristos local bytes=$1 1491.11Schristos local bsize=$2 1501.11Schristos local fsize=$3 1511.11Schristos local max=$(( 2 * ${bytes} )) 1521.11Schristos local min="${bytes}" 1531.11Schristos local cur 1541.11Schristos local res 1551.11Schristos while true; do 1561.11Schristos cur="$(( ( ${max} + ${min} ) / 2 ))" 1571.11Schristos res="$(getffssize "${cur}" ${bsize} ${fsize})" 1581.11Schristos# echo ${min} ${cur} ${max} ${res} ${bytes} 1>&2 1591.11Schristos if [ "${res}" -eq "${bytes}" ] 1601.11Schristos then 1611.11Schristos break 1621.11Schristos elif [ "$(( ${min} + 1 ))" -ge "${max}" ] 1631.11Schristos then 1641.11Schristos break 1651.11Schristos elif [ "${res}" -lt "${bytes}" ] 1661.11Schristos then 1671.11Schristos min="${cur}" 1681.11Schristos elif [ "${res}" -gt "${bytes}" ] 1691.11Schristos then 1701.11Schristos max="${cur}" 1711.11Schristos fi 1721.11Schristos done 1731.11Schristos echo "${cur}" 1741.11Schristos} 1751.11Schristos 1761.4Schristosfinish() { 1771.4Schristos cleanup 1781.4Schristos ${sudo} umount ${mnt} 1791.4Schristos ${sudo} vnconfig -u ${vnddev} 1801.4Schristos} 1811.4Schristos 1821.11Schristos 1831.4SchristosDIR="$(dirname "$0")" 1841.4SchristosPROG="$(basename "$0")" 1851.2Sagcbar="===" 1861.4Schristossudo= 1871.7Schristosmnt="${TMPDIR:-/tmp}/image.$$" 1881.4Schristossrc="/usr/src" 1891.4Schristosobj="/usr/obj" 1901.4Schristos 1911.11Schristos 1921.11Schristos# Presumable block and fragment size. 1931.11Schristosbsize=16384 1941.11Schristosfsize=2048 1951.12Schristosmtob=$(( 1024 * 1024 )) 1961.11Schristos 1971.4Schristos# First pass for options to get the host 1981.4SchristosOPTS="S:c:h:s:x" 1991.4Schristoswhile getopts "$OPTS" f 2001.4Schristosdo 2011.4Schristos case $f in 2021.4Schristos h) h="$OPTARG";; 2031.4Schristos *) ;; 2041.4Schristos esac 2051.4Schristosdone 2061.4Schristos 2071.4Schristosif [ -z "$h" ] 2081.4Schristosthen 2091.4Schristos usage 2101.4Schristosfi 2111.4Schristos 2121.5Schristosif [ ! -f "${DIR}/conf/${h}.conf" ] 2131.4Schristosthen 2141.5Schristos echo $PROG: ${DIR}/conf/${h}.conf is not present 1>&2 2151.4Schristos exit 1 2161.4Schristosfi 2171.4Schristos 2181.5Schristos. "${DIR}/conf/${h}.conf" 2191.4Schristos 2201.4SchristosOPTIND=1 2211.4Schristoswhile getopts "$OPTS" f 2221.4Schristosdo 2231.4Schristos case $f in 2241.4Schristos S) setsdir="$OPTARG";; 2251.4Schristos c) custom="$OPTARG";; 2261.4Schristos h) ;; 2271.4Schristos s) size="$OPTARG";; 2281.4Schristos x) set -x;; 2291.6Schristos *) usage;; 2301.1Sagc esac 2311.1Sagcdone 2321.1Sagc 2331.8Sjmcneilltrap finish 0 1 2 3 15 2341.8Sjmcneill 2351.4Schristosshift $(( "$OPTIND" - 1 )) 2361.4Schristosif [ -n "$1" ]; then 2371.1Sagc # take the next argument as being the image name 2381.1Sagc image="$1" 2391.1Sagc shift 2401.1Sagcfi 2411.1Sagc 2421.11Schristos# calculate the set bytes 2431.11Schristossetbytes=0 2441.11Schristosecho -n "${bar} computing set sizes (" 2451.1Sagcfor s in ${sets}; do 2461.4Schristos one="$(sizeone ${setsdir}/${s}.tgz)" 2471.12Schristos echo -n " $s=$(( ${one} / ${mtob} ))MB" 2481.11Schristos setbytes=$(( ${setbytes} + ${one} )) 2491.1Sagcdone 2501.12Schristosecho "): $(( ${setbytes} / ${mtob} ))MB ${bar}" 2511.11Schristos 2521.2Sagc# calculate size of custom files 2531.11Schristoscustbytes=0 2541.2Sagcif [ -d "${custom}" ]; then 2551.12Schristos custbytes=$(ls -lR "${custom}" | getfssize ${bsize} ${fsize}) 2561.2Sagcfi 2571.12Schristosecho "${bar} computing custom sizes: $(( ${custbytes} / ${mtob} ))MB ${bar}" 2581.11Schristos 2591.11Schristos# how many bytes 2601.11Schristosrawbytes="$(( ${setbytes} + ${custbytes} ))" 2611.12Schristosecho -n "${bar} computing ffs filesystem size for $(( ${rawbytes} / ${mtob} ))MB: " 2621.11Schristosffsbytes="$(makeffssize "${rawbytes}")" 2631.12Schristosffsmb=$(( ${ffsbytes} / ${mtob} )) 2641.11Schristosecho " ${ffsmb}MB ${bar}" 2651.11Schristos 2661.11Schristos# total in MB 2671.11Schristostotal=$(( ${ffsmb} + ${overhead} )) 2681.11Schristosecho "${bar} overhead: ${overhead}MB ${bar}" 2691.11Schristos 2701.1Sagcif [ $size -eq 0 ]; then 2711.1Sagc # auto-size the pkgs fs 2721.10Schristos newsize=${total} 2731.1Sagcelse 2741.1Sagc # check that we've been given enough space 2751.1Sagc if [ ${total} -gt ${size} ]; then 2761.4Schristos echo "$PROG: Given size is ${size} MB, but we need ${total} MB" >&2 2771.1Sagc exit 1 2781.1Sagc fi 2791.10Schristos newsize=${size} 2801.1Sagcfi 2811.1Sagc 2821.10Schristosecho "${bar} making a new ${newsize} MB image in ${image} ${bar}" 2831.10Schristosdd if=/dev/zero of=${image} bs=1m count=${newsize} conv=sparse 2841.1Sagc 2851.1Sagcvnddev=$(next_avail vnd) 2861.1Sagcecho "${bar} mounting image via vnd ${vnddev} ${bar}" 2871.2Sagc${sudo} vnconfig ${vnddev} ${image} 2881.4Schristos${sudo} mkdir -p ${mnt} 2891.4Schristosmake_filesystems 2901.4Schristos 2911.4Schristos${sudo} mkdir -p ${mnt}/etc ${mnt}/dev 2921.1Sagc 2931.10Schristosecho -n "${bar} installing sets:" 2941.4Schristos(cd ${mnt} && 2951.1Sagc for s in ${sets}; do 2961.10Schristos ss="${setsdir}/${s}.tgz" 2971.10Schristos if [ -f "${ss}" ]; then 2981.10Schristos echo -n " ${s}" 2991.10Schristos ${sudo} tar xpzf "${ss}" 3001.4Schristos fi 3011.1Sagc done 3021.1Sagc) 3031.10Schristosecho " ${bar}" 3041.1Sagc 3051.1Sagcecho "${bar} performing customisations ${bar}" 3061.1Sagc 3071.4Schristosmake_fstab 3081.1Sagc 3091.4Schristos${sudo} cat > ${mnt}/etc/rc.conf << EOF 3101.1Sagc# 3111.1Sagc# see rc.conf(5) for more information. 3121.1Sagc# 3131.1Sagc# Use program=YES to enable program, NO to disable it. program_flags are 3141.1Sagc# passed to the program on the command line. 3151.1Sagc# 3161.1Sagc 3171.1Sagc# Load the defaults in from /etc/defaults/rc.conf (if it's readable). 3181.1Sagc# These can be overridden below. 3191.1Sagc# 3201.1Sagcif [ -r /etc/defaults/rc.conf ]; then 3211.1Sagc . /etc/defaults/rc.conf 3221.1Sagcfi 3231.1Sagc 3241.1Sagc# If this is not set to YES, the system will drop into single-user mode. 3251.1Sagc# 3261.1Sagcrc_configured=YES 3271.1Sagc 3281.4Schristoshostname=${h} 3291.1Sagc 3301.4SchristosEOF 3311.1Sagc 3321.4Schristoscustomize 3331.1Sagc 3341.1Sagcfor d in ${specialdirs}; do 3351.4Schristos ${sudo} mkdir -p ${mnt}/${d} 3361.1Sagcdone 3371.1Sagc 3381.4Schristosif [ \( -n "${custom}" \) -a \( -d "${custom}" \) ]; then 3391.2Sagc echo "${bar} user customisations from files in ${custom} ${bar}" 3401.4Schristos (cd ${custom} && ${sudo} pax -rwpe . ${mnt}) 3411.2Sagcfi 3421.1Sagc 3431.1Sagcexit 0 344