mkimage revision 1.11
1#! /bin/sh 2 3# $NetBSD: mkimage,v 1.11 2013/01/15 21:04:41 christos Exp $ 4 5# Copyright (c) 2012 Alistair Crooks <agc@NetBSD.org> 6# All rights reserved. 7# 8# Redistribution and use in source and binary forms, with or without 9# modification, are permitted provided that the following conditions 10# are met: 11# 1. Redistributions of source code must retain the above copyright 12# notice, this list of conditions and the following disclaimer. 13# 2. Redistributions in binary form must reproduce the above copyright 14# notice, this list of conditions and the following disclaimer in the 15# documentation and/or other materials provided with the distribution. 16# 17# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27# 28 29# find next available vnd, from kre 30next_avail () 31{ 32 local dev="$1" 33 local N=$(( ${#dev} + 1 )) 34 local unit units 35 36 units=$( 37 sysctl -n hw.disknames | 38 tr ' ' '\012' | 39 grep '^'"${dev}"'[0-9]' | 40 sort -n -k 1.$N ) 41 42 test -z "${units}" && { 43 test -e "/dev/${dev}0a" || { 44 echo >&2 "No ${dev}s available!" 45 return 1 46 } 47 echo "${dev}0" 48 return 49 } 50 51 N=0 52 for unit in ${units} 53 do 54 if [ "${unit}" = "${dev}${N}" ] 55 then 56 N=$(( N + 1 )) 57 else 58 echo "${dev}${N}" 59 return 60 fi 61 done 62 63 test -e /dev/"${dev}${N}a" || { 64 echo >&2 "All ${dev}s in use" 65 return 1 66 } 67 68 echo "${dev}${N}" 69} 70 71# find the size of the gzipped files in a .tgz archive 72# Directories appear as 0, so count them as one block 73# and round up files to a fragment. 74sizeone() { 75 if [ ! -f "$1" ] 76 then 77 echo "$PROG: Missing set $1" 1>&2 78 echo 0 79 return; 80 fi 81 case "$1" in 82 *.tgz|*.tar.gz|*.tbz|*.tar.bz2|*.txz|*.tar.xz) 83 tar tvzf "$1" | 84 awk -v fsize=${fsize} -v bsize=${bsize} ' 85 { 86 if ($5 == 0) 87 tot += bsize; 88 else 89 tot += ((int)(($5 + fsize - 1) / fsize)) * fsize; 90 } 91 END { 92 printf("%d\n", tot); 93 }' 94 ;; 95 *) 96 echo 0 97 ;; 98 esac 99} 100 101usage() { 102 cat << EOF 1>&2 103Usage: $PROG [-S <setsdir>] [-c <custom-files-dir>] [-h <host-arch>] [-s <size>] 104EOF 105exit 1 106} 107 108# Return the usable filesystem size in bytes, given the total size in bytes, 109# and optionally block and fragment sizes 110getffssize() { 111 local bytes="$1" 112 local barg 113 local farg 114 local overhead 115 116 if [ -n "$2" ] 117 then 118 barg="-b $2" 119 if [ -n "$3" ] 120 then 121 farg="-f $3" 122 fi 123 fi 124 125 overhead=$(newfs -N ${farg} ${barg} -s "${bytes}b" -F /dev/null | 126 awk '/using/ { 127 printf("%d\n", substr($6, 1, length($6) - 3) * 1024 * 1024); 128 }' 129 ) 130 echo $(( ${bytes} - ${overhead} )) 131} 132 133# Compute the size of an ffs filesystem that can fit x bytes. 134# Instead of duplicating the newfs calculations here we let 135# it do the job, using binary search. 136makeffssize() { 137 local bytes=$1 138 local bsize=$2 139 local fsize=$3 140 local max=$(( 2 * ${bytes} )) 141 local min="${bytes}" 142 local cur 143 local res 144 while true; do 145 cur="$(( ( ${max} + ${min} ) / 2 ))" 146 res="$(getffssize "${cur}" ${bsize} ${fsize})" 147# echo ${min} ${cur} ${max} ${res} ${bytes} 1>&2 148 if [ "${res}" -eq "${bytes}" ] 149 then 150 break 151 elif [ "$(( ${min} + 1 ))" -ge "${max}" ] 152 then 153 break 154 elif [ "${res}" -lt "${bytes}" ] 155 then 156 min="${cur}" 157 elif [ "${res}" -gt "${bytes}" ] 158 then 159 max="${cur}" 160 fi 161 done 162 echo "${cur}" 163} 164 165finish() { 166 cleanup 167 ${sudo} umount ${mnt} 168 ${sudo} vnconfig -u ${vnddev} 169} 170 171 172DIR="$(dirname "$0")" 173PROG="$(basename "$0")" 174bar="===" 175sudo= 176mnt="${TMPDIR:-/tmp}/image.$$" 177src="/usr/src" 178obj="/usr/obj" 179 180 181# Presumable block and fragment size. 182bsize=16384 183fsize=2048 184 185# First pass for options to get the host 186OPTS="S:c:h:s:x" 187while getopts "$OPTS" f 188do 189 case $f in 190 h) h="$OPTARG";; 191 *) ;; 192 esac 193done 194 195if [ -z "$h" ] 196then 197 usage 198fi 199 200if [ ! -f "${DIR}/conf/${h}.conf" ] 201then 202 echo $PROG: ${DIR}/conf/${h}.conf is not present 1>&2 203 exit 1 204fi 205 206. "${DIR}/conf/${h}.conf" 207 208OPTIND=1 209while getopts "$OPTS" f 210do 211 case $f in 212 S) setsdir="$OPTARG";; 213 c) custom="$OPTARG";; 214 h) ;; 215 s) size="$OPTARG";; 216 x) set -x;; 217 *) usage;; 218 esac 219done 220 221trap finish 0 1 2 3 15 222 223shift $(( "$OPTIND" - 1 )) 224if [ -n "$1" ]; then 225 # take the next argument as being the image name 226 image="$1" 227 shift 228fi 229 230# calculate the set bytes 231setbytes=0 232echo -n "${bar} computing set sizes (" 233for s in ${sets}; do 234 one="$(sizeone ${setsdir}/${s}.tgz)" 235 echo -n " $s=$(( ${one} / 1024 / 1024 ))MB" 236 setbytes=$(( ${setbytes} + ${one} )) 237done 238echo "): $(( ${setbytes} / 1024 / 1024 ))MB ${bar}" 239 240# calculate size of custom files 241custbytes=0 242if [ -d "${custom}" ]; then 243 custbytes=$(ls -lR "${custom}" | 244 awk 'NF == 9 { tot += $5 } END { print tot }') 245fi 246echo "${bar} computing custom sizes: $(( ${custbytes} / 1024 / 1024 ))MB ${bar}" 247 248# how many bytes 249rawbytes="$(( ${setbytes} + ${custbytes} ))" 250echo -n "${bar} computing ffs filesystem size for $(( ${rawbytes} / 1024 / 1024 ))MB: " 251ffsbytes="$(makeffssize "${rawbytes}")" 252ffsmb=$(( ${ffsbytes} / 1024 / 1024 )) 253echo " ${ffsmb}MB ${bar}" 254 255# total in MB 256total=$(( ${ffsmb} + ${overhead} )) 257echo "${bar} overhead: ${overhead}MB ${bar}" 258 259if [ $size -eq 0 ]; then 260 # auto-size the pkgs fs 261 newsize=${total} 262else 263 # check that we've been given enough space 264 if [ ${total} -gt ${size} ]; then 265 echo "$PROG: Given size is ${size} MB, but we need ${total} MB" >&2 266 exit 1 267 fi 268 newsize=${size} 269fi 270 271echo "${bar} making a new ${newsize} MB image in ${image} ${bar}" 272dd if=/dev/zero of=${image} bs=1m count=${newsize} conv=sparse 273 274vnddev=$(next_avail vnd) 275echo "${bar} mounting image via vnd ${vnddev} ${bar}" 276${sudo} vnconfig ${vnddev} ${image} 277${sudo} mkdir -p ${mnt} 278make_filesystems 279 280${sudo} mkdir -p ${mnt}/etc ${mnt}/dev 281 282echo -n "${bar} installing sets:" 283(cd ${mnt} && 284 for s in ${sets}; do 285 ss="${setsdir}/${s}.tgz" 286 if [ -f "${ss}" ]; then 287 echo -n " ${s}" 288 ${sudo} tar xpzf "${ss}" 289 fi 290 done 291) 292echo " ${bar}" 293 294echo "${bar} performing customisations ${bar}" 295 296make_fstab 297 298${sudo} cat > ${mnt}/etc/rc.conf << EOF 299# 300# see rc.conf(5) for more information. 301# 302# Use program=YES to enable program, NO to disable it. program_flags are 303# passed to the program on the command line. 304# 305 306# Load the defaults in from /etc/defaults/rc.conf (if it's readable). 307# These can be overridden below. 308# 309if [ -r /etc/defaults/rc.conf ]; then 310 . /etc/defaults/rc.conf 311fi 312 313# If this is not set to YES, the system will drop into single-user mode. 314# 315rc_configured=YES 316 317hostname=${h} 318 319EOF 320 321customize 322 323for d in ${specialdirs}; do 324 ${sudo} mkdir -p ${mnt}/${d} 325done 326 327if [ \( -n "${custom}" \) -a \( -d "${custom}" \) ]; then 328 echo "${bar} user customisations from files in ${custom} ${bar}" 329 (cd ${custom} && ${sudo} pax -rwpe . ${mnt}) 330fi 331 332exit 0 333