xref: /titanic_52/usr/src/cmd/boot/scripts/create_ramdisk.ksh (revision ae115bc77f6fcde83175c75b4206dc2e50747966)
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=
30compress=yes
31SPLIT=unknown
32ERROR=0
33
34BOOT_ARCHIVE=platform/i86pc/boot_archive
35BOOT_ARCHIVE_64=platform/i86pc/amd64/boot_archive
36
37export PATH=$PATH:/usr/sbin:/usr/bin:/sbin
38
39#
40# Parse options
41#
42while [ "$1" != "" ]
43do
44        case $1 in
45        -R)	shift
46		ALT_ROOT="$1"
47		if [ "$ALT_ROOT" != "/" ]; then
48			echo "Creating ram disk for $ALT_ROOT"
49		fi
50		;;
51	-n|--nocompress) compress=no ;;
52        *)      echo Usage: ${0##*/}: [-R \<root\>] [--nocompress]
53		exit ;;
54        esac
55	shift
56done
57
58if [ -x /usr/bin/mkisofs -o -x /tmp/bfubin/mkisofs ] ; then
59	format=isofs
60fi
61
62#
63# mkisofs on s8 doesn't support functionality used by GRUB boot.
64# Use ufs format for boot archive instead.
65#
66release=`uname -r`
67if [ "$release" = "5.8" ]; then
68	format=ufs
69fi
70
71shift `expr $OPTIND - 1`
72
73if [ $# -eq 1 ]; then
74	ALT_ROOT="$1"
75	echo "Creating ram disk for $ALT_ROOT"
76fi
77
78rundir=`dirname $0`
79if [ ! -x $rundir/symdef ]; then
80	# Shouldn't happen
81	echo "Warning: $rundir/symdef not present."
82	echo "Creating single archive at $ALT_ROOT/platform/i86pc/boot_archive"
83	SPLIT=no
84	compress=no
85elif $rundir/symdef "$ALT_ROOT"/platform/i86pc/kernel/unix \
86    dboot_image 2>/dev/null; then
87	SPLIT=yes
88else
89	SPLIT=no
90	compress=no
91fi
92
93[ -x /usr/bin/gzip ] || compress=no
94
95function cleanup
96{
97	umount -f "$rdmnt32" 2>/dev/null
98	umount -f "$rdmnt64" 2>/dev/null
99	lofiadm -d "$rdfile32" 2>/dev/null
100	lofiadm -d "$rdfile64" 2>/dev/null
101	rm -fr "$rddir" 2> /dev/null
102}
103
104function getsize
105{
106	# Estimate image size and add %10 overhead for ufs stuff.
107	# Note, we can't use du here in case we're on a filesystem, e.g. zfs,
108	# in which the disk usage is less than the sum of the file sizes.
109	# The nawk code
110	#
111	#	{t += ($7 % 1024) ? (int($7 / 1024) + 1) * 1024 : $7}
112	#
113	# below rounds up the size of a file/directory, in bytes, to the
114	# next multiple of 1024.  This mimics the behavior of ufs especially
115	# with directories.  This results in a total size that's slightly
116	# bigger than if du was called on a ufs directory.
117	total_size=$(cd "/$ALT_ROOT"
118		find $filelist -ls 2>/dev/null | nawk '
119			{t += ($7 % 1024) ? (int($7 / 1024) + 1) * 1024 : $7}
120			END {print int(t * 1.10 / 1024)}')
121}
122
123#
124# The first argument can be:
125#
126# "both" - create an archive with both 32-bit and 64-bit binaries
127# "32-bit" - create an archive with only 32-bit binaries
128# "64-bit" - create an archive with only 64-bit binaries
129#
130function create_ufs
131{
132	which=$1
133	archive=$2
134	lofidev=$3
135
136	# should we exclude amd64 binaries?
137	if [ "$which" = "32-bit" ]; then
138		NO_AMD64="-type d -name amd64 -prune -o"
139		rdfile="$rdfile32"
140		rdmnt="$rdmnt32"
141	elif [ "$which" = "64-bit" ]; then
142		NO_AMD64=""
143		rdfile="$rdfile64"
144		rdmnt="$rdmnt64"
145	else
146		NO_AMD64=""
147		rdfile="$rdfile32"
148		rdmnt="$rdmnt32"
149	fi
150
151	newfs $lofidev < /dev/null 2> /dev/null
152	mkdir "$rdmnt"
153	mount -F mntfs mnttab /etc/mnttab > /dev/null 2>&1
154	mount -o nologging $lofidev "$rdmnt"
155	files=
156
157	# do the actual copy
158	cd "/$ALT_ROOT"
159
160	for path in `find $filelist $NO_AMD64 -type f -print 2> /dev/null`
161	do
162		if [ "$which" = "both" ]; then
163			files="$files $path"
164		else
165			filetype=`file $path 2>/dev/null |\
166			    awk '/ELF/ { print \$3 }'`
167			if [ -z "$filetype" ] || [ "$filetype" = "$which" ]
168			then
169				files="$files $path"
170			fi
171		fi
172	done
173	if [ $compress = yes ]; then
174		ls $files | while read path
175		do
176			dir="${path%/*}"
177			mkdir -p "$rdmnt/$dir"
178			/usr/bin/gzip -c "$path" > "$rdmnt/$path"
179		done
180	else
181		ls $files | cpio -pdum "$rdmnt" 2> /dev/null
182	fi
183	umount "$rdmnt"
184	rmdir "$rdmnt"
185
186	#
187	# Check if gzip exists in /usr/bin, so we only try to run gzip
188	# on systems that have gzip. Then run gzip out of the patch to
189	# pick it up from bfubin or something like that if needed.
190	#
191	# If compress is set, the individual files in the archive are
192	# compressed, and the final compression will accomplish very
193	# little.  To save time, we skip the gzip in this case.
194	#
195	if [ $compress = no ] && [ -x /usr/bin/gzip ] ; then
196		gzip -c "$rdfile" > "${archive}-new"
197	else
198		cat "$rdfile" > "${archive}-new"
199	fi
200}
201
202#
203# The first argument can be:
204#
205# "both" - create an archive with both 32-bit and 64-bit binaries
206# "32-bit" - create an archive with only 32-bit binaries
207# "64-bit" - create an archive with only 64-bit binaries
208#
209function create_isofs
210{
211	which=$1
212	archive=$2
213
214	# should we exclude amd64 binaries?
215	if [ "$which" = "32-bit" ]; then
216		NO_AMD64="-type d -name amd64 -prune -o"
217		rdmnt="$rdmnt32"
218		errlog="$errlog32"
219	elif [ "$which" = "64-bit" ]; then
220		NO_AMD64=""
221		rdmnt="$rdmnt64"
222		errlog="$errlog64"
223	else
224		NO_AMD64=""
225		rdmnt="$rdmnt32"
226		errlog="$errlog32"
227	fi
228
229	# create image directory seed with graft points
230	mkdir "$rdmnt"
231	files=
232	isocmd="mkisofs -quiet -graft-points -dlrDJN -relaxed-filenames"
233
234	cd "/$ALT_ROOT"
235	for path in `find $filelist $NO_AMD64 -type f -print 2> /dev/null`
236	do
237		if [ "$which" = "both" ]; then
238			files="$files $path"
239		else
240			filetype=`file $path 2>/dev/null |\
241			    awk '/ELF/ { print \$3 }'`
242			if [ -z "$filetype" ] || [ "$filetype" = "$which" ]
243			then
244				files="$files $path"
245			fi
246		fi
247	done
248	if [ $compress = yes ]; then
249		ls $files | while read path
250		do
251			dir="${path%/*}"
252			mkdir -p "$rdmnt/$dir"
253			/usr/bin/gzip -c "$path" > "$rdmnt/$path"
254		done
255	else
256		ls $files | cpio -pdum "$rdmnt" 2> /dev/null
257	fi
258	isocmd="$isocmd \"$rdmnt\""
259	rm -f "$errlog"
260
261	#
262	# Check if gzip exists in /usr/bin, so we only try to run gzip
263	# on systems that have gzip. Then run gzip out of the patch to
264	# pick it up from bfubin or something like that if needed.
265	#
266	# If compress is set, the individual files in the archive are
267	# compressed, and the final compression will accomplish very
268	# little.  To save time, we skip the gzip in this case.
269	#
270	if [ $compress = no ] && [ -x /usr/bin/gzip ] ; then
271		ksh -c "$isocmd" 2> "$errlog" | \
272		    gzip > "${archive}-new"
273	else
274		ksh -c "$isocmd" 2> "$errlog" > "${archive}-new"
275	fi
276
277	if [ -s "$errlog" ]; then
278		grep Error: "$errlog" >/dev/null 2>&1
279		if [ $? -eq 0 ]; then
280			grep Error: "$errlog"
281			rm -f "${archive}-new"
282		fi
283	fi
284	rm -f "$errlog"
285}
286
287function create_archive
288{
289	which=$1
290	archive=$2
291	lofidev=$3
292
293	echo "updating $archive...this may take a minute"
294
295	if [ "$format" = "ufs" ]; then
296		create_ufs "$which" "$archive" "$lofidev"
297	else
298		create_isofs "$which" "$archive"
299	fi
300
301	# sanity check the archive before moving it into place
302	#
303	ARCHIVE_SIZE=`du -k "${archive}-new" | cut -f 1`
304	if [ $compress = yes ]
305	then
306		#
307		# 'file' will report "English text" for uncompressed
308		# boot_archives.  Checking for that doesn't seem stable,
309		# so we just check that the file exists.
310		#
311		ls "${archive}-new" >/dev/null 2>&1
312	else
313		#
314		# the file type check also establishes that the
315		# file exists at all
316		#
317		file "${archive}-new" | grep gzip > /dev/null
318	fi
319
320	if [ $? = 1 ] && [ -x /usr/bin/gzip ] || [ $ARCHIVE_SIZE -lt 5000 ]
321	then
322		#
323		# Two of these functions may be run in parallel.  We
324		# need to allow the other to clean up, so we can't
325		# exit immediately.  Instead, we set a flag.
326		#
327		echo "update of $archive failed"
328		ERROR=1
329	else
330		lockfs -f "/$ALT_ROOT" 2>/dev/null
331		mv "${archive}-new" "$archive"
332		lockfs -f "/$ALT_ROOT" 2>/dev/null
333	fi
334
335}
336
337#
338# get filelist
339#
340files=$(ls "$ALT_ROOT/boot/solaris/filelist.ramdisk" \
341	"$ALT_ROOT/etc/boot/solaris/filelist.ramdisk" 2>/dev/null)
342if [[ -z "$files" ]]
343then
344	print -u2 "Can't find filelist.ramdisk"
345	exit 1
346fi
347filelist=$(sort -u $files)
348
349scratch=tmp
350
351if [ $format = ufs ] ; then
352	# calculate image size
353	getsize
354
355	# We do two mkfile's of total_size, so double the space
356	(( tmp_needed = total_size * 2 ))
357
358	# check to see if there is sufficient space in tmpfs
359	#
360	tmp_free=`df -b /tmp | tail -1 | awk '{ printf ($2) }'`
361	(( tmp_free = tmp_free / 2 ))
362
363	if [ $tmp_needed -gt $tmp_free  ] ; then
364		# assumes we have enough scratch space on $ALT_ROOT
365        	scratch="$ALT_ROOT"
366	fi
367fi
368
369rddir="/$scratch/create_ramdisk.$$.tmp"
370rdfile32="$rddir/rd.file.32"
371rdfile64="$rddir/rd.file.64"
372rdmnt32="$rddir/rd.mount.32"
373rdmnt64="$rddir/rd.mount.64"
374errlog32="$rddir/rd.errlog.32"
375errlog64="$rddir/rd.errlog.64"
376lofidev32=""
377lofidev64=""
378
379# make directory for temp files safely
380rm -rf "$rddir"
381mkdir "$rddir"
382
383# Clean up upon exit.
384trap 'cleanup' EXIT
385
386if [ $SPLIT = yes ]; then
387	#
388	# We can't run lofiadm commands in parallel, so we have to do
389	# them here.
390	#
391	if [ "$format" = "ufs" ]; then
392		mkfile ${total_size}k "$rdfile32"
393		lofidev32=`lofiadm -a "$rdfile32"`
394		mkfile ${total_size}k "$rdfile64"
395		lofidev64=`lofiadm -a "$rdfile64"`
396	fi
397	create_archive "32-bit" "$ALT_ROOT/$BOOT_ARCHIVE" $lofidev32 &
398	create_archive "64-bit" "$ALT_ROOT/$BOOT_ARCHIVE_64" $lofidev64
399	wait
400	if [ "$format" = "ufs" ]; then
401		lofiadm -d "$rdfile32"
402		lofiadm -d "$rdfile64"
403	fi
404else
405	if [ "$format" = "ufs" ]; then
406		mkfile ${total_size}k "$rdfile32"
407		lofidev32=`lofiadm -a "$rdfile32"`
408	fi
409	create_archive "both" "$ALT_ROOT/$BOOT_ARCHIVE" $lofidev32
410	[ "$format" = "ufs" ] && lofiadm -d "$rdfile32"
411fi
412if [ $ERROR = 1 ]; then
413	cleanup
414	exit 1
415fi
416
417#
418# For the diskless case, hardlink archive to /boot to make it
419# visible via tftp. /boot is lofs mounted under /tftpboot/<hostname>.
420# NOTE: this script must work on both client and server.
421#
422grep "[	 ]/[	 ]*nfs[	 ]" "$ALT_ROOT/etc/vfstab" > /dev/null
423if [ $? = 0 ]; then
424	rm -f "$ALT_ROOT/boot/boot_archive" "$ALT_ROOT/boot/amd64/boot_archive"
425	ln "$ALT_ROOT/$BOOT_ARCHIVE" "$ALT_ROOT/boot/boot_archive"
426	ln "$ALT_ROOT/$BOOT_ARCHIVE_64" "$ALT_ROOT/boot/amd64/boot_archive"
427fi
428rm -rf "$rddir"
429