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