mkimage revision 1.13
11.1Sagc#! /bin/sh 21.1Sagc 31.13Schristos# $NetBSD: mkimage,v 1.13 2013/01/16 23:27:34 christos Exp $ 41.13Schristos# 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.13Schristos exit 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.13Schristos cleanup 1781.13Schristos ${sudo} umount ${mnt} 1791.13Schristos ${sudo} vnconfig -u ${vnddev} 1801.13Schristos rm -fr ${mnt} 1811.4Schristos} 1821.4Schristos 1831.11Schristos 1841.4SchristosDIR="$(dirname "$0")" 1851.4SchristosPROG="$(basename "$0")" 1861.2Sagcbar="===" 1871.4Schristossudo= 1881.7Schristosmnt="${TMPDIR:-/tmp}/image.$$" 1891.4Schristossrc="/usr/src" 1901.4Schristosobj="/usr/obj" 1911.4Schristos 1921.13Schristossets="base comp etc games man misc modules text" 1931.13Schristosxsets="xbase xcomp xetc xfont xserver" 1941.11Schristos 1951.11Schristos# Presumable block and fragment size. 1961.11Schristosbsize=16384 1971.11Schristosfsize=2048 1981.12Schristosmtob=$(( 1024 * 1024 )) 1991.11Schristos 2001.4Schristos# First pass for options to get the host 2011.4SchristosOPTS="S:c:h:s:x" 2021.4Schristoswhile getopts "$OPTS" f 2031.4Schristosdo 2041.4Schristos case $f in 2051.4Schristos h) h="$OPTARG";; 2061.4Schristos *) ;; 2071.4Schristos esac 2081.4Schristosdone 2091.4Schristos 2101.4Schristosif [ -z "$h" ] 2111.4Schristosthen 2121.4Schristos usage 2131.4Schristosfi 2141.4Schristos 2151.5Schristosif [ ! -f "${DIR}/conf/${h}.conf" ] 2161.4Schristosthen 2171.5Schristos echo $PROG: ${DIR}/conf/${h}.conf is not present 1>&2 2181.4Schristos exit 1 2191.4Schristosfi 2201.4Schristos 2211.5Schristos. "${DIR}/conf/${h}.conf" 2221.4Schristos 2231.4SchristosOPTIND=1 2241.4Schristoswhile getopts "$OPTS" f 2251.4Schristosdo 2261.4Schristos case $f in 2271.4Schristos S) setsdir="$OPTARG";; 2281.4Schristos c) custom="$OPTARG";; 2291.4Schristos h) ;; 2301.4Schristos s) size="$OPTARG";; 2311.13Schristos x) sets="$sets $xsets";; 2321.6Schristos *) usage;; 2331.1Sagc esac 2341.1Sagcdone 2351.1Sagc 2361.8Sjmcneilltrap finish 0 1 2 3 15 2371.8Sjmcneill 2381.4Schristosshift $(( "$OPTIND" - 1 )) 2391.4Schristosif [ -n "$1" ]; then 2401.1Sagc # take the next argument as being the image name 2411.1Sagc image="$1" 2421.1Sagc shift 2431.1Sagcfi 2441.1Sagc 2451.11Schristos# calculate the set bytes 2461.11Schristossetbytes=0 2471.11Schristosecho -n "${bar} computing set sizes (" 2481.13Schristosb= 2491.1Sagcfor s in ${sets}; do 2501.4Schristos one="$(sizeone ${setsdir}/${s}.tgz)" 2511.13Schristos echo -n "$b$s=$(( ${one} / ${mtob} ))MB" 2521.11Schristos setbytes=$(( ${setbytes} + ${one} )) 2531.13Schristos b=" " 2541.1Sagcdone 2551.12Schristosecho "): $(( ${setbytes} / ${mtob} ))MB ${bar}" 2561.11Schristos 2571.2Sagc# calculate size of custom files 2581.11Schristoscustbytes=0 2591.2Sagcif [ -d "${custom}" ]; then 2601.12Schristos custbytes=$(ls -lR "${custom}" | getfssize ${bsize} ${fsize}) 2611.2Sagcfi 2621.12Schristosecho "${bar} computing custom sizes: $(( ${custbytes} / ${mtob} ))MB ${bar}" 2631.11Schristos 2641.11Schristos# how many bytes 2651.11Schristosrawbytes="$(( ${setbytes} + ${custbytes} ))" 2661.12Schristosecho -n "${bar} computing ffs filesystem size for $(( ${rawbytes} / ${mtob} ))MB: " 2671.11Schristosffsbytes="$(makeffssize "${rawbytes}")" 2681.12Schristosffsmb=$(( ${ffsbytes} / ${mtob} )) 2691.11Schristosecho " ${ffsmb}MB ${bar}" 2701.11Schristos 2711.11Schristos# total in MB 2721.11Schristostotal=$(( ${ffsmb} + ${overhead} )) 2731.11Schristosecho "${bar} overhead: ${overhead}MB ${bar}" 2741.11Schristos 2751.1Sagcif [ $size -eq 0 ]; then 2761.1Sagc # auto-size the pkgs fs 2771.10Schristos newsize=${total} 2781.1Sagcelse 2791.1Sagc # check that we've been given enough space 2801.1Sagc if [ ${total} -gt ${size} ]; then 2811.4Schristos echo "$PROG: Given size is ${size} MB, but we need ${total} MB" >&2 2821.1Sagc exit 1 2831.1Sagc fi 2841.10Schristos newsize=${size} 2851.1Sagcfi 2861.1Sagc 2871.10Schristosecho "${bar} making a new ${newsize} MB image in ${image} ${bar}" 2881.10Schristosdd if=/dev/zero of=${image} bs=1m count=${newsize} conv=sparse 2891.1Sagc 2901.1Sagcvnddev=$(next_avail vnd) 2911.1Sagcecho "${bar} mounting image via vnd ${vnddev} ${bar}" 2921.2Sagc${sudo} vnconfig ${vnddev} ${image} 2931.4Schristos${sudo} mkdir -p ${mnt} 2941.4Schristosmake_filesystems 2951.4Schristos 2961.4Schristos${sudo} mkdir -p ${mnt}/etc ${mnt}/dev 2971.1Sagc 2981.10Schristosecho -n "${bar} installing sets:" 2991.4Schristos(cd ${mnt} && 3001.1Sagc for s in ${sets}; do 3011.10Schristos ss="${setsdir}/${s}.tgz" 3021.10Schristos if [ -f "${ss}" ]; then 3031.10Schristos echo -n " ${s}" 3041.10Schristos ${sudo} tar xpzf "${ss}" 3051.4Schristos fi 3061.1Sagc done 3071.1Sagc) 3081.10Schristosecho " ${bar}" 3091.1Sagc 3101.1Sagcecho "${bar} performing customisations ${bar}" 3111.1Sagc 3121.4Schristosmake_fstab 3131.1Sagc 3141.4Schristos${sudo} cat > ${mnt}/etc/rc.conf << EOF 3151.1Sagc# 3161.1Sagc# see rc.conf(5) for more information. 3171.1Sagc# 3181.1Sagc# Use program=YES to enable program, NO to disable it. program_flags are 3191.1Sagc# passed to the program on the command line. 3201.1Sagc# 3211.1Sagc 3221.1Sagc# Load the defaults in from /etc/defaults/rc.conf (if it's readable). 3231.1Sagc# These can be overridden below. 3241.1Sagc# 3251.1Sagcif [ -r /etc/defaults/rc.conf ]; then 3261.1Sagc . /etc/defaults/rc.conf 3271.1Sagcfi 3281.1Sagc 3291.1Sagc# If this is not set to YES, the system will drop into single-user mode. 3301.1Sagc# 3311.1Sagcrc_configured=YES 3321.1Sagc 3331.4Schristoshostname=${h} 3341.1Sagc 3351.4SchristosEOF 3361.1Sagc 3371.4Schristoscustomize 3381.1Sagc 3391.1Sagcfor d in ${specialdirs}; do 3401.4Schristos ${sudo} mkdir -p ${mnt}/${d} 3411.1Sagcdone 3421.1Sagc 3431.4Schristosif [ \( -n "${custom}" \) -a \( -d "${custom}" \) ]; then 3441.2Sagc echo "${bar} user customisations from files in ${custom} ${bar}" 3451.4Schristos (cd ${custom} && ${sudo} pax -rwpe . ${mnt}) 3461.2Sagcfi 3471.1Sagc 3481.1Sagcexit 0 349