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