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