xref: /illumos-gate/usr/src/cmd/boot/scripts/create_ramdisk.ksh (revision 168c213023b7f347f11abfc72f448b0c621ab718)
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# Copies all desired files to a target directory.
127#
128# This function depends on several variables that must be set before calling:
129# $ALT_ROOT - the target directory
130# $filelist - the list of files and directories to search
131# $NO_AMD64 - the find(1) expression to exclude files, if desired
132# $which - One of "both", "32-bit", or "64-bit"
133# $compress - whether or not the files in the archives should be compressed
134# $rdmnt - the target directory
135#
136function find_and_copy
137{
138	cd "/$ALT_ROOT"
139
140	#
141	# If compress is set, the files are gzip'd and put in the correct
142	# location in the loop.  Nothing is printed, so the pipe and cpio
143	# at the end is a nop.
144	#
145	# If compress is not set, the file names are printed, which causes
146	# the cpio at the end to do the copy.
147	#
148	find $filelist $NO_AMD64 -type f -print 2>/dev/null | while read path
149	do
150		if [ "$which" = "both" ]; then
151			if [ $compress = yes ]; then
152				dir="${path%/*}"
153				mkdir -p "$rdmnt/$dir"
154				/usr/bin/gzip -c "$path" > "$rdmnt/$path"
155			else
156				print "$path"
157			fi
158		else
159			filetype=`LC_MESSAGES=C file $path 2>/dev/null |\
160			    awk '/ELF/ { print \$3 }'`
161			if [ -z "$filetype" ] || [ "$filetype" = "$which" ]
162			then
163				if [ $compress = yes ]; then
164					dir="${path%/*}"
165					mkdir -p "$rdmnt/$dir"
166					/usr/bin/gzip -c "$path" > \
167					    "$rdmnt/$path"
168				else
169					print "$path"
170				fi
171			fi
172		fi
173	done | cpio -pdum "$rdmnt" 2>/dev/null
174}
175
176#
177# The first argument can be:
178#
179# "both" - create an archive with both 32-bit and 64-bit binaries
180# "32-bit" - create an archive with only 32-bit binaries
181# "64-bit" - create an archive with only 64-bit binaries
182#
183function create_ufs
184{
185	which=$1
186	archive=$2
187	lofidev=$3
188
189	# should we exclude amd64 binaries?
190	if [ "$which" = "32-bit" ]; then
191		NO_AMD64="-type d -name amd64 -prune -o"
192		rdfile="$rdfile32"
193		rdmnt="$rdmnt32"
194	elif [ "$which" = "64-bit" ]; then
195		NO_AMD64=""
196		rdfile="$rdfile64"
197		rdmnt="$rdmnt64"
198	else
199		NO_AMD64=""
200		rdfile="$rdfile32"
201		rdmnt="$rdmnt32"
202	fi
203
204	newfs $lofidev < /dev/null 2> /dev/null
205	mkdir "$rdmnt"
206	mount -F mntfs mnttab /etc/mnttab > /dev/null 2>&1
207	mount -o nologging $lofidev "$rdmnt"
208	files=
209
210	# do the actual copy
211	find_and_copy
212	umount "$rdmnt"
213	rmdir "$rdmnt"
214
215	#
216	# Check if gzip exists in /usr/bin, so we only try to run gzip
217	# on systems that have gzip. Then run gzip out of the patch to
218	# pick it up from bfubin or something like that if needed.
219	#
220	# If compress is set, the individual files in the archive are
221	# compressed, and the final compression will accomplish very
222	# little.  To save time, we skip the gzip in this case.
223	#
224	if [ $compress = no ] && [ -x /usr/bin/gzip ] ; then
225		gzip -c "$rdfile" > "${archive}-new"
226	else
227		cat "$rdfile" > "${archive}-new"
228	fi
229}
230
231#
232# The first argument can be:
233#
234# "both" - create an archive with both 32-bit and 64-bit binaries
235# "32-bit" - create an archive with only 32-bit binaries
236# "64-bit" - create an archive with only 64-bit binaries
237#
238function create_isofs
239{
240	which=$1
241	archive=$2
242
243	# should we exclude amd64 binaries?
244	if [ "$which" = "32-bit" ]; then
245		NO_AMD64="-type d -name amd64 -prune -o"
246		rdmnt="$rdmnt32"
247		errlog="$errlog32"
248	elif [ "$which" = "64-bit" ]; then
249		NO_AMD64=""
250		rdmnt="$rdmnt64"
251		errlog="$errlog64"
252	else
253		NO_AMD64=""
254		rdmnt="$rdmnt32"
255		errlog="$errlog32"
256	fi
257
258	# create image directory seed with graft points
259	mkdir "$rdmnt"
260	files=
261	isocmd="mkisofs -quiet -graft-points -dlrDJN -relaxed-filenames"
262
263	find_and_copy
264	isocmd="$isocmd \"$rdmnt\""
265	rm -f "$errlog"
266
267	#
268	# Check if gzip exists in /usr/bin, so we only try to run gzip
269	# on systems that have gzip. Then run gzip out of the patch to
270	# pick it up from bfubin or something like that if needed.
271	#
272	# If compress is set, the individual files in the archive are
273	# compressed, and the final compression will accomplish very
274	# little.  To save time, we skip the gzip in this case.
275	#
276	if [ $compress = no ] && [ -x /usr/bin/gzip ] ; then
277		ksh -c "$isocmd" 2> "$errlog" | \
278		    gzip > "${archive}-new"
279	else
280		ksh -c "$isocmd" 2> "$errlog" > "${archive}-new"
281	fi
282
283	if [ -s "$errlog" ]; then
284		grep Error: "$errlog" >/dev/null 2>&1
285		if [ $? -eq 0 ]; then
286			grep Error: "$errlog"
287			rm -f "${archive}-new"
288		fi
289	fi
290	rm -f "$errlog"
291}
292
293function create_archive
294{
295	which=$1
296	archive=$2
297	lofidev=$3
298
299	echo "updating $archive...this may take a minute"
300
301	if [ "$format" = "ufs" ]; then
302		create_ufs "$which" "$archive" "$lofidev"
303	else
304		create_isofs "$which" "$archive"
305	fi
306
307	# sanity check the archive before moving it into place
308	#
309	ARCHIVE_SIZE=`ls -l "${archive}-new" | nawk '{ print $5 }'`
310	if [ $compress = yes ]
311	then
312		#
313		# 'file' will report "English text" for uncompressed
314		# boot_archives.  Checking for that doesn't seem stable,
315		# so we just check that the file exists.
316		#
317		ls "${archive}-new" >/dev/null 2>&1
318	else
319		#
320		# the file type check also establishes that the
321		# file exists at all
322		#
323		LC_MESSAGES=C file "${archive}-new" | grep gzip > /dev/null
324	fi
325
326	if [ $? = 1 ] && [ -x /usr/bin/gzip ] || [ $ARCHIVE_SIZE -lt 5000 ]
327	then
328		#
329		# Two of these functions may be run in parallel.  We
330		# need to allow the other to clean up, so we can't
331		# exit immediately.  Instead, we set a flag.
332		#
333		echo "update of $archive failed"
334		ERROR=1
335	else
336		lockfs -f "/$ALT_ROOT" 2>/dev/null
337		mv "${archive}-new" "$archive"
338		lockfs -f "/$ALT_ROOT" 2>/dev/null
339	fi
340
341}
342
343#
344# get filelist
345#
346if [ ! -f "$ALT_ROOT/boot/solaris/filelist.ramdisk" ] &&
347    [ ! -f "$ALT_ROOT/etc/boot/solaris/filelist.ramdisk" ]
348then
349	print -u2 "Can't find filelist.ramdisk"
350	exit 1
351fi
352filelist=$(cat "$ALT_ROOT/boot/solaris/filelist.ramdisk" \
353    "$ALT_ROOT/etc/boot/solaris/filelist.ramdisk" 2>/dev/null | sort -u)
354
355scratch=tmp
356
357if [ $format = ufs ] ; then
358	# calculate image size
359	getsize
360
361	# We do two mkfile's of total_size, so double the space
362	(( tmp_needed = total_size * 2 ))
363
364	# check to see if there is sufficient space in tmpfs
365	#
366	tmp_free=`df -b /tmp | tail -1 | awk '{ printf ($2) }'`
367	(( tmp_free = tmp_free / 2 ))
368
369	if [ $tmp_needed -gt $tmp_free  ] ; then
370		# assumes we have enough scratch space on $ALT_ROOT
371        	scratch="$ALT_ROOT"
372	fi
373fi
374
375rddir="/$scratch/create_ramdisk.$$.tmp"
376rdfile32="$rddir/rd.file.32"
377rdfile64="$rddir/rd.file.64"
378rdmnt32="$rddir/rd.mount.32"
379rdmnt64="$rddir/rd.mount.64"
380errlog32="$rddir/rd.errlog.32"
381errlog64="$rddir/rd.errlog.64"
382lofidev32=""
383lofidev64=""
384
385# make directory for temp files safely
386rm -rf "$rddir"
387mkdir "$rddir"
388
389# Clean up upon exit.
390trap 'cleanup' EXIT
391
392if [ $SPLIT = yes ]; then
393	#
394	# We can't run lofiadm commands in parallel, so we have to do
395	# them here.
396	#
397	if [ "$format" = "ufs" ]; then
398		mkfile ${total_size}k "$rdfile32"
399		lofidev32=`lofiadm -a "$rdfile32"`
400		mkfile ${total_size}k "$rdfile64"
401		lofidev64=`lofiadm -a "$rdfile64"`
402	fi
403	create_archive "32-bit" "$ALT_ROOT/$BOOT_ARCHIVE" $lofidev32 &
404	create_archive "64-bit" "$ALT_ROOT/$BOOT_ARCHIVE_64" $lofidev64
405	wait
406	if [ "$format" = "ufs" ]; then
407		lofiadm -d "$rdfile32"
408		lofiadm -d "$rdfile64"
409	fi
410else
411	if [ "$format" = "ufs" ]; then
412		mkfile ${total_size}k "$rdfile32"
413		lofidev32=`lofiadm -a "$rdfile32"`
414	fi
415	create_archive "both" "$ALT_ROOT/$BOOT_ARCHIVE" $lofidev32
416	[ "$format" = "ufs" ] && lofiadm -d "$rdfile32"
417fi
418if [ $ERROR = 1 ]; then
419	cleanup
420	exit 1
421fi
422
423#
424# For the diskless case, hardlink archive to /boot to make it
425# visible via tftp. /boot is lofs mounted under /tftpboot/<hostname>.
426# NOTE: this script must work on both client and server.
427#
428grep "[	 ]/[	 ]*nfs[	 ]" "$ALT_ROOT/etc/vfstab" > /dev/null
429if [ $? = 0 ]; then
430	rm -f "$ALT_ROOT/boot/boot_archive" "$ALT_ROOT/boot/amd64/boot_archive"
431	ln "$ALT_ROOT/$BOOT_ARCHIVE" "$ALT_ROOT/boot/boot_archive"
432	ln "$ALT_ROOT/$BOOT_ARCHIVE_64" "$ALT_ROOT/boot/amd64/boot_archive"
433fi
434rm -rf "$rddir"
435