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 awk 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                 /usr/xpg4/bin/awk '{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                 /usr/xpg4/bin/awk '{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 | /usr/xpg4/bin/awk '{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 | /usr/xpg4/bin/awk '{ 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" | /usr/xpg4/bin/awk '
 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"