xref: /illumos-gate/usr/src/lib/brand/shared/zone/common.ksh (revision 1022fd2a9aa2c967697116c2ca51a238a3c550ac)
1#
2# CDDL HEADER START
3#
4# The contents of this file are subject to the terms of the
5# Common Development and Distribution License (the "License").
6# You may not use this file except in compliance with the License.
7#
8# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9# or http://www.opensolaris.org/os/licensing.
10# See the License for the specific language governing permissions
11# and limitations under the License.
12#
13# When distributing Covered Code, include this CDDL HEADER in each
14# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15# If applicable, add the following below this CDDL HEADER, with the
16# fields enclosed by brackets "[]" replaced with your own identifying
17# information: Portions Copyright [yyyy] [name of copyright owner]
18#
19# CDDL HEADER END
20#
21# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
22#
23
24#
25# Send the error message to the screen and to the logfile.
26#
27error()
28{
29        typeset fmt="$1"
30        shift
31
32        printf "${MSG_PREFIX}ERROR: ${fmt}\n" "$@"
33        [[ -n $LOGFILE ]] && printf "[$(date)] ERROR: ${fmt}\n" "$@" >&2
34}
35
36fatal()
37{
38        typeset fmt="$1"
39        shift
40
41	error "$fmt" "$@"
42	exit $EXIT_CODE
43}
44
45fail_fatal() {
46	printf "ERROR: "
47	printf "$@"
48	printf "\n"
49	exit $ZONE_SUBPROC_FATAL
50}
51
52#
53# Send the provided printf()-style arguments to the screen and to the logfile.
54#
55log()
56{
57        typeset fmt="$1"
58        shift
59
60        printf "${MSG_PREFIX}${fmt}\n" "$@"
61        [[ -n $LOGFILE ]] && printf "[$(date)] ${MSG_PREFIX}${fmt}\n" "$@" >&2
62}
63
64#
65# Print provided text to the screen if the shell variable "OPT_V" is set.
66# The text is always sent to the logfile.
67#
68vlog()
69{
70        typeset fmt="$1"
71        shift
72
73        [[ -n $OPT_V ]] && printf "${MSG_PREFIX}${fmt}\n" "$@"
74        [[ -n $LOGFILE ]] && printf "[$(date)] ${MSG_PREFIX}${fmt}\n" "$@" >&2
75}
76
77#
78# Validate that the directory is safe.
79#
80# It is possible for a malicious zone root user to modify a zone's filesystem
81# so that modifications made to the zone's filesystem by administrators in the
82# global zone modify the global zone's filesystem.  We can prevent this by
83# ensuring that all components of paths accessed by scripts are real (i.e.,
84# non-symlink) directories.
85#
86# NOTE: The specified path should be an absolute path as would be seen from
87# within the zone.  Also, this function does not check parent directories.
88# If, for example, you need to ensure that every component of the path
89# '/foo/bar/baz' is a directory and not a symlink, then do the following:
90#
91#	safe_dir /foo
92#	safe_dir /foo/bar
93#	safe_dir /foo/bar/baz
94#
95safe_dir()
96{
97	typeset dir="$1"
98
99	if [[ -h $ZONEROOT/$dir || ! -d $ZONEROOT/$dir ]]; then
100		fatal "$e_baddir" "$dir"
101	fi
102}
103
104# Like safe_dir except the dir doesn't have to exist.
105safe_opt_dir()
106{
107	typeset dir="$1"
108
109	[[ ! -e $ZONEROOT/$dir ]] && return
110
111	if [[ -h $ZONEROOT/$dir || ! -d $ZONEROOT/$dir ]]; then
112		fatal "$e_baddir" "$dir"
113	fi
114}
115
116# Only make a copy if we haven't already done so.
117safe_backup()
118{
119	typeset src="$1"
120	typeset dst="$2"
121
122	if [[ ! -h $src && ! -h $dst && ! -d $dst && ! -f $dst ]]; then
123		/usr/bin/cp -p $src $dst || fatal "$e_badfile" "$src"
124	fi
125}
126
127# Make a copy even if the destination already exists.
128safe_copy()
129{
130	typeset src="$1"
131	typeset dst="$2"
132
133	if [[ ! -h $src && ! -h $dst && ! -d $dst ]]; then
134		/usr/bin/cp -p $src $dst || fatal "$e_badfile" "$src"
135	fi
136}
137
138# Move a file
139safe_move()
140{
141	typeset src="$1"
142	typeset dst="$2"
143
144	if [[ ! -h $src && ! -h $dst && ! -d $dst ]]; then
145		/usr/bin/mv $src $dst || fatal "$e_badfile" "$src"
146	fi
147}
148
149safe_rm()
150{
151	if [[ ! -h $ZONEROOT/$1 && -f $ZONEROOT/$1 ]]; then
152		rm -f "$ZONEROOT/$1"
153	fi
154}
155
156#
157# Replace the file with a wrapper pointing to the native brand code.
158# However, we only do the replacement if the file hasn't already been
159# replaced with our wrapper.  This function expects the cwd to be the
160# location of the file we're replacing.
161#
162# Some of the files we're replacing are hardlinks to isaexec so we need to 'rm'
163# the file before we setup the wrapper while others are hardlinks to rc scripts
164# that we need to maintain.
165#
166safe_replace()
167{
168	typeset filename="$1"
169	typeset runname="$2"
170	typeset mode="$3"
171	typeset own="$4"
172	typeset rem="$5"
173
174	if [ -h $filename -o ! -f $filename ]; then
175		return
176	fi
177
178	egrep -s "Solaris Brand Replacement" $filename
179	if [ $? -eq 0 ]; then
180		return
181	fi
182
183	safe_backup $filename $filename.pre_p2v
184	if [ $rem = "remove" ]; then
185		rm -f $filename
186	fi
187
188	cat <<-END >$filename || exit 1
189	#!/bin/sh -p
190	#
191	# Solaris Brand Replacement
192	#
193	# Attention.  This file has been replaced with a new version for
194	# use in a virtualized environment.  Modification of this script is not
195	# supported and all changes will be lost upon reboot.  The
196	# {name}.pre_p2v version of this file is a backup copy of the
197	# original and should not be deleted.
198	#
199	END
200
201	echo ". $runname \"\$@\"" >>$filename || exit 1
202
203	chmod $mode $filename
204	chown $own $filename
205}
206
207safe_wrap()
208{
209	typeset filename="$1"
210	typeset runname="$2"
211	typeset mode="$3"
212	typeset own="$4"
213
214	if [ -f $filename ]; then
215		log "$e_cannot_wrap" "$filename"
216		exit 1
217	fi
218
219	cat <<-END >$filename || exit 1
220	#!/bin/sh
221	#
222	# Solaris Brand Wrapper
223	#
224	# Attention.  This file has been created for use in a
225	# virtualized environment.  Modification of this script
226	# is not supported and all changes will be lost upon reboot.
227	#
228	END
229
230	echo ". $runname \"\$@\"" >>$filename || exit 1
231
232	chmod $mode $filename
233	chown $own $filename
234}
235
236#
237# Read zonecfg ipd and fs entries and save the relevant data, one entry per
238# line.
239# This assumes the properties from the zonecfg output, e.g.:
240#	inherit-pkg-dir:
241#		dir: /usr
242#	fs:
243#		dir: /opt
244#		special: /opt
245#		raw not specified
246#		type: lofs
247#		options: [noexec,ro,noatime]
248#
249# and it assumes the order of the fs properties as above.  This also saves the
250# inherit-pkg-dir patterns into the ipd.{cpio|pax} temporary files for
251# filtering while extracting the image into the zonepath.  We have to save the
252# IPD patterns in the appropriate format for filtering with the different
253# archivers and we don't know what format we'll get until after the flash
254# archive is unpacked.
255#
256get_fs_info()
257{
258	zonecfg -z $zonename info inherit-pkg-dir | \
259	    nawk -v ipdcpiof=$ipdcpiofile -v ipdpaxf=$ipdpaxfile '{
260		if ($1 == "dir:") {
261			dir=$2;
262			printf("%s lofs %s ro\n", dir, dir);
263
264			if (substr(dir, 1, 1) == "/") {
265				printf("%s\n", substr(dir, 2)) >> ipdcpiof
266				printf("%s/*\n", substr(dir, 2)) >> ipdcpiof
267			} else {
268				printf("%s\n", dir) >> ipdcpiof
269				printf("%s/*\n", dir) >> ipdcpiof
270			}
271
272			if (substr(dir, 1, 1) == "/") {
273				printf("%s ", substr(dir, 2)) >> ipdpaxf
274			} else {
275				printf("%s ", dir) >> ipdpaxf
276			}
277		}
278	}' >> $fstmpfile
279
280	zonecfg -z $zonename info fs | nawk '{
281		if ($1 == "options:") {
282			# Remove brackets.
283			options=substr($2, 2, length($2) - 2);
284			printf("%s %s %s %s\n", dir, type, special, options);
285		} else if ($1 == "dir:") {
286			dir=$2;
287		} else if ($1 == "special:") {
288			special=$2;
289		} else if ($1 == "type:") {
290			type=$2
291		}
292	}' >> $fstmpfile
293}
294
295#
296# Mount zonecfg fs entries into the zonepath.
297#
298mnt_fs()
299{
300	if [ ! -s $fstmpfile ]; then
301		return;
302	fi
303
304	# Sort the fs entries so we can handle nested mounts.
305	sort $fstmpfile | nawk -v zonepath=$zonepath '{
306		if (NF == 4)
307			options="-o " $4;
308		else
309			options=""
310
311		# Create the mount point.  Ignore errors since we might have
312		# a nested mount with a pre-existing mount point.
313		cmd="/usr/bin/mkdir -p " zonepath "/root" $1 " >/dev/null 2>&1"
314		system(cmd);
315
316		cmd="/usr/sbin/mount -F " $2 " " options " " $3 " " \
317		    zonepath "/root" $1;
318		if (system(cmd) != 0) {
319			printf("command failed: %s\n", cmd);
320			exit 1;
321		}
322	}' >>$LOGFILE
323}
324
325#
326# Unmount zonecfg fs entries from the zonepath.
327#
328umnt_fs()
329{
330	if [ ! -s $fstmpfile ]; then
331		return;
332	fi
333
334	# Reverse sort the fs entries so we can handle nested unmounts.
335	sort -r $fstmpfile | nawk -v zonepath=$zonepath '{
336		cmd="/usr/sbin/umount " zonepath "/root" $1
337		if (system(cmd) != 0) {
338			printf("command failed: %s\n", cmd);
339		}
340	}' >>$LOGFILE
341}
342
343# Find the dataset mounted on the zonepath.
344get_zonepath_ds() {
345	ZONEPATH_DS=`/usr/sbin/zfs list -H -t filesystem -o name,mountpoint | \
346	    /usr/bin/nawk -v zonepath=$1 '{
347		if ($2 == zonepath)
348			print $1
349	}'`
350
351	if [ -z "$ZONEPATH_DS" ]; then
352		fail_fatal "$f_no_ds"
353	fi
354}
355
356#
357# Perform any cleanup in the zoneroot after unpacking the archive.
358#
359post_unpack()
360{
361	( cd "$ZONEROOT" && \
362	    find . \( -type b -o -type c \) -exec rm -f "{}" \; )
363}
364
365#
366# Determine flar compression style from identification file.
367#
368get_compression()
369{
370	typeset ident=$1
371	typeset line=$(grep "^files_compressed_method=" $ident)
372
373	print ${line##*=}
374}
375
376#
377# Determine flar archive style from identification file.
378#
379get_archiver()
380{
381        typeset ident=$1
382        typeset line=$(grep "^files_archived_method=" $ident)
383
384        print ${line##*=}
385}
386
387#
388# Unpack flar into current directory (which should be zoneroot).  The flash
389# archive is standard input.  See flash_archive(4) man page.
390#
391# We can't use "flar split" since it will only unpack into a directory called
392# "archive".  We need to unpack in place in order to properly handle nested
393# fs mounts within the zone root.  This function does the unpacking into the
394# current directory.
395#
396# This code is derived from the gen_split() function in /usr/sbin/flar so
397# we keep the same style as the original.
398#
399install_flar()
400{
401	typeset result
402        typeset archiver_command
403        typeset archiver_arguments
404
405	vlog "cd $ZONEROOT && $stage1 "$insrc" | install_flar"
406
407	# Read cookie
408	read -r input_line
409	if (( $? != 0 )); then
410		log "$not_readable" "$install_media"
411		return 1
412	fi
413	# The cookie has format FlAsH-aRcHiVe-m.n where m and n are integers.
414	if [[ ${input_line%%-[0-9]*.[0-9]*} != "FlAsH-aRcHiVe" ]]; then
415		log "$not_flar"
416		return 1
417	fi
418
419	while [ true ]
420	do
421		# We should always be at the start of a section here
422		read -r input_line
423		if [[ ${input_line%%=*} != "section_begin" ]]; then
424			log "$bad_flar"
425			return 1
426		fi
427		section_name=${input_line##*=}
428
429		# If we're at the archive, we're done skipping sections.
430		if [[ "$section_name" == "archive" ]]; then
431			break
432		fi
433
434		#
435		# Save identification section to a file so we can determine
436		# how to unpack the archive.
437		#
438		if [[ "$section_name" == "identification" ]]; then
439			/usr/bin/rm -f identification
440			while read -r input_line
441			do
442				if [[ ${input_line%%=*} == \
443				    "section_begin" ]]; then
444					/usr/bin/rm -f identification
445					log "$bad_flar"
446					return 1
447				fi
448
449				if [[ $input_line == \
450				    "section_end=$section_name" ]]; then
451					break;
452				fi
453				echo $input_line >> identification
454			done
455
456			continue
457		fi
458
459		#
460		# Otherwise skip past this section; read lines until detecting
461		# section_end.  According to flash_archive(4) we can have
462		# an arbitrary number of sections but the archive section
463		# must be last.
464		#
465		success=0
466		while read -r input_line
467		do
468			if [[ $input_line == "section_end=$section_name" ]];
469			then
470				success=1
471				break
472			fi
473			# Fail if we miss the end of the section
474			if [[ ${input_line%%=*} == "section_begin" ]]; then
475				/usr/bin/rm -f identification
476				log "$bad_flar"
477				return 1
478			fi
479		done
480		if (( $success == 0 )); then
481			#
482			# If we get here we read to the end of the file before
483			# seeing the end of the section we were reading.
484			#
485			/usr/bin/rm -f identification
486			log "$bad_flar"
487			return 1
488		fi
489	done
490
491	# Check for an archive made from a ZFS root pool.
492	egrep -s "^rootpool=" identification
493        if (( $? == 0 )); then
494		/usr/bin/rm -f identification
495                log "$bad_zfs_flar"
496                return 1
497        fi
498
499	# Get the information needed to unpack the archive.
500	archiver=$(get_archiver identification)
501	if [[ $archiver == "pax" ]]; then
502		# pax archiver specified
503		archiver_command="/usr/bin/pax"
504		if [[ -s $ipdpaxfile ]]; then
505			archiver_arguments="-r -p e -c \
506			    $(/usr/bin/cat $ipdpaxfile)"
507		else
508			archiver_arguments="-r -p e"
509		fi
510	elif [[ $archiver == "cpio" || -z $archiver ]]; then
511		# cpio archived specified OR no archiver specified - use default
512		archiver_command="/usr/bin/cpio"
513		archiver_arguments="-icdumfE $ipdcpiofile"
514	else
515		# unknown archiver specified
516		log "$unknown_archiver" $archiver
517		return 1
518	fi
519
520	if [[ ! -x $archiver_command ]]; then
521		/usr/bin/rm -f identification
522		log "$cmd_not_exec" $archiver_command
523		return 1
524	fi
525
526	compression=$(get_compression identification)
527
528	# We're done with the identification file
529	/usr/bin/rm -f identification
530
531	# Extract archive
532	if [[ $compression == "compress" ]]; then
533		/usr/bin/zcat | \
534		    $archiver_command $archiver_arguments 2>/dev/null
535	else
536		$archiver_command $archiver_arguments 2>/dev/null
537	fi
538	result=$?
539
540	post_unpack
541
542	(( $result != 0 )) && return 1
543
544	return 0
545}
546
547#
548# Get the archive base.
549#
550# We must unpack the archive in the right place within the zonepath so
551# that files are installed into the various mounted filesystems that are set
552# up in the zone's configuration.  These are already mounted for us by the
553# mntfs function.
554#
555# Archives can be made of either a physical host's root file system or a
556# zone's zonepath.  For a physical system, if the archive is made using an
557# absolute path (/...) we can't use it.  For a zone the admin can make the
558# archive from a variety of locations;
559#
560#   a) zonepath itself: This will be a single dir, probably named with the
561#      zone name, it will contain a root dir and under the root we'll see all
562#      the top level dirs; etc, var, usr...  We must be above the ZONEPATH
563#      when we unpack the archive but this will only work if the the archive's
564#      top-level dir name matches the ZONEPATH base-level dir name.  If not,
565#      this is an error.
566#
567#   b) inside the zonepath: We'll see root and it will contain all the top
568#      level dirs; etc, var, usr....  We must be in the ZONEPATH when we unpack
569#      the archive.
570#
571#   c) inside the zonepath root: We'll see all the top level dirs, ./etc,
572#      ./var, ./usr....  This is also the case we see when we get an archive
573#      of a physical sytem.  We must be in ZONEROOT when we unpack the archive.
574#
575# Note that there can be a directory named "root" under the ZONEPATH/root
576# directory.
577#
578# This function handles the above possibilities so that we reject absolute
579# path archives and figure out where in the file system we need to be to
580# properly unpack the archive into the zone.  It sets the ARCHIVE_BASE
581# variable to the location where the achive should be unpacked.
582#
583get_archive_base()
584{
585	stage1=$1
586	archive=$2
587	stage2=$3
588
589	vlog "$m_analyse_archive"
590
591	base=`$stage1 $archive | $stage2 2>/dev/null | nawk -F/ '{
592		# Check for an absolute path archive
593		if (substr($0, 1, 1) == "/")
594			exit 1
595
596		if ($1 != ".")
597			dirs[$1] = 1
598		else
599			dirs[$2] = 1
600	}
601	END {
602		for (d in dirs) {
603			cnt++
604			if (d == "bin")  sawbin = 1
605			if (d == "etc")  sawetc = 1
606			if (d == "root") sawroot = 1
607			if (d == "var")  sawvar = 1
608                }
609
610		if (cnt == 1) {
611			# If only one top-level dir named root, we are in the
612			# zonepath, otherwise this must be an archive *of*
613			# the zonepath so print the top-level dir name.
614			if (sawroot)
615				print "*zonepath*"
616			else
617				for (d in dirs) print d
618		} else {
619			# We are either in the zonepath or in the zonepath/root
620			# (or at the top level of a full system archive which
621			# looks like the zonepath/root case).  Figure out which
622			# one.
623			if (sawroot && !sawbin && !sawetc && !sawvar)
624				print "*zonepath*"
625			else
626				print "*zoneroot*"
627		}
628	}'`
629
630	if (( $? != 0 )); then
631		umnt_fs
632		fatal "$e_absolute_archive"
633	fi
634
635	if [[ "$base" == "*zoneroot*" ]]; then
636		ARCHIVE_BASE=$ZONEROOT
637	elif [[ "$base" == "*zonepath*" ]]; then
638		ARCHIVE_BASE=$ZONEPATH
639	else
640		# We need to be in the dir above the ZONEPATH but we need to
641		# validate that $base matches the final component of ZONEPATH.
642		bname=`basename $ZONEPATH`
643
644		if [[ "$bname" != "$base" ]]; then
645			umnt_fs
646			fatal "$e_mismatch_archive" "$base" "$bname"
647		fi
648		ARCHIVE_BASE=`dirname $ZONEPATH`
649	fi
650}
651
652#
653# Unpack cpio archive into zoneroot.
654#
655install_cpio()
656{
657	stage1=$1
658	archive=$2
659
660	get_archive_base "$stage1" "$archive" "cpio -it"
661
662	cpioopts="-idmfE $ipdcpiofile"
663
664	vlog "cd \"$ARCHIVE_BASE\" && $stage1 \"$archive\" | cpio $cpioopts"
665
666	# Ignore errors from cpio since we expect some errors depending on
667	# how the archive was made.
668	( cd "$ARCHIVE_BASE" && $stage1 "$archive" | cpio $cpioopts )
669
670	post_unpack
671
672	return 0
673}
674
675#
676# Unpack pax archive into zoneroot.
677#
678install_pax()
679{
680	archive=$1
681
682	get_archive_base "cat" "$archive" "pax"
683
684	if [[ -s $ipdpaxfile ]]; then
685		filtopt="-c $(/usr/bin/cat $ipdpaxfile)"
686	fi
687
688	vlog "cd \"$ARCHIVE_BASE\" && pax -r -f \"$archive\" $filtopt"
689
690	# Ignore errors from pax since we expect some errors depending on
691	# how the archive was made.
692	( cd "$ARCHIVE_BASE" && pax -r -f "$archive" $filtopt )
693
694	post_unpack
695
696	return 0
697}
698
699#
700# Unpack UFS dump into zoneroot.
701#
702install_ufsdump()
703{
704	archive=$1
705
706	vlog "cd \"$ZONEROOT\" && ufsrestore rf \"$archive\""
707
708	#
709	# ufsrestore goes interactive if you ^C it.  To prevent that,
710	# we make sure its stdin is not a terminal.
711	# Note that there is no way to filter inherit-pkg-dirs for a full
712	# restore so there will be warnings in the log file.
713	#
714	( cd "$ZONEROOT" && ufsrestore rf "$archive" < /dev/null )
715	result=$?
716
717	post_unpack
718
719	return $result
720}
721
722#
723# Copy directory hierarchy into zoneroot.
724#
725install_dir()
726{
727	source_dir=$1
728
729	cpioopts="-pdm"
730
731	first=1
732	filt=$(for i in $(cat $ipdpaxfile)
733		do
734			echo $i | egrep -s "/" && continue
735			if [[ $first == 1 ]]; then
736				printf "^%s" $i
737				first=0
738			else
739				printf "|^%s" $i
740			fi
741		done)
742
743	list=$(cd "$source_dir" && ls -d * | egrep -v "$filt")
744	flist=$(for i in $list
745	do
746		printf "%s " "$i"
747	done)
748	findopts="-xdev ( -type d -o -type f -o -type l ) -print"
749
750	vlog "cd \"$source_dir\" && find $flist $findopts | "
751	vlog "cpio $cpioopts \"$ZONEROOT\""
752
753	# Ignore errors from cpio since we expect some errors depending on
754	# how the archive was made.
755	( cd "$source_dir" && find $flist $findopts | \
756	    cpio $cpioopts "$ZONEROOT" )
757
758	post_unpack
759
760	return 0
761}
762
763#
764# This is a common function for laying down a zone image from a variety of
765# different sources.  This can be used to either install a fresh zone or as
766# part of zone migration during attach.
767#
768# The first argument specifies the type of image: archive, directory or stdin.
769# The second argument specifies the image itself.  In the case of stdin, the
770# second argument specifies the format of the stream (cpio, flar, etc.).
771# Any validation or post-processing on the image is done elsewhere.
772#
773# This function calls a 'sanity_check' function which must be provided by
774# the script which includes this code.
775#
776install_image()
777{
778	intype=$1
779	insrc=$2
780
781	if [[ -z "$intype" || -z "$insrc" ]]; then
782		return 1
783	fi
784
785	filetype="unknown"
786	filetypename="unknown"
787	stage1="cat"
788
789	if [[ "$intype" == "directory" ]]; then
790		if [[ "$insrc" == "-" ]]; then
791			# Indicates that the existing zonepath is prepopulated.
792			filetype="existing"
793			filetypename="existing"
794		else
795			if [[ "$(echo $insrc | cut -c 1)" != "/" ]]; then
796				fatal "$e_path_abs" "$insrc"
797			fi
798
799			if [[ ! -e "$insrc" ]]; then
800				log "$e_not_found" "$insrc"
801				fatal "$e_install_abort"
802			fi
803
804			if [[ ! -r "$insrc" ]]; then
805				log "$e_not_readable" "$insrc"
806				fatal "$e_install_abort"
807			fi
808
809			if [[ ! -d "$insrc" ]]; then
810				log "$e_not_dir"
811				fatal "$e_install_abort"
812			fi
813
814			sanity_check $insrc
815
816			filetype="directory"
817			filetypename="directory"
818		fi
819
820	else
821		# Common code for both archive and stdin stream.
822
823		if [[ "$intype" == "archive" ]]; then
824			if [[ ! -f "$insrc" ]]; then
825				log "$e_unknown_archive"
826				fatal "$e_install_abort"
827			fi
828			ftype="$(LC_ALL=C file $insrc | cut -d: -f 2)"
829		else
830			# For intype == stdin, the insrc parameter specifies
831			# the stream format coming on stdin.
832			ftype="$insrc"
833			insrc="-"
834		fi
835
836		# Setup vars for the archive type we have.
837		case "$ftype" in
838		*cpio*)		filetype="cpio"
839				filetypename="cpio archive"
840			;;
841		*bzip2*)	filetype="bzip2"
842				filetypename="bzipped cpio archive"
843			;;
844		*gzip*)		filetype="gzip"
845				filetypename="gzipped cpio archive"
846			;;
847		*ufsdump*)	filetype="ufsdump"
848				filetypename="ufsdump archive"
849			;;
850		"flar")
851				filetype="flar"
852				filetypename="flash archive"
853			;;
854		"flash")
855				filetype="flar"
856				filetypename="flash archive"
857			;;
858		*Flash\ Archive*)
859				filetype="flar"
860				filetypename="flash archive"
861			;;
862		"tar")
863				filetype="tar"
864				filetypename="tar archive"
865			;;
866		*USTAR\ tar\ archive)
867				filetype="tar"
868				filetypename="tar archive"
869			;;
870		"pax")
871				filetype="xustar"
872				filetypename="pax (xustar) archive"
873			;;
874		*USTAR\ tar\ archive\ extended\ format*)
875				filetype="xustar"
876				filetypename="pax (xustar) archive"
877			;;
878		"zfs")
879				filetype="zfs"
880				filetypename="ZFS send stream"
881			;;
882		*ZFS\ snapshot\ stream*)
883				filetype="zfs"
884				filetypename="ZFS send stream"
885			;;
886		*)		log "$e_unknown_archive"
887				fatal "$e_install_abort"
888			;;
889		esac
890	fi
891
892	vlog "$filetypename"
893
894	# Check for a non-empty root if no '-d -' option.
895	if [[ "$filetype" != "existing" ]]; then
896		cnt=$(ls $ZONEROOT | wc -l)
897		if (( $cnt != 0 )); then
898			fatal "$e_root_full" "$ZONEROOT"
899		fi
900	fi
901
902	fstmpfile=$(/usr/bin/mktemp -t -p /var/tmp)
903	if [[ -z "$fstmpfile" ]]; then
904		fatal "$e_tmpfile"
905	fi
906
907	# Make sure we always have the files holding the directories to filter
908	# out when extracting from a CPIO or PAX archive.  We'll add the fs
909	# entries to these files in get_fs_info() (there may be no IPDs for
910	# some brands but thats ok).
911	ipdcpiofile=$(/usr/bin/mktemp -t -p /var/tmp ipd.cpio.XXXXXX)
912	if [[ -z "$ipdcpiofile" ]]; then
913		rm -f $fstmpfile
914		fatal "$e_tmpfile"
915	fi
916
917	# In addition to the IPDs, also filter out these directories.
918	echo 'dev/*' >>$ipdcpiofile
919	echo 'devices/*' >>$ipdcpiofile
920	echo 'devices' >>$ipdcpiofile
921	echo 'proc/*' >>$ipdcpiofile
922	echo 'tmp/*' >>$ipdcpiofile
923	echo 'var/run/*' >>$ipdcpiofile
924	echo 'system/contract/*' >>$ipdcpiofile
925	echo 'system/object/*' >>$ipdcpiofile
926
927	ipdpaxfile=$(/usr/bin/mktemp -t -p /var/tmp ipd.pax.XXXXXX)
928	if [[ -z "$ipdpaxfile" ]]; then
929		rm -f $fstmpfile $ipdcpiofile
930		fatal "$e_tmpfile"
931	fi
932
933	printf "%s " \
934	    "dev devices proc tmp var/run system/contract system/object" \
935	    >>$ipdpaxfile
936
937	# Set up any fs mounts so the archive will install into the correct
938	# locations.
939	get_fs_info
940	mnt_fs
941	if (( $? != 0 )); then
942		umnt_fs >/dev/null 2>&1
943		rm -f $fstmpfile $ipdcpiofile $ipdpaxfile
944		fatal "$mount_failed"
945	fi
946
947	if [[ "$filetype" == "existing" ]]; then
948		log "$no_installing"
949	else
950		log "$installing"
951	fi
952
953	#
954	# Install the image into the zonepath.
955	#
956	unpack_result=0
957	stage1="cat"
958	if [[ "$filetype" == "gzip" ]]; then
959		stage1="gzcat"
960		filetype="cpio"
961	elif [[ "$filetype" == "bzip2" ]]; then
962		stage1="bzcat"
963		filetype="cpio"
964	fi
965
966	if [[ "$filetype" == "cpio" ]]; then
967		install_cpio "$stage1" "$insrc"
968		unpack_result=$?
969
970	elif [[ "$filetype" == "flar" ]]; then
971		( cd "$ZONEROOT" && $stage1 $insrc | install_flar )
972		unpack_result=$?
973
974	elif [[ "$filetype" == "xustar" ]]; then
975		install_pax "$insrc"
976		unpack_result=$?
977
978	elif [[ "$filetype" = "tar" ]]; then
979		vlog "cd \"$ZONEROOT\" && tar -xf \"$insrc\""
980		# Ignore errors from tar since we expect some errors depending
981		# on how the archive was made.
982		( cd "$ZONEROOT" && tar -xf "$insrc" )
983		unpack_result=0
984		post_unpack
985
986	elif [[ "$filetype" == "ufsdump" ]]; then
987		install_ufsdump "$insrc"
988		unpack_result=$?
989
990	elif [[ "$filetype" == "directory" ]]; then
991		install_dir "$insrc"
992		unpack_result=$?
993
994	elif [[ "$filetype" == "zfs" ]]; then
995		#
996		# Given a 'zfs send' stream file, receive the snapshot into
997		# the zone's dataset.  We're getting the original system's
998		# zonepath dataset.  Destroy the existing dataset created
999		# above since this recreates it.
1000		#
1001		if [[ -z "$DATASET" ]]; then
1002			fatal "$f_nodataset"
1003		fi
1004		/usr/sbin/zfs destroy "$DATASET"
1005		if (( $? != 0 )); then
1006			log "$f_zfsdestroy" "$DATASET"
1007		fi
1008
1009		vlog "$stage1 $insrc | zfs receive -F $DATASET"
1010		( $stage1 $insrc | /usr/sbin/zfs receive -F $DATASET )
1011		unpack_result=$?
1012	fi
1013
1014	# Clean up any fs mounts used during unpacking.
1015	umnt_fs
1016	rm -f $fstmpfile $ipdcpiofile $ipdpaxfile
1017
1018	chmod 700 $zonepath
1019
1020	(( $unpack_result != 0 )) && fatal "$f_unpack_failed"
1021
1022	# Verify this is a valid image.
1023	sanity_check $ZONEROOT
1024
1025	return 0
1026}
1027
1028# Setup i18n output
1029TEXTDOMAIN="SUNW_OST_OSCMD"
1030export TEXTDOMAIN
1031
1032e_cannot_wrap=$(gettext "%s: error: wrapper file already exists")
1033e_baddir=$(gettext "Invalid '%s' directory within the zone")
1034e_badfile=$(gettext "Invalid '%s' file within the zone")
1035e_path_abs=$(gettext "Pathname specified to -a '%s' must be absolute.")
1036e_not_found=$(gettext "%s: error: file or directory not found.")
1037e_install_abort=$(gettext "Installation aborted.")
1038e_not_readable=$(gettext "Cannot read directory '%s'")
1039e_not_dir=$(gettext "Error: must be a directory")
1040e_unknown_archive=$(gettext "Error: Unknown archive format. Must be a flash archive, a cpio archive (can also be gzipped or bzipped), a pax XUSTAR archive, or a level 0 ufsdump archive.")
1041e_absolute_archive=$(gettext "Error: archive contains absolute paths instead of relative paths.")
1042e_mismatch_archive=$(gettext "Error: the archive top-level directory (%s) does not match the zonepath (%s).")
1043e_tmpfile=$(gettext "Unable to create temporary file")
1044e_root_full=$(gettext "Zonepath root %s exists and contains data; remove or move aside prior to install.")
1045f_mkdir=$(gettext "Unable to create directory %s.")
1046f_chmod=$(gettext "Unable to chmod directory %s.")
1047f_chown=$(gettext "Unable to chown directory %s.")
1048
1049
1050m_analyse_archive=$(gettext "Analysing the archive")
1051
1052not_readable=$(gettext "Cannot read file '%s'")
1053not_flar=$(gettext "Input is not a flash archive")
1054bad_flar=$(gettext "Flash archive is a corrupt")
1055bad_zfs_flar=$(gettext "Flash archive contains a ZFS send stream.\n\tRecreate the flar using the -L option with cpio or pax.")
1056f_unpack_failed=$(gettext "Unpacking the archive failed")
1057unknown_archiver=$(gettext "Archiver %s is not supported")
1058cmd_not_exec=$(gettext "Required command '%s' not executable!")
1059
1060#
1061# Exit values used by the script, as #defined in <sys/zone.h>
1062#
1063#	ZONE_SUBPROC_OK
1064#	===============
1065#	Installation was successful
1066#
1067#	ZONE_SUBPROC_USAGE
1068#	==================
1069#	Improper arguments were passed, so print a usage message before exiting
1070#
1071#	ZONE_SUBPROC_NOTCOMPLETE
1072#	========================
1073#	Installation did not complete, but another installation attempt can be
1074#	made without an uninstall
1075#
1076#	ZONE_SUBPROC_FATAL
1077#	==================
1078#	Installation failed and an uninstall will be required before another
1079#	install can be attempted
1080#
1081ZONE_SUBPROC_OK=0
1082ZONE_SUBPROC_USAGE=253
1083ZONE_SUBPROC_NOTCOMPLETE=254
1084ZONE_SUBPROC_FATAL=255
1085
1086