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