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