mkimage revision 1.12
1#! /bin/sh 2 3# $NetBSD: mkimage,v 1.12 2013/01/16 15:58:19 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 71usage() { 72 cat << EOF 1>&2 73Usage: $PROG [-S <setsdir>] [-c <custom-files-dir>] [-h <host-arch>] [-s <size>] 74EOF 75exit 1 76} 77 78# Return the filesystem size for an ls -l or tar -xvf list 79# Directories and symlinks in tar are 0 size, we assume one block 80# (which is too much), we round up by the fragment size the rest. 81getfssize() { 82 local bsize="$1" 83 local fsize="$2" 84 85 awk -v fsize=${fsize} -v bsize=${bsize} ' 86 NF >= 9 && $1 != "tar:" { 87 if ($5 == 0) 88 tot += bsize; 89 else 90 tot += ((int)(($5 + fsize - 1) / fsize)) * fsize; 91 } 92 END { 93 printf("%d\n", tot); 94 }' 95} 96 97# find the size of the gzipped files in a .tgz archive 98# Directories appear as 0, so count them as one block 99# and round up files to a fragment. 100sizeone() { 101 if [ ! -f "$1" ] 102 then 103 echo "$PROG: Missing set $1" 1>&2 104 echo 0 105 return; 106 fi 107 108 case "$1" in 109 *.tgz|*.tar.gz|*.tbz|*.tar.bz2|*.txz|*.tar.xz) 110 tar tvzf "$1" | getfssize ${bsize} ${fsize} 111 ;; 112 *) 113 echo 0 114 ;; 115 esac 116} 117 118 119# Return the usable filesystem size in bytes, given the total size in bytes, 120# and optionally block and fragment sizes 121getffssize() { 122 local bytes="$1" 123 local barg 124 local farg 125 local overhead 126 127 if [ -n "$2" ] 128 then 129 barg="-b $2" 130 if [ -n "$3" ] 131 then 132 farg="-f $3" 133 fi 134 fi 135 136 overhead=$(newfs -N ${farg} ${barg} -s "${bytes}b" -F /dev/null | 137 awk '/using/ { 138 printf("%d\n", substr($6, 1, length($6) - 3) * 1024 * 1024); 139 }' 140 ) 141 echo $(( ${bytes} - ${overhead} )) 142} 143 144# Compute the size of an ffs filesystem that can fit x bytes. 145# Instead of duplicating the newfs calculations here we let 146# it do the job, using binary search. 147makeffssize() { 148 local bytes=$1 149 local bsize=$2 150 local fsize=$3 151 local max=$(( 2 * ${bytes} )) 152 local min="${bytes}" 153 local cur 154 local res 155 while true; do 156 cur="$(( ( ${max} + ${min} ) / 2 ))" 157 res="$(getffssize "${cur}" ${bsize} ${fsize})" 158# echo ${min} ${cur} ${max} ${res} ${bytes} 1>&2 159 if [ "${res}" -eq "${bytes}" ] 160 then 161 break 162 elif [ "$(( ${min} + 1 ))" -ge "${max}" ] 163 then 164 break 165 elif [ "${res}" -lt "${bytes}" ] 166 then 167 min="${cur}" 168 elif [ "${res}" -gt "${bytes}" ] 169 then 170 max="${cur}" 171 fi 172 done 173 echo "${cur}" 174} 175 176finish() { 177 cleanup 178 ${sudo} umount ${mnt} 179 ${sudo} vnconfig -u ${vnddev} 180} 181 182 183DIR="$(dirname "$0")" 184PROG="$(basename "$0")" 185bar="===" 186sudo= 187mnt="${TMPDIR:-/tmp}/image.$$" 188src="/usr/src" 189obj="/usr/obj" 190 191 192# Presumable block and fragment size. 193bsize=16384 194fsize=2048 195mtob=$(( 1024 * 1024 )) 196 197# First pass for options to get the host 198OPTS="S:c:h:s:x" 199while getopts "$OPTS" f 200do 201 case $f in 202 h) h="$OPTARG";; 203 *) ;; 204 esac 205done 206 207if [ -z "$h" ] 208then 209 usage 210fi 211 212if [ ! -f "${DIR}/conf/${h}.conf" ] 213then 214 echo $PROG: ${DIR}/conf/${h}.conf is not present 1>&2 215 exit 1 216fi 217 218. "${DIR}/conf/${h}.conf" 219 220OPTIND=1 221while getopts "$OPTS" f 222do 223 case $f in 224 S) setsdir="$OPTARG";; 225 c) custom="$OPTARG";; 226 h) ;; 227 s) size="$OPTARG";; 228 x) set -x;; 229 *) usage;; 230 esac 231done 232 233trap finish 0 1 2 3 15 234 235shift $(( "$OPTIND" - 1 )) 236if [ -n "$1" ]; then 237 # take the next argument as being the image name 238 image="$1" 239 shift 240fi 241 242# calculate the set bytes 243setbytes=0 244echo -n "${bar} computing set sizes (" 245for s in ${sets}; do 246 one="$(sizeone ${setsdir}/${s}.tgz)" 247 echo -n " $s=$(( ${one} / ${mtob} ))MB" 248 setbytes=$(( ${setbytes} + ${one} )) 249done 250echo "): $(( ${setbytes} / ${mtob} ))MB ${bar}" 251 252# calculate size of custom files 253custbytes=0 254if [ -d "${custom}" ]; then 255 custbytes=$(ls -lR "${custom}" | getfssize ${bsize} ${fsize}) 256fi 257echo "${bar} computing custom sizes: $(( ${custbytes} / ${mtob} ))MB ${bar}" 258 259# how many bytes 260rawbytes="$(( ${setbytes} + ${custbytes} ))" 261echo -n "${bar} computing ffs filesystem size for $(( ${rawbytes} / ${mtob} ))MB: " 262ffsbytes="$(makeffssize "${rawbytes}")" 263ffsmb=$(( ${ffsbytes} / ${mtob} )) 264echo " ${ffsmb}MB ${bar}" 265 266# total in MB 267total=$(( ${ffsmb} + ${overhead} )) 268echo "${bar} overhead: ${overhead}MB ${bar}" 269 270if [ $size -eq 0 ]; then 271 # auto-size the pkgs fs 272 newsize=${total} 273else 274 # check that we've been given enough space 275 if [ ${total} -gt ${size} ]; then 276 echo "$PROG: Given size is ${size} MB, but we need ${total} MB" >&2 277 exit 1 278 fi 279 newsize=${size} 280fi 281 282echo "${bar} making a new ${newsize} MB image in ${image} ${bar}" 283dd if=/dev/zero of=${image} bs=1m count=${newsize} conv=sparse 284 285vnddev=$(next_avail vnd) 286echo "${bar} mounting image via vnd ${vnddev} ${bar}" 287${sudo} vnconfig ${vnddev} ${image} 288${sudo} mkdir -p ${mnt} 289make_filesystems 290 291${sudo} mkdir -p ${mnt}/etc ${mnt}/dev 292 293echo -n "${bar} installing sets:" 294(cd ${mnt} && 295 for s in ${sets}; do 296 ss="${setsdir}/${s}.tgz" 297 if [ -f "${ss}" ]; then 298 echo -n " ${s}" 299 ${sudo} tar xpzf "${ss}" 300 fi 301 done 302) 303echo " ${bar}" 304 305echo "${bar} performing customisations ${bar}" 306 307make_fstab 308 309${sudo} cat > ${mnt}/etc/rc.conf << EOF 310# 311# see rc.conf(5) for more information. 312# 313# Use program=YES to enable program, NO to disable it. program_flags are 314# passed to the program on the command line. 315# 316 317# Load the defaults in from /etc/defaults/rc.conf (if it's readable). 318# These can be overridden below. 319# 320if [ -r /etc/defaults/rc.conf ]; then 321 . /etc/defaults/rc.conf 322fi 323 324# If this is not set to YES, the system will drop into single-user mode. 325# 326rc_configured=YES 327 328hostname=${h} 329 330EOF 331 332customize 333 334for d in ${specialdirs}; do 335 ${sudo} mkdir -p ${mnt}/${d} 336done 337 338if [ \( -n "${custom}" \) -a \( -d "${custom}" \) ]; then 339 echo "${bar} user customisations from files in ${custom} ${bar}" 340 (cd ${custom} && ${sudo} pax -rwpe . ${mnt}) 341fi 342 343exit 0 344