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