xref: /titanic_44/usr/src/cmd/boot/scripts/create_ramdisk.ksh (revision da14cebe459d3275048785f25bd869cb09b5307f)
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 2008 Sun Microsystems, Inc.  All rights reserved.
24# Use is subject to license terms.
25
26format=ufs
27ALT_ROOT=
28EXTRACT_ARGS=
29compress=yes
30SPLIT=unknown
31ERROR=0
32dirsize32=0
33dirsize64=0
34
35usage() {
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
46PLATFORM=`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#
52if [ "`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
56else
57	export PATH=/usr/sbin:/usr/bin:/sbin
58	export GZIP_CMD=/usr/bin/gzip
59fi
60
61EXTRACT_FILELIST="/boot/solaris/bin/extract_boot_filelist"
62
63#
64# Parse options
65#
66while [ "$1" != "" ]
67do
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
87done
88
89if [ -x /usr/bin/mkisofs -o -x /tmp/bfubin/mkisofs ] ; then
90	format=isofs
91fi
92
93#
94# mkisofs on s8 doesn't support functionality used by GRUB boot.
95# Use ufs format for boot archive instead.
96#
97release=`uname -r`
98if [ "$release" = "5.8" ]; then
99	format=ufs
100fi
101
102shift `expr $OPTIND - 1`
103
104if [ $# -eq 1 ]; then
105	ALT_ROOT="$1"
106	echo "Creating boot_archive for $ALT_ROOT"
107fi
108
109case $PLATFORM in
110i386)	PLATFORM=i86pc
111	ISA=i386
112	ARCH64=amd64
113	;;
114i86pc)	ISA=i386
115	ARCH64=amd64
116	;;
117sun4u)	ISA=sparc
118	ARCH64=sparcv9
119	;;
120sun4v)	ISA=sparc
121	ARCH64=sparcv9
122	;;
123*)	usage
124	;;
125esac
126
127BOOT_ARCHIVE=platform/$PLATFORM/boot_archive
128BOOT_ARCHIVE_64=platform/$PLATFORM/$ARCH64/boot_archive
129
130if [ $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
140else			# must be sparc
141	SPLIT=no	# there's only 64-bit (sparcv9), so don't split
142	compress=no
143fi
144
145[ -x $GZIP_CMD ] || compress=no
146
147function 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
157function 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 nawk 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		nawk '{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		nawk '{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 | nawk '{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#
194function 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#
237function 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 "$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
293#
294# The first argument can be:
295#
296# "both" - create an archive with both 32-bit and 64-bit binaries
297# "32-bit" - create an archive with only 32-bit binaries
298# "64-bit" - create an archive with only 64-bit binaries
299#
300function create_isofs
301{
302	which=$1
303	archive=$2
304
305	# should we exclude amd64 binaries?
306	if [ "$which" = "32-bit" ]; then
307		rdmnt="$rdmnt32"
308		errlog="$errlog32"
309		list="$list32"
310	elif [ "$which" = "64-bit" ]; then
311		rdmnt="$rdmnt64"
312		errlog="$errlog64"
313		list="$list64"
314	else
315		rdmnt="$rdmnt32"
316		errlog="$errlog32"
317		list="$list32"
318	fi
319
320	# create image directory seed with graft points
321	mkdir "$rdmnt"
322	files=
323	isocmd="mkisofs -quiet -graft-points -dlrDJN -relaxed-filenames"
324
325	if [ $ISA = sparc ] ; then
326		bb="$ALT_ROOT/platform/$PLATFORM/lib/fs/hsfs/bootblk"
327		isocmd="$isocmd -G \"$bb\""
328	fi
329
330	copy_files "$list"
331	isocmd="$isocmd \"$rdmnt\""
332	rm -f "$errlog"
333
334	#
335	# Check if gzip exists in /usr/bin, so we only try to run gzip
336	# on systems that have gzip. Then run gzip out of the patch to
337	# pick it up from bfubin or something like that if needed.
338	#
339	# If compress is set, the individual files in the archive are
340	# compressed, and the final compression will accomplish very
341	# little.  To save time, we skip the gzip in this case.
342	#
343	if [ $ISA = i386 ] &&[ $compress = no ] && [ -x $GZIP_CMD ]
344	then
345		ksh -c "$isocmd" 2> "$errlog" | \
346		    gzip > "${archive}-new"
347	else
348		ksh -c "$isocmd" 2> "$errlog" > "${archive}-new"
349	fi
350
351	dd_ret=0
352	if [ $ISA = sparc ] ; then
353		bb="$ALT_ROOT/platform/$PLATFORM/lib/fs/hsfs/bootblk"
354		dd if="$bb" of="${archive}-new" bs=1b oseek=1 count=15 \
355		    conv=notrunc conv=sync >> "$errlog" 2>&1
356		dd_ret=$?
357	fi
358
359	if [ -s "$errlog" ] || [ $dd_ret -ne 0 ] ; then
360		grep Error: "$errlog" >/dev/null 2>&1
361		if [ $? -eq 0 ] || [ $dd_ret -ne 0 ] ; then
362			cat "$errlog"
363			rm -f "${archive}-new"
364		fi
365	fi
366	rm -f "$errlog"
367}
368
369function create_archive
370{
371	which=$1
372	archive=$2
373	lofidev=$3
374
375	echo "updating $archive"
376
377	if [ "$format" = "ufs" ]; then
378		create_ufs "$which" "$archive" "$lofidev"
379	else
380		create_isofs "$which" "$archive"
381	fi
382
383	# sanity check the archive before moving it into place
384	#
385	ARCHIVE_SIZE=`ls -l "${archive}-new" | nawk '{ print $5 }'`
386	if [ $compress = yes ] || [ $ISA = sparc ] ; then
387		#
388		# 'file' will report "English text" for uncompressed
389		# boot_archives.  Checking for that doesn't seem stable,
390		# so we just check that the file exists.
391		#
392		ls "${archive}-new" >/dev/null 2>&1
393	else
394		#
395		# the file type check also establishes that the
396		# file exists at all
397		#
398		LC_MESSAGES=C file "${archive}-new" | grep gzip > /dev/null
399	fi
400
401	if [ $? = 1 ] && [ -x $GZIP_CMD ] || [ $ARCHIVE_SIZE -lt 5000 ]
402	then
403		#
404		# Two of these functions may be run in parallel.  We
405		# need to allow the other to clean up, so we can't
406		# exit immediately.  Instead, we set a flag.
407		#
408		echo "update of $archive failed"
409		ERROR=1
410	else
411		lockfs -f "/$ALT_ROOT" 2>/dev/null
412		mv "${archive}-new" "$archive"
413		lockfs -f "/$ALT_ROOT" 2>/dev/null
414	fi
415
416}
417
418function fatal_error
419{
420	print -u2 $*
421	exit 1
422}
423
424#
425# get filelist
426#
427if [ ! -f "$ALT_ROOT/boot/solaris/filelist.ramdisk" ] &&
428    [ ! -f "$ALT_ROOT/etc/boot/solaris/filelist.ramdisk" ]
429then
430	print -u2 "Can't find filelist.ramdisk"
431	exit 1
432fi
433filelist=$($EXTRACT_FILELIST $EXTRACT_ARGS \
434	/boot/solaris/filelist.ramdisk \
435	/etc/boot/solaris/filelist.ramdisk \
436		2>/dev/null | sort -u)
437
438#
439# We use /tmp/ for scratch space now.  This may be changed later if there
440# is insufficient space in /tmp/.
441#
442rddir="/tmp/create_ramdisk.$$.tmp"
443new_rddir=
444rm -rf "$rddir"
445mkdir "$rddir" || fatal_error "Could not create temporary directory $rddir"
446
447# Clean up upon exit.
448trap 'cleanup' EXIT
449
450list32="$rddir/filelist.32"
451list64="$rddir/filelist.64"
452
453touch $list32 $list64
454
455#
456# This loop creates the 32-bit and 64-bit lists of files.  The 32-bit list
457# is written to stdout, which is redirected at the end of the loop.  The
458# 64-bit list is appended with each write.
459#
460cd "/$ALT_ROOT"
461find $filelist -print 2>/dev/null | while read path
462do
463	if [ $SPLIT = no ]; then
464		print "$path"
465	elif [ -d "$path" ]; then
466		if [ $format = ufs ]; then
467			size=`ls -lLd "$path" | nawk '
468			    {print ($5 % 1024) ? (int($5 / 1024) + 1) * 1024 : $5}'`
469			if [ `basename "$path"` != "amd64" ]; then
470				(( dirsize32 += size ))
471			fi
472			(( dirsize64 += size ))
473		fi
474	else
475		case `LC_MESSAGES=C /usr/bin/file -m /dev/null "$path" 2>/dev/null` in
476		*ELF\ 64-bit*)
477			print "$path" >> "$list64"
478			;;
479		*ELF\ 32-bit*)
480			print "$path"
481			;;
482		*)
483			# put in both lists
484			print "$path"
485			print "$path" >> "$list64"
486		esac
487	fi
488done >"$list32"
489
490if [ $format = ufs ] ; then
491	# calculate image size
492	getsize
493
494	# check to see if there is sufficient space in tmpfs
495	#
496	tmp_free=`df -b /tmp | tail -1 | awk '{ printf ($2) }'`
497	(( tmp_free = tmp_free / 3 ))
498	if [ $SPLIT = yes ]; then
499		(( tmp_free = tmp_free / 2 ))
500	fi
501
502	if [ $total_size -gt $tmp_free  ] ; then
503		# assumes we have enough scratch space on $ALT_ROOT
504		new_rddir="/$ALT_ROOT/var/tmp/create_ramdisk.$$.tmp"
505		rm -rf "$new_rddir"
506		mkdir "$new_rddir" || fatal_error \
507		    "Could not create temporary directory $new_rddir"
508
509		# Save the file lists
510		mv "$list32" "$new_rddir"/
511		mv "$list64" "$new_rddir"/
512		list32="/$new_rddir/filelist.32"
513		list64="/$new_rddir/filelist.64"
514
515		# Remove the old $rddir and set the new value of rddir
516		rm -rf "$rddir"
517		rddir="$new_rddir"
518		new_rddir=
519	fi
520fi
521
522rdfile32="$rddir/rd.file.32"
523rdfile64="$rddir/rd.file.64"
524rdmnt32="$rddir/rd.mount.32"
525rdmnt64="$rddir/rd.mount.64"
526errlog32="$rddir/rd.errlog.32"
527errlog64="$rddir/rd.errlog.64"
528lofidev32=""
529lofidev64=""
530
531if [ $SPLIT = yes ]; then
532	#
533	# We can't run lofiadm commands in parallel, so we have to do
534	# them here.
535	#
536	if [ "$format" = "ufs" ]; then
537		mkfile ${size32}k "$rdfile32"
538		lofidev32=`lofiadm -a "$rdfile32"`
539		mkfile ${size64}k "$rdfile64"
540		lofidev64=`lofiadm -a "$rdfile64"`
541	fi
542	create_archive "32-bit" "$ALT_ROOT/$BOOT_ARCHIVE" $lofidev32 &
543	create_archive "64-bit" "$ALT_ROOT/$BOOT_ARCHIVE_64" $lofidev64
544	wait
545	if [ "$format" = "ufs" ]; then
546		lofiadm -d "$rdfile32"
547		lofiadm -d "$rdfile64"
548	fi
549else
550	if [ "$format" = "ufs" ]; then
551		mkfile ${total_size}k "$rdfile32"
552		lofidev32=`lofiadm -a "$rdfile32"`
553	fi
554	create_archive "both" "$ALT_ROOT/$BOOT_ARCHIVE" $lofidev32
555	[ "$format" = "ufs" ] && lofiadm -d "$rdfile32"
556fi
557if [ $ERROR = 1 ]; then
558	cleanup
559	exit 1
560fi
561
562#
563# For the diskless case, hardlink archive to /boot to make it
564# visible via tftp. /boot is lofs mounted under /tftpboot/<hostname>.
565# NOTE: this script must work on both client and server.
566#
567grep "[	 ]/[	 ]*nfs[	 ]" "$ALT_ROOT/etc/vfstab" > /dev/null
568if [ $? = 0 ]; then
569	rm -f "$ALT_ROOT/boot/boot_archive" "$ALT_ROOT/boot/amd64/boot_archive"
570	ln "$ALT_ROOT/$BOOT_ARCHIVE" "$ALT_ROOT/boot/boot_archive"
571	if [ $SPLIT = yes ]; then
572		ln "$ALT_ROOT/$BOOT_ARCHIVE_64" \
573		    "$ALT_ROOT/boot/amd64/boot_archive"
574	fi
575fi
576[ -n "$rddir" ] && rm -rf "$rddir"
577