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 create_ufs_archive 225{ 226 typeset archive="$ALT_ROOT/$BOOT_ARCHIVE" 227 228 [ "$compress" = yes ] && \ 229 echo "updating $archive (UFS)" || \ 230 echo "updating $archive (UFS-nocompress)" 231 232 # 233 # We use /tmp/ for scratch space now. This will be changed later to 234 # $ALT_ROOT/var/tmp if there is insufficient space in /tmp/. 235 # 236 rddir="/tmp/create_ramdisk.$$.tmp" 237 new_rddir= 238 rm -rf "$rddir" 239 mkdir "$rddir" || fatal_error "Could not create directory $rddir" 240 241 # Clean up upon exit. 242 trap 'ufs_cleanup' EXIT 243 244 list="$rddir/filelist" 245 246 cd "/$ALT_ROOT" || fatal_error "Cannot chdir to $ALT_ROOT" 247 find $filelist -print 2>/dev/null | while read path; do 248 if [ -d "$path" ]; then 249 size=`ls -lLd "$path" | nawk ' 250 {print ($5 % 1024) ? (int($5 / 1024) + 1) * 1024 : $5}'` 251 (( dirsize += size / 1024 )) 252 else 253 print "$path" 254 fi 255 done >"$list" 256 257 # calculate image size 258 ufs_getsize 259 260 # check to see if there is sufficient space in tmpfs 261 # 262 tmp_free=`df -b /tmp | tail -1 | awk '{ print $2 }'` 263 (( tmp_free = tmp_free / 3 )) 264 265 if [ $total_size -gt $tmp_free ] ; then 266 echo "Insufficient space in /tmp, using $ALT_ROOT/var/tmp" 267 # assumes we have enough scratch space on $ALT_ROOT 268 new_rddir="/$ALT_ROOT/var/tmp/create_ramdisk.$$.tmp" 269 rm -rf "$new_rddir" 270 mkdir "$new_rddir" || fatal_error \ 271 "Could not create temporary directory $new_rddir" 272 273 # Save the file lists 274 mv "$list" "$new_rddir"/ 275 list="/$new_rddir/filelist" 276 277 # Remove the old $rddir and set the new value of rddir 278 rm -rf "$rddir" 279 rddir="$new_rddir" 280 new_rddir= 281 fi 282 283 rdfile="$rddir/rd.file" 284 rdmnt="$rddir/rd.mount" 285 errlog="$rddir/rd.errlog" 286 lofidev="" 287 288 mkfile ${total_size}k "$rdfile" || \ 289 fatal_error "Could not create backing file" 290 lofidev=`lofiadm -a "$rdfile"` || \ 291 fatal_error "Could not create lofi device" 292 293 NOINUSE_CHECK=1 newfs -m 0 $lofidev < /dev/null 2> /dev/null 294 mkdir "$rdmnt" 295 mount -F mntfs mnttab /etc/mnttab > /dev/null 2>&1 296 mount -F ufs -o nologging $lofidev "$rdmnt" 297 rm -rf "$rdmnt/lost+found" 298 299 # do the actual copy 300 copy_files "$list" 301 umount -f "$rdmnt" 302 rmdir "$rdmnt" 303 304 if [ $ISA = sparc ] ; then 305 rlofidev="${lofidev/lofi/rlofi}" 306 bb="/$ALT_ROOT/platform/$PLATFORM/lib/fs/ufs/bootblk" 307 # installboot is not available on all platforms 308 dd if=$bb of=$rlofidev bs=1b oseek=1 count=15 conv=sync 2>&1 309 fi 310 311 lofiadm -d "$rdfile" 312 313 # 314 # Check if gzip exists in /usr/bin, so we only try to run gzip 315 # on systems that have gzip. Then run gzip out of the patch to 316 # pick it up from bfubin or something like that if needed. 317 # 318 # If compress is set, the individual files in the archive are 319 # compressed, and the final compression will accomplish very 320 # little. To save time, we skip the gzip in this case. 321 # 322 if [ $ISA = i386 ] && [ $compress = no ] && [ -x $GZIP_CMD ] ; then 323 $GZIP_CMD -c "$rdfile" > "${archive}-new" 324 else 325 cat "$rdfile" > "${archive}-new" 326 fi 327 328 if [ $? -ne 0 ] ; then 329 rm -f "${archive}-new" 330 fi 331 332 # sanity check the archive before moving it into place 333 # 334 ARCHIVE_SIZE=`ls -l "${archive}-new" 2> /dev/null | nawk '{ print $5 }'` 335 if [ $compress = yes ] || [ $ISA = sparc ] ; then 336 # 337 # 'file' will report "English text" for uncompressed 338 # boot_archives. Checking for that doesn't seem stable, 339 # so we just check that the file exists. 340 # 341 ls "${archive}-new" >/dev/null 2>&1 342 else 343 # 344 # the file type check also establishes that the 345 # file exists at all 346 # 347 LC_MESSAGES=C file "${archive}-new" | grep gzip > /dev/null 348 fi 349 350 if [ $? = 1 ] && [ -x $GZIP_CMD ] || [ "$ARCHIVE_SIZE" -lt 10000 ] 351 then 352 fatal_error "update of $archive failed" 353 else 354 lockfs -f "/$ALT_ROOT" 2>/dev/null 355 rm -f "$archive.hash" 356 mv "${archive}-new" "$archive" 357 digest -a sha1 "$rdfile" > "$archive.hash" 358 lockfs -f "/$ALT_ROOT" 2>/dev/null 359 fi 360 [ -n "$rddir" ] && rm -rf "$rddir" 361} 362 363function cpio_cleanup 364{ 365 rm -f "$tarchive" "$tarchive.cpio" "$tarchive.hash" 366} 367 368function create_cpio_archive 369{ 370 typeset archive="$ALT_ROOT/$BOOT_ARCHIVE" 371 372 echo "updating $archive (CPIO)" 373 374 tarchive="$archive.$$.new" 375 376 # Clean up upon exit. 377 trap 'cpio_cleanup' EXIT 378 379 cd "/$ALT_ROOT" || fatal_error "Cannot chdir to $ALT_ROOT" 380 381 touch "$tarchive" \ 382 || fatal_error "Cannot create temporary archive $tarchive" 383 384 find $filelist 2>/dev/null | cpio -qo -H odc > "$tarchive.cpio" \ 385 || fatal_error "Problem creating archive" 386 387 [ -x /usr/bin/digest ] \ 388 && /usr/bin/digest -a sha1 "$tarchive.cpio" \ 389 > "$tarchive.hash" 390 391 if [ -x "$GZIP_CMD" ]; then 392 $GZIP_CMD -c "$tarchive.cpio" > "$tarchive" 393 rm -f "$tarchive.cpio" 394 else 395 mv "$tarchive.cpio" "$tarchive" 396 fi 397 398 # Move new archive into place 399 [ -f "$archive.hash" ] && rm -f "$archive.hash" 400 mv "$tarchive" "$archive" 401 [ $? -eq 0 -a -f "$tarchive.hash" ] \ 402 && mv "$tarchive.hash" "$archive.hash" 403} 404 405# 406# get filelist 407# 408if [ ! -f "$ALT_ROOT/boot/solaris/filelist.ramdisk" ] && 409 [ ! -f "$ALT_ROOT/etc/boot/solaris/filelist.ramdisk" ] 410then 411 print -u2 "Can't find filelist.ramdisk" 412 exit 1 413fi 414filelist=$($EXTRACT_FILELIST $EXTRACT_ARGS \ 415 /boot/solaris/filelist.ramdisk \ 416 /etc/boot/solaris/filelist.ramdisk \ 417 2>/dev/null | sort -u) 418 419# Now that we have the list of files, we can create the archive. 420 421case "$FORMAT" in 422 cpio) create_cpio_archive ;; 423 ufs) create_ufs_archive ;; 424 *) print -u2 "Unknown boot archive format, $FORMAT" 425 exit 1 426 ;; 427esac 428 429# 430# For the diskless case, hardlink archive to /boot to make it 431# visible via tftp. /boot is lofs mounted under /tftpboot/<hostname>. 432# NOTE: this script must work on both client and server. 433# 434grep "[ ]/[ ]*nfs[ ]" "$ALT_ROOT/etc/vfstab" > /dev/null 435if [ $? = 0 ]; then 436 rm -f "$ALT_ROOT/boot/$BOOT_ARCHIVE_SUFFIX" 437 mkdir -p "$ALT_ROOT/boot/`dirname $BOOT_ARCHIVE_SUFFIX`" 438 ln "$ALT_ROOT/$BOOT_ARCHIVE" "$ALT_ROOT/boot/$BOOT_ARCHIVE_SUFFIX" 439fi 440