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