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