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