1#!/bin/ksh -p 2# 3# CDDL HEADER START 4# 5# The contents of this file are subject to the terms of the 6# Common Development and Distribution License (the "License"). 7# You may not use this file except in compliance with the License. 8# 9# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10# or http://www.opensolaris.org/os/licensing. 11# See the License for the specific language governing permissions 12# and limitations under the License. 13# 14# When distributing Covered Code, include this CDDL HEADER in each 15# file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16# If applicable, add the following below this CDDL HEADER, with the 17# fields enclosed by brackets "[]" replaced with your own identifying 18# information: Portions Copyright [yyyy] [name of copyright owner] 19# 20# CDDL HEADER END 21# 22 23# Copyright 2008 Sun Microsystems, Inc. All rights reserved. 24# Use is subject to license terms. 25 26# ident "%Z%%M% %I% %E% SMI" 27 28format=ufs 29ALT_ROOT= 30EXTRACT_ARGS= 31compress=yes 32SPLIT=unknown 33ERROR=0 34dirsize32=0 35dirsize64=0 36 37usage() { 38 echo "This utility is a component of the bootadm(1M) implementation" 39 echo "and it is not recommended for stand-alone use." 40 echo "Please use bootadm(1M) instead." 41 echo "" 42 echo "Usage: ${0##*/}: [-R \<root\>] [-p \<platform\>] [--nocompress]" 43 echo "where \<platform\> is one of i86pc, sun4u or sun4v" 44 exit 45} 46 47# default platform is what we're running on 48PLATFORM=`uname -m` 49 50# 51# set path, but inherit /tmp/bfubin if owned by 52# same uid executing this process, which must be root. 53# 54if [ "`echo $PATH | cut -f 1 -d :`" = /tmp/bfubin ] && \ 55 [ -O /tmp/bfubin ] ; then 56 export PATH=/tmp/bfubin 57 export GZIP_CMD=/tmp/bfubin/gzip 58else 59 export PATH=/usr/sbin:/usr/bin:/sbin 60 export GZIP_CMD=/usr/bin/gzip 61fi 62 63EXTRACT_FILELIST="/boot/solaris/bin/extract_boot_filelist" 64 65# 66# Parse options 67# 68while [ "$1" != "" ] 69do 70 case $1 in 71 -R) shift 72 ALT_ROOT="$1" 73 if [ "$ALT_ROOT" != "/" ]; then 74 echo "Creating boot_archive for $ALT_ROOT" 75 EXTRACT_ARGS="${EXTRACT_ARGS} -R ${ALT_ROOT}" 76 EXTRACT_FILELIST="${ALT_ROOT}${EXTRACT_FILELIST}" 77 fi 78 ;; 79 -n|--nocompress) compress=no 80 ;; 81 -p) shift 82 PLATFORM="$1" 83 EXTRACT_ARGS="${EXTRACT_ARGS} -p ${PLATFORM}" 84 ;; 85 *) usage 86 ;; 87 esac 88 shift 89done 90 91if [ -x /usr/bin/mkisofs -o -x /tmp/bfubin/mkisofs ] ; then 92 format=isofs 93fi 94 95# 96# mkisofs on s8 doesn't support functionality used by GRUB boot. 97# Use ufs format for boot archive instead. 98# 99release=`uname -r` 100if [ "$release" = "5.8" ]; then 101 format=ufs 102fi 103 104shift `expr $OPTIND - 1` 105 106if [ $# -eq 1 ]; then 107 ALT_ROOT="$1" 108 echo "Creating boot_archive for $ALT_ROOT" 109fi 110 111case $PLATFORM in 112i386) PLATFORM=i86pc 113 ISA=i386 114 ARCH64=amd64 115 ;; 116i86pc) ISA=i386 117 ARCH64=amd64 118 ;; 119sun4u) ISA=sparc 120 ARCH64=sparcv9 121 ;; 122sun4v) ISA=sparc 123 ARCH64=sparcv9 124 ;; 125*) usage 126 ;; 127esac 128 129BOOT_ARCHIVE=platform/$PLATFORM/boot_archive 130BOOT_ARCHIVE_64=platform/$PLATFORM/$ARCH64/boot_archive 131 132if [ $PLATFORM = i86pc ] ; then 133 NM=/bin/nm 134 SYMDEF=/boot/solaris/bin/symdef 135 if [ -x $NM ]; then 136 $NM "$ALT_ROOT"/platform/i86pc/kernel/unix | \ 137 grep dboot_image >/dev/null 2>&1 138 if [ $? = 0 ]; then 139 SPLIT=yes 140 else 141 SPLIT=no 142 compress=no 143 fi 144 elif [ ! -x $SYMDEF ]; then 145 # Shouldn't happen 146 echo "Warning: both $NM and $SYMDEF not present." 147 echo "Creating single archive at $ALT_ROOT/$BOOT_ARCHIVE" 148 SPLIT=no 149 compress=no 150 elif $SYMDEF "$ALT_ROOT"/platform/i86pc/kernel/unix \ 151 dboot_image 2>/dev/null; then 152 SPLIT=yes 153 else 154 # no dboot 155 SPLIT=no 156 compress=no 157 fi 158else # must be sparc 159 SPLIT=no # there's only 64-bit (sparcv9), so don't split 160 compress=no 161fi 162 163[ -x $GZIP_CMD ] || compress=no 164 165function cleanup 166{ 167 umount -f "$rdmnt32" 2>/dev/null 168 umount -f "$rdmnt64" 2>/dev/null 169 lofiadm -d "$rdfile32" 2>/dev/null 170 lofiadm -d "$rdfile64" 2>/dev/null 171 [ -n "$rddir" ] && rm -fr "$rddir" 2> /dev/null 172 [ -n "$new_rddir" ] && rm -fr "$new_rddir" 2>/dev/null 173} 174 175function getsize 176{ 177 # Estimate image size and add 10% overhead for ufs stuff. 178 # Note, we can't use du here in case we're on a filesystem, e.g. zfs, 179 # in which the disk usage is less than the sum of the file sizes. 180 # The nawk code 181 # 182 # {t += ($5 % 1024) ? (int($5 / 1024) + 1) * 1024 : $5} 183 # 184 # below rounds up the size of a file/directory, in bytes, to the 185 # next multiple of 1024. This mimics the behavior of ufs especially 186 # with directories. This results in a total size that's slightly 187 # bigger than if du was called on a ufs directory. 188 size32=$(cat "$list32" | xargs -I {} ls -lLd "{}" 2> /dev/null | 189 nawk '{t += ($5 % 1024) ? (int($5 / 1024) + 1) * 1024 : $5} 190 END {print int(t * 1.10 / 1024)}') 191 (( size32 += dirsize32 )) 192 size64=$(cat "$list64" | xargs -I {} ls -lLd "{}" 2> /dev/null | 193 nawk '{t += ($5 % 1024) ? (int($5 / 1024) + 1) * 1024 : $5} 194 END {print int(t * 1.10 / 1024)}') 195 (( size64 += dirsize64 )) 196 (( total_size = size32 + size64 )) 197 198 if [ $compress = yes ] ; then 199 total_size=`echo $total_size | nawk '{print int($1 / 2)}'` 200 fi 201} 202 203# 204# Copies all desired files to a target directory. One argument should be 205# passed: the file containing the list of files to copy. This function also 206# depends on several variables that must be set before calling: 207# 208# $ALT_ROOT - the target directory 209# $compress - whether or not the files in the archives should be compressed 210# $rdmnt - the target directory 211# 212function copy_files 213{ 214 list="$1" 215 216 # 217 # If compress is set, the files are gzip'd and put in the correct 218 # location in the loop. Nothing is printed, so the pipe and cpio 219 # at the end is a nop. 220 # 221 # If compress is not set, the file names are printed, which causes 222 # the cpio at the end to do the copy. 223 # 224 while read path 225 do 226 if [ $compress = yes ]; then 227 dir="${path%/*}" 228 [ -d "$rdmnt/$dir" ] || mkdir -p "$rdmnt/$dir" 229 $GZIP_CMD -c "$path" > "$rdmnt/$path" 230 else 231 print "$path" 232 fi 233 done <"$list" | cpio -pdum "$rdmnt" 2>/dev/null 234 235 if [ $ISA = sparc ] ; then 236 # copy links 237 find $filelist -type l -print 2>/dev/null |\ 238 cpio -pdum "$rdmnt" 2>/dev/null 239 if [ $compress = yes ] ; then 240 # always copy unix uncompressed 241 find $filelist -name unix -type f -print 2>/dev/null |\ 242 cpio -pdum "$rdmnt" 2>/dev/null 243 fi 244 fi 245 246} 247 248# 249# The first argument can be: 250# 251# "both" - create an archive with both 32-bit and 64-bit binaries 252# "32-bit" - create an archive with only 32-bit binaries 253# "64-bit" - create an archive with only 64-bit binaries 254# 255function create_ufs 256{ 257 which=$1 258 archive=$2 259 lofidev=$3 260 261 # should we exclude amd64 binaries? 262 if [ "$which" = "32-bit" ]; then 263 rdfile="$rdfile32" 264 rdmnt="$rdmnt32" 265 list="$list32" 266 elif [ "$which" = "64-bit" ]; then 267 rdfile="$rdfile64" 268 rdmnt="$rdmnt64" 269 list="$list64" 270 else 271 rdfile="$rdfile32" 272 rdmnt="$rdmnt32" 273 list="$list32" 274 fi 275 276 newfs $lofidev < /dev/null 2> /dev/null 277 mkdir "$rdmnt" 278 mount -F mntfs mnttab /etc/mnttab > /dev/null 2>&1 279 mount -o nologging $lofidev "$rdmnt" 280 files= 281 282 # do the actual copy 283 copy_files "$list" 284 umount "$rdmnt" 285 rmdir "$rdmnt" 286 287 if [ $ISA = sparc ] ; then 288 rlofidev=`echo "$lofidev" | sed -e "s/dev\/lofi/dev\/rlofi/"` 289 bb="$ALT_ROOT/platform/$PLATFORM/lib/fs/ufs/bootblk" 290 # installboot is not available on all platforms 291 dd if=$bb of=$rlofidev bs=1b oseek=1 count=15 conv=sync 2>&1 292 fi 293 294 # 295 # Check if gzip exists in /usr/bin, so we only try to run gzip 296 # on systems that have gzip. Then run gzip out of the patch to 297 # pick it up from bfubin or something like that if needed. 298 # 299 # If compress is set, the individual files in the archive are 300 # compressed, and the final compression will accomplish very 301 # little. To save time, we skip the gzip in this case. 302 # 303 if [ $ISA = i386 ] && [ $compress = no ] && \ 304 [ -x $GZIP_CMD ] ; then 305 gzip -c "$rdfile" > "${archive}-new" 306 else 307 cat "$rdfile" > "${archive}-new" 308 fi 309} 310 311# 312# The first argument can be: 313# 314# "both" - create an archive with both 32-bit and 64-bit binaries 315# "32-bit" - create an archive with only 32-bit binaries 316# "64-bit" - create an archive with only 64-bit binaries 317# 318function create_isofs 319{ 320 which=$1 321 archive=$2 322 323 # should we exclude amd64 binaries? 324 if [ "$which" = "32-bit" ]; then 325 rdmnt="$rdmnt32" 326 errlog="$errlog32" 327 list="$list32" 328 elif [ "$which" = "64-bit" ]; then 329 rdmnt="$rdmnt64" 330 errlog="$errlog64" 331 list="$list64" 332 else 333 rdmnt="$rdmnt32" 334 errlog="$errlog32" 335 list="$list32" 336 fi 337 338 # create image directory seed with graft points 339 mkdir "$rdmnt" 340 files= 341 isocmd="mkisofs -quiet -graft-points -dlrDJN -relaxed-filenames" 342 343 if [ $ISA = sparc ] ; then 344 bb="$ALT_ROOT/platform/$PLATFORM/lib/fs/hsfs/bootblk" 345 isocmd="$isocmd -G \"$bb\"" 346 fi 347 348 copy_files "$list" 349 isocmd="$isocmd \"$rdmnt\"" 350 rm -f "$errlog" 351 352 # 353 # Check if gzip exists in /usr/bin, so we only try to run gzip 354 # on systems that have gzip. Then run gzip out of the patch to 355 # pick it up from bfubin or something like that if needed. 356 # 357 # If compress is set, the individual files in the archive are 358 # compressed, and the final compression will accomplish very 359 # little. To save time, we skip the gzip in this case. 360 # 361 if [ $ISA = i386 ] &&[ $compress = no ] && [ -x $GZIP_CMD ] 362 then 363 ksh -c "$isocmd" 2> "$errlog" | \ 364 gzip > "${archive}-new" 365 else 366 ksh -c "$isocmd" 2> "$errlog" > "${archive}-new" 367 fi 368 369 dd_ret=0 370 if [ $ISA = sparc ] ; then 371 bb="$ALT_ROOT/platform/$PLATFORM/lib/fs/hsfs/bootblk" 372 dd if="$bb" of="${archive}-new" bs=1b oseek=1 count=15 \ 373 conv=notrunc conv=sync >> "$errlog" 2>&1 374 dd_ret=$? 375 fi 376 377 if [ -s "$errlog" ] || [ $dd_ret -ne 0 ] ; then 378 grep Error: "$errlog" >/dev/null 2>&1 379 if [ $? -eq 0 ] || [ $dd_ret -ne 0 ] ; then 380 cat "$errlog" 381 rm -f "${archive}-new" 382 fi 383 fi 384 rm -f "$errlog" 385} 386 387function create_archive 388{ 389 which=$1 390 archive=$2 391 lofidev=$3 392 393 echo "updating $archive" 394 395 if [ "$format" = "ufs" ]; then 396 create_ufs "$which" "$archive" "$lofidev" 397 else 398 create_isofs "$which" "$archive" 399 fi 400 401 # sanity check the archive before moving it into place 402 # 403 ARCHIVE_SIZE=`ls -l "${archive}-new" | nawk '{ print $5 }'` 404 if [ $compress = yes ] || [ $ISA = sparc ] ; then 405 # 406 # 'file' will report "English text" for uncompressed 407 # boot_archives. Checking for that doesn't seem stable, 408 # so we just check that the file exists. 409 # 410 ls "${archive}-new" >/dev/null 2>&1 411 else 412 # 413 # the file type check also establishes that the 414 # file exists at all 415 # 416 LC_MESSAGES=C file "${archive}-new" | grep gzip > /dev/null 417 fi 418 419 if [ $? = 1 ] && [ -x $GZIP_CMD ] || [ $ARCHIVE_SIZE -lt 5000 ] 420 then 421 # 422 # Two of these functions may be run in parallel. We 423 # need to allow the other to clean up, so we can't 424 # exit immediately. Instead, we set a flag. 425 # 426 echo "update of $archive failed" 427 ERROR=1 428 else 429 lockfs -f "/$ALT_ROOT" 2>/dev/null 430 mv "${archive}-new" "$archive" 431 lockfs -f "/$ALT_ROOT" 2>/dev/null 432 fi 433 434} 435 436function fatal_error 437{ 438 print -u2 $* 439 exit 1 440} 441 442# 443# get filelist 444# 445if [ ! -f "$ALT_ROOT/boot/solaris/filelist.ramdisk" ] && 446 [ ! -f "$ALT_ROOT/etc/boot/solaris/filelist.ramdisk" ] 447then 448 print -u2 "Can't find filelist.ramdisk" 449 exit 1 450fi 451filelist=$($EXTRACT_FILELIST $EXTRACT_ARGS \ 452 /boot/solaris/filelist.ramdisk \ 453 /etc/boot/solaris/filelist.ramdisk \ 454 2>/dev/null | sort -u) 455 456# 457# We use /tmp/ for scratch space now. This may be changed later if there 458# is insufficient space in /tmp/. 459# 460rddir="/tmp/create_ramdisk.$$.tmp" 461new_rddir= 462rm -rf "$rddir" 463mkdir "$rddir" || fatal_error "Could not create temporary directory $rddir" 464 465# Clean up upon exit. 466trap 'cleanup' EXIT 467 468list32="$rddir/filelist.32" 469list64="$rddir/filelist.64" 470 471touch $list32 $list64 472 473# 474# This loop creates the 32-bit and 64-bit lists of files. The 32-bit list 475# is written to stdout, which is redirected at the end of the loop. The 476# 64-bit list is appended with each write. 477# 478cd "/$ALT_ROOT" 479find $filelist -print 2>/dev/null | while read path 480do 481 if [ $SPLIT = no ]; then 482 print "$path" 483 elif [ -d "$path" ]; then 484 if [ $format = ufs ]; then 485 size=`ls -lLd "$path" | nawk ' 486 {print ($5 % 1024) ? (int($5 / 1024) + 1) * 1024 : $5}'` 487 if [ `basename "$path"` != "amd64" ]; then 488 (( dirsize32 += size )) 489 fi 490 (( dirsize64 += size )) 491 fi 492 else 493 case `LC_MESSAGES=C /usr/bin/file -m /dev/null "$path" 2>/dev/null` in 494 *ELF\ 64-bit*) 495 print "$path" >> "$list64" 496 ;; 497 *ELF\ 32-bit*) 498 print "$path" 499 ;; 500 *) 501 # put in both lists 502 print "$path" 503 print "$path" >> "$list64" 504 esac 505 fi 506done >"$list32" 507 508if [ $format = ufs ] ; then 509 # calculate image size 510 getsize 511 512 # check to see if there is sufficient space in tmpfs 513 # 514 tmp_free=`df -b /tmp | tail -1 | awk '{ printf ($2) }'` 515 (( tmp_free = tmp_free / 2 )) 516 517 if [ $total_size -gt $tmp_free ] ; then 518 # assumes we have enough scratch space on $ALT_ROOT 519 new_rddir="/$ALT_ROOT/create_ramdisk.$$.tmp" 520 rm -rf "$new_rddir" 521 mkdir "$new_rddir" || fatal_error \ 522 "Could not create temporary directory $new_rddir" 523 524 # Save the file lists 525 mv "$list32" "$new_rddir"/ 526 mv "$list64" "$new_rddir"/ 527 list32="/$new_rddir/filelist.32" 528 list64="/$new_rddir/filelist.64" 529 530 # Remove the old $rddir and set the new value of rddir 531 rm -rf "$rddir" 532 rddir="$new_rddir" 533 new_rddir= 534 fi 535fi 536 537rdfile32="$rddir/rd.file.32" 538rdfile64="$rddir/rd.file.64" 539rdmnt32="$rddir/rd.mount.32" 540rdmnt64="$rddir/rd.mount.64" 541errlog32="$rddir/rd.errlog.32" 542errlog64="$rddir/rd.errlog.64" 543lofidev32="" 544lofidev64="" 545 546if [ $SPLIT = yes ]; then 547 # 548 # We can't run lofiadm commands in parallel, so we have to do 549 # them here. 550 # 551 if [ "$format" = "ufs" ]; then 552 mkfile ${size32}k "$rdfile32" 553 lofidev32=`lofiadm -a "$rdfile32"` 554 mkfile ${size64}k "$rdfile64" 555 lofidev64=`lofiadm -a "$rdfile64"` 556 fi 557 create_archive "32-bit" "$ALT_ROOT/$BOOT_ARCHIVE" $lofidev32 & 558 create_archive "64-bit" "$ALT_ROOT/$BOOT_ARCHIVE_64" $lofidev64 559 wait 560 if [ "$format" = "ufs" ]; then 561 lofiadm -d "$rdfile32" 562 lofiadm -d "$rdfile64" 563 fi 564else 565 if [ "$format" = "ufs" ]; then 566 mkfile ${total_size}k "$rdfile32" 567 lofidev32=`lofiadm -a "$rdfile32"` 568 fi 569 create_archive "both" "$ALT_ROOT/$BOOT_ARCHIVE" $lofidev32 570 [ "$format" = "ufs" ] && lofiadm -d "$rdfile32" 571fi 572if [ $ERROR = 1 ]; then 573 cleanup 574 exit 1 575fi 576 577# 578# For the diskless case, hardlink archive to /boot to make it 579# visible via tftp. /boot is lofs mounted under /tftpboot/<hostname>. 580# NOTE: this script must work on both client and server. 581# 582grep "[ ]/[ ]*nfs[ ]" "$ALT_ROOT/etc/vfstab" > /dev/null 583if [ $? = 0 ]; then 584 rm -f "$ALT_ROOT/boot/boot_archive" "$ALT_ROOT/boot/amd64/boot_archive" 585 ln "$ALT_ROOT/$BOOT_ARCHIVE" "$ALT_ROOT/boot/boot_archive" 586 if [ $SPLIT = yes ]; then 587 ln "$ALT_ROOT/$BOOT_ARCHIVE_64" \ 588 "$ALT_ROOT/boot/amd64/boot_archive" 589 fi 590fi 591[ -n "$rddir" ] && rm -rf "$rddir" 592