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 2009 Sun Microsystems, Inc. All rights reserved. 24 # Use is subject to license terms. 25 26 format=ufs 27 ALT_ROOT= 28 EXTRACT_ARGS= 29 compress=yes 30 SPLIT=unknown 31 ERROR=0 32 dirsize32=0 33 dirsize64=0 34 35 usage() { 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 46 PLATFORM=`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 # 52 if [ "`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 56 else 57 export PATH=/usr/sbin:/usr/bin:/sbin 58 export GZIP_CMD=/usr/bin/gzip 59 fi 60 61 EXTRACT_FILELIST="/boot/solaris/bin/extract_boot_filelist" 62 63 # 64 # Parse options 65 # 66 while [ "$1" != "" ] 67 do 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 87 done 88 89 if [ -x /usr/bin/mkisofs -o -x /tmp/bfubin/mkisofs ] ; then 90 format=isofs 91 fi 92 93 # 94 # mkisofs on s8 doesn't support functionality used by GRUB boot. 95 # Use ufs format for boot archive instead. 96 # 97 release=`uname -r` 98 if [ "$release" = "5.8" ]; then 99 format=ufs 100 fi 101 102 shift `expr $OPTIND - 1` 103 104 if [ $# -eq 1 ]; then 105 ALT_ROOT="$1" 106 echo "Creating boot_archive for $ALT_ROOT" 107 fi 108 109 case $PLATFORM in 110 i386) PLATFORM=i86pc 111 ISA=i386 112 ARCH64=amd64 113 ;; 114 i86pc) ISA=i386 115 ARCH64=amd64 116 ;; 117 sun4u) ISA=sparc 118 ARCH64=sparcv9 119 ;; 120 sun4v) ISA=sparc 121 ARCH64=sparcv9 122 ;; 123 *) usage 124 ;; 125 esac 126 127 BOOT_ARCHIVE=platform/$PLATFORM/boot_archive 128 BOOT_ARCHIVE_64=platform/$PLATFORM/$ARCH64/boot_archive 129 130 if [ $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 140 else # must be sparc 141 SPLIT=no # there's only 64-bit (sparcv9), so don't split 142 compress=no 143 fi 144 145 [ -x $GZIP_CMD ] || compress=no 146 147 function 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 157 function 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 # 194 function 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 # 237 function 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 -f "$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 if [ $? -ne 0 ] ; then 293 rm -f "${archive}-new" 294 fi 295 } 296 297 # 298 # The first argument can be: 299 # 300 # "both" - create an archive with both 32-bit and 64-bit binaries 301 # "32-bit" - create an archive with only 32-bit binaries 302 # "64-bit" - create an archive with only 64-bit binaries 303 # 304 function create_isofs 305 { 306 which=$1 307 archive=$2 308 309 # should we exclude amd64 binaries? 310 if [ "$which" = "32-bit" ]; then 311 rdmnt="$rdmnt32" 312 errlog="$errlog32" 313 list="$list32" 314 elif [ "$which" = "64-bit" ]; then 315 rdmnt="$rdmnt64" 316 errlog="$errlog64" 317 list="$list64" 318 else 319 rdmnt="$rdmnt32" 320 errlog="$errlog32" 321 list="$list32" 322 fi 323 324 # create image directory seed with graft points 325 mkdir "$rdmnt" 326 files= 327 isocmd="mkisofs -quiet -graft-points -dlrDJN -relaxed-filenames" 328 329 if [ $ISA = sparc ] ; then 330 bb="$ALT_ROOT/platform/$PLATFORM/lib/fs/hsfs/bootblk" 331 isocmd="$isocmd -G \"$bb\"" 332 fi 333 334 copy_files "$list" 335 isocmd="$isocmd \"$rdmnt\"" 336 rm -f "$errlog" 337 338 # 339 # Check if gzip exists in /usr/bin, so we only try to run gzip 340 # on systems that have gzip. Then run gzip out of the patch to 341 # pick it up from bfubin or something like that if needed. 342 # 343 # If compress is set, the individual files in the archive are 344 # compressed, and the final compression will accomplish very 345 # little. To save time, we skip the gzip in this case. 346 # 347 mkiso_ret=0 348 349 if [ $ISA = i386 ] &&[ $compress = no ] && [ -x $GZIP_CMD ] 350 then 351 ksh -c "$isocmd" 2> "$errlog" | \ 352 gzip > "${archive}-new" 353 else 354 ksh -c "$isocmd" 2> "$errlog" > "${archive}-new" 355 fi 356 357 if [ $? -ne 0 ]; then 358 cat "$errlog" 359 rm -f "${archive}-new" 2> /dev/null 360 rm -f "$errlog" 2> /dev/null 361 return 362 fi 363 364 dd_ret=0 365 if [ $ISA = sparc ] ; then 366 bb="$ALT_ROOT/platform/$PLATFORM/lib/fs/hsfs/bootblk" 367 dd if="$bb" of="${archive}-new" bs=1b oseek=1 count=15 \ 368 conv=notrunc conv=sync >> "$errlog" 2>&1 369 dd_ret=$? 370 fi 371 372 if [ -s "$errlog" ] || [ $dd_ret -ne 0 ] ; then 373 grep Error: "$errlog" >/dev/null 2>&1 374 if [ $? -eq 0 ] || [ $dd_ret -ne 0 ] ; then 375 cat "$errlog" 376 rm -f "${archive}-new" 377 fi 378 fi 379 rm -f "$errlog" 380 } 381 382 function create_archive 383 { 384 which=$1 385 archive=$2 386 lofidev=$3 387 388 echo "updating $archive" 389 390 if [ "$format" = "ufs" ]; then 391 create_ufs "$which" "$archive" "$lofidev" 392 else 393 create_isofs "$which" "$archive" 394 fi 395 396 # sanity check the archive before moving it into place 397 # 398 ARCHIVE_SIZE=`ls -l "${archive}-new" 2> /dev/null | nawk '{ print $5 }'` 399 if [ $compress = yes ] || [ $ISA = sparc ] ; then 400 # 401 # 'file' will report "English text" for uncompressed 402 # boot_archives. Checking for that doesn't seem stable, 403 # so we just check that the file exists. 404 # 405 ls "${archive}-new" >/dev/null 2>&1 406 else 407 # 408 # the file type check also establishes that the 409 # file exists at all 410 # 411 LC_MESSAGES=C file "${archive}-new" | grep gzip > /dev/null 412 fi 413 414 if [ $? = 1 ] && [ -x $GZIP_CMD ] || [ "$ARCHIVE_SIZE" -lt 10000 ] 415 then 416 # 417 # Two of these functions may be run in parallel. We 418 # need to allow the other to clean up, so we can't 419 # exit immediately. Instead, we set a flag. 420 # 421 echo "update of $archive failed" 422 ERROR=1 423 else 424 lockfs -f "/$ALT_ROOT" 2>/dev/null 425 mv "${archive}-new" "$archive" 426 lockfs -f "/$ALT_ROOT" 2>/dev/null 427 fi 428 429 } 430 431 function fatal_error 432 { 433 print -u2 $* 434 exit 1 435 } 436 437 # 438 # get filelist 439 # 440 if [ ! -f "$ALT_ROOT/boot/solaris/filelist.ramdisk" ] && 441 [ ! -f "$ALT_ROOT/etc/boot/solaris/filelist.ramdisk" ] 442 then 443 print -u2 "Can't find filelist.ramdisk" 444 exit 1 445 fi 446 filelist=$($EXTRACT_FILELIST $EXTRACT_ARGS \ 447 /boot/solaris/filelist.ramdisk \ 448 /etc/boot/solaris/filelist.ramdisk \ 449 2>/dev/null | sort -u) 450 451 # 452 # We use /tmp/ for scratch space now. This may be changed later if there 453 # is insufficient space in /tmp/. 454 # 455 rddir="/tmp/create_ramdisk.$$.tmp" 456 new_rddir= 457 rm -rf "$rddir" 458 mkdir "$rddir" || fatal_error "Could not create temporary directory $rddir" 459 460 # Clean up upon exit. 461 trap 'cleanup' EXIT 462 463 list32="$rddir/filelist.32" 464 list64="$rddir/filelist.64" 465 466 touch $list32 $list64 467 468 # 469 # This loop creates the 32-bit and 64-bit lists of files. The 32-bit list 470 # is written to stdout, which is redirected at the end of the loop. The 471 # 64-bit list is appended with each write. 472 # 473 cd "/$ALT_ROOT" 474 find $filelist -print 2>/dev/null | while read path 475 do 476 if [ $SPLIT = no ]; then 477 print "$path" 478 elif [ -d "$path" ]; then 479 if [ $format = ufs ]; then 480 size=`ls -lLd "$path" | nawk ' 481 {print ($5 % 1024) ? (int($5 / 1024) + 1) * 1024 : $5}'` 482 if [ `basename "$path"` != "amd64" ]; then 483 (( dirsize32 += size )) 484 fi 485 (( dirsize64 += size )) 486 fi 487 else 488 case `LC_MESSAGES=C /usr/bin/file -m /dev/null "$path" 2>/dev/null` in 489 *ELF\ 64-bit*) 490 print "$path" >> "$list64" 491 ;; 492 *ELF\ 32-bit*) 493 print "$path" 494 ;; 495 *) 496 # put in both lists 497 print "$path" 498 print "$path" >> "$list64" 499 esac 500 fi 501 done >"$list32" 502 503 if [ $format = ufs ] ; then 504 # calculate image size 505 getsize 506 507 # check to see if there is sufficient space in tmpfs 508 # 509 tmp_free=`df -b /tmp | tail -1 | awk '{ printf ($2) }'` 510 (( tmp_free = tmp_free / 3 )) 511 if [ $SPLIT = yes ]; then 512 (( tmp_free = tmp_free / 2 )) 513 fi 514 515 if [ $total_size -gt $tmp_free ] ; then 516 # assumes we have enough scratch space on $ALT_ROOT 517 new_rddir="/$ALT_ROOT/var/tmp/create_ramdisk.$$.tmp" 518 rm -rf "$new_rddir" 519 mkdir "$new_rddir" || fatal_error \ 520 "Could not create temporary directory $new_rddir" 521 522 # Save the file lists 523 mv "$list32" "$new_rddir"/ 524 mv "$list64" "$new_rddir"/ 525 list32="/$new_rddir/filelist.32" 526 list64="/$new_rddir/filelist.64" 527 528 # Remove the old $rddir and set the new value of rddir 529 rm -rf "$rddir" 530 rddir="$new_rddir" 531 new_rddir= 532 fi 533 fi 534 535 rdfile32="$rddir/rd.file.32" 536 rdfile64="$rddir/rd.file.64" 537 rdmnt32="$rddir/rd.mount.32" 538 rdmnt64="$rddir/rd.mount.64" 539 errlog32="$rddir/rd.errlog.32" 540 errlog64="$rddir/rd.errlog.64" 541 lofidev32="" 542 lofidev64="" 543 544 if [ $SPLIT = yes ]; then 545 # 546 # We can't run lofiadm commands in parallel, so we have to do 547 # them here. 548 # 549 if [ "$format" = "ufs" ]; then 550 mkfile ${size32}k "$rdfile32" 551 lofidev32=`lofiadm -a "$rdfile32"` 552 mkfile ${size64}k "$rdfile64" 553 lofidev64=`lofiadm -a "$rdfile64"` 554 fi 555 create_archive "32-bit" "$ALT_ROOT/$BOOT_ARCHIVE" $lofidev32 & 556 create_archive "64-bit" "$ALT_ROOT/$BOOT_ARCHIVE_64" $lofidev64 557 wait 558 if [ "$format" = "ufs" ]; then 559 lofiadm -d "$rdfile32" 560 lofiadm -d "$rdfile64" 561 fi 562 else 563 if [ "$format" = "ufs" ]; then 564 mkfile ${total_size}k "$rdfile32" 565 lofidev32=`lofiadm -a "$rdfile32"` 566 fi 567 create_archive "both" "$ALT_ROOT/$BOOT_ARCHIVE" $lofidev32 568 [ "$format" = "ufs" ] && lofiadm -d "$rdfile32" 569 fi 570 if [ $ERROR = 1 ]; then 571 cleanup 572 exit 1 573 fi 574 575 # 576 # For the diskless case, hardlink archive to /boot to make it 577 # visible via tftp. /boot is lofs mounted under /tftpboot/<hostname>. 578 # NOTE: this script must work on both client and server. 579 # 580 grep "[ ]/[ ]*nfs[ ]" "$ALT_ROOT/etc/vfstab" > /dev/null 581 if [ $? = 0 ]; then 582 rm -f "$ALT_ROOT/boot/boot_archive" "$ALT_ROOT/boot/amd64/boot_archive" 583 ln "$ALT_ROOT/$BOOT_ARCHIVE" "$ALT_ROOT/boot/boot_archive" 584 if [ $SPLIT = yes ]; then 585 ln "$ALT_ROOT/$BOOT_ARCHIVE_64" \ 586 "$ALT_ROOT/boot/amd64/boot_archive" 587 fi 588 fi 589 [ -n "$rddir" ] && rm -rf "$rddir"