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