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