xref: /illumos-gate/usr/src/lib/brand/shared/zone/common.ksh (revision ef150c2b133e57069ae0f58d5d80cef8980ac9c3)
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) 2009, 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        typeset fmt="$1"
47        shift
48
49	error "$fmt" "$@"
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 -p
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
208safe_wrap()
209{
210	typeset filename="$1"
211	typeset runname="$2"
212	typeset mode="$3"
213	typeset own="$4"
214
215	if [ -f $filename ]; then
216		log "$e_cannot_wrap" "$filename"
217		exit 1
218	fi
219
220	cat <<-END >$filename || exit 1
221	#!/bin/sh
222	#
223	# Solaris Brand Wrapper
224	#
225	# Attention.  This file has been created for use in a
226	# virtualized environment.  Modification of this script
227	# is not supported and all changes will be lost upon reboot.
228	#
229	END
230
231	echo ". $runname \"\$@\"" >>$filename || exit 1
232
233	chmod $mode $filename
234	chown $own $filename
235}
236
237#
238# Read zonecfg fs entries and save the relevant data, one entry per
239# line.
240# This assumes the properties from the zonecfg output, e.g.:
241#	fs:
242#		dir: /opt
243#		special: /opt
244#		raw not specified
245#		type: lofs
246#		options: [noexec,ro,noatime]
247#
248# and it assumes the order of the fs properties as above.
249#
250get_fs_info()
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 validation and cleanup in the zoneroot after unpacking the archive.
330#
331post_unpack()
332{
333	#
334	# Check if the image was created with a valid libc.so.1.
335	#
336	hwcap=`moe -v -32 $ZONEROOT/lib/libc.so.1 2>&1`
337	if (( $? != 0 )); then
338		vlog "$f_hwcap_info" "$hwcap"
339		fail_fatal "$f_sanity_hwcap"
340	fi
341
342	( cd "$ZONEROOT" && \
343	    find . \( -type b -o -type c \) -exec rm -f "{}" \; )
344}
345
346#
347# Determine flar compression style from identification file.
348#
349get_compression()
350{
351	typeset ident=$1
352	typeset line=$(grep "^files_compressed_method=" $ident)
353
354	print ${line##*=}
355}
356
357#
358# Determine flar archive style from identification file.
359#
360get_archiver()
361{
362        typeset ident=$1
363        typeset line=$(grep "^files_archived_method=" $ident)
364
365        print ${line##*=}
366}
367
368#
369# Unpack flar into current directory (which should be zoneroot).  The flash
370# archive is standard input.  See flash_archive(5) man page.
371#
372# We can't use "flar split" since it will only unpack into a directory called
373# "archive".  We need to unpack in place in order to properly handle nested
374# fs mounts within the zone root.  This function does the unpacking into the
375# current directory.
376#
377# This code is derived from the gen_split() function in /usr/sbin/flar so
378# we keep the same style as the original.
379#
380install_flar()
381{
382	typeset result
383        typeset archiver_command
384        typeset archiver_arguments
385
386	vlog "cd $ZONEROOT && $stage1 "$insrc" | install_flar"
387
388	# Read cookie
389	read -r input_line
390	if (( $? != 0 )); then
391		log "$not_readable" "$install_media"
392		return 1
393	fi
394	# The cookie has format FlAsH-aRcHiVe-m.n where m and n are integers.
395	if [[ ${input_line%%-[0-9]*.[0-9]*} != "FlAsH-aRcHiVe" ]]; then
396		log "$not_flar"
397		return 1
398	fi
399
400	while [ true ]
401	do
402		# We should always be at the start of a section here
403		read -r input_line
404		if [[ ${input_line%%=*} != "section_begin" ]]; then
405			log "$bad_flar"
406			return 1
407		fi
408		section_name=${input_line##*=}
409
410		# If we're at the archive, we're done skipping sections.
411		if [[ "$section_name" == "archive" ]]; then
412			break
413		fi
414
415		#
416		# Save identification section to a file so we can determine
417		# how to unpack the archive.
418		#
419		if [[ "$section_name" == "identification" ]]; then
420			/usr/bin/rm -f identification
421			while read -r input_line
422			do
423				if [[ ${input_line%%=*} == \
424				    "section_begin" ]]; then
425					/usr/bin/rm -f identification
426					log "$bad_flar"
427					return 1
428				fi
429
430				if [[ $input_line == \
431				    "section_end=$section_name" ]]; then
432					break;
433				fi
434				echo $input_line >> identification
435			done
436
437			continue
438		fi
439
440		#
441		# Otherwise skip past this section; read lines until detecting
442		# section_end.  According to flash_archive(5) we can have
443		# an arbitrary number of sections but the archive section
444		# must be last.
445		#
446		success=0
447		while read -r input_line
448		do
449			if [[ $input_line == "section_end=$section_name" ]];
450			then
451				success=1
452				break
453			fi
454			# Fail if we miss the end of the section
455			if [[ ${input_line%%=*} == "section_begin" ]]; then
456				/usr/bin/rm -f identification
457				log "$bad_flar"
458				return 1
459			fi
460		done
461		if (( $success == 0 )); then
462			#
463			# If we get here we read to the end of the file before
464			# seeing the end of the section we were reading.
465			#
466			/usr/bin/rm -f identification
467			log "$bad_flar"
468			return 1
469		fi
470	done
471
472	# Check for an archive made from a ZFS root pool.
473	egrep -s "^rootpool=" identification
474        if (( $? == 0 )); then
475		/usr/bin/rm -f identification
476                log "$bad_zfs_flar"
477                return 1
478        fi
479
480	# Get the information needed to unpack the archive.
481	archiver=$(get_archiver identification)
482	if [[ $archiver == "pax" ]]; then
483		# pax archiver specified
484		archiver_command="/usr/bin/pax"
485		if [[ -s $fspaxfile ]]; then
486			archiver_arguments="-r -p e -c \
487			    $(/usr/bin/cat $fspaxfile)"
488		else
489			archiver_arguments="-r -p e"
490		fi
491	elif [[ $archiver == "cpio" || -z $archiver ]]; then
492		# cpio archived specified OR no archiver specified - use default
493		archiver_command="/usr/bin/cpio"
494		archiver_arguments="-icdumfE $fscpiofile"
495	else
496		# unknown archiver specified
497		log "$unknown_archiver" $archiver
498		return 1
499	fi
500
501	if [[ ! -x $archiver_command ]]; then
502		/usr/bin/rm -f identification
503		log "$cmd_not_exec" $archiver_command
504		return 1
505	fi
506
507	compression=$(get_compression identification)
508
509	# We're done with the identification file
510	/usr/bin/rm -f identification
511
512	# Extract archive
513	if [[ $compression == "compress" ]]; then
514		/usr/bin/zcat | \
515		    $archiver_command $archiver_arguments 2>/dev/null
516	else
517		$archiver_command $archiver_arguments 2>/dev/null
518	fi
519	result=$?
520
521	post_unpack
522
523	(( $result != 0 )) && return 1
524
525	return 0
526}
527
528#
529# Get the archive base.
530#
531# We must unpack the archive in the right place within the zonepath so
532# that files are installed into the various mounted filesystems that are set
533# up in the zone's configuration.  These are already mounted for us by the
534# mntfs function.
535#
536# Archives can be made of either a physical host's root file system or a
537# zone's zonepath.  For a physical system, if the archive is made using an
538# absolute path (/...) we can't use it.  For a zone the admin can make the
539# archive from a variety of locations;
540#
541#   a) zonepath itself: This will be a single dir, probably named with the
542#      zone name, it will contain a root dir and under the root we'll see all
543#      the top level dirs; etc, var, usr...  We must be above the ZONEPATH
544#      when we unpack the archive but this will only work if the the archive's
545#      top-level dir name matches the ZONEPATH base-level dir name.  If not,
546#      this is an error.
547#
548#   b) inside the zonepath: We'll see root and it will contain all the top
549#      level dirs; etc, var, usr....  We must be in the ZONEPATH when we unpack
550#      the archive.
551#
552#   c) inside the zonepath root: We'll see all the top level dirs, ./etc,
553#      ./var, ./usr....  This is also the case we see when we get an archive
554#      of a physical system.  We must be in ZONEROOT when we unpack the archive.
555#
556# Note that there can be a directory named "root" under the ZONEPATH/root
557# directory.
558#
559# This function handles the above possibilities so that we reject absolute
560# path archives and figure out where in the file system we need to be to
561# properly unpack the archive into the zone.  It sets the ARCHIVE_BASE
562# variable to the location where the achive should be unpacked.
563#
564get_archive_base()
565{
566	stage1=$1
567	archive=$2
568	stage2=$3
569
570	vlog "$m_analyse_archive"
571
572	base=`$stage1 $archive | $stage2 2>/dev/null | nawk -F/ '{
573		# Check for an absolute path archive
574		if (substr($0, 1, 1) == "/")
575			exit 1
576
577		if ($1 != ".")
578			dirs[$1] = 1
579		else
580			dirs[$2] = 1
581	}
582	END {
583		for (d in dirs) {
584			cnt++
585			if (d == "bin")  sawbin = 1
586			if (d == "etc")  sawetc = 1
587			if (d == "root") sawroot = 1
588			if (d == "var")  sawvar = 1
589                }
590
591		if (cnt == 1) {
592			# If only one top-level dir named root, we are in the
593			# zonepath, otherwise this must be an archive *of*
594			# the zonepath so print the top-level dir name.
595			if (sawroot)
596				print "*zonepath*"
597			else
598				for (d in dirs) print d
599		} else {
600			# We are either in the zonepath or in the zonepath/root
601			# (or at the top level of a full system archive which
602			# looks like the zonepath/root case).  Figure out which
603			# one.
604			if (sawroot && !sawbin && !sawetc && !sawvar)
605				print "*zonepath*"
606			else
607				print "*zoneroot*"
608		}
609	}'`
610
611	if (( $? != 0 )); then
612		umnt_fs
613		fatal "$e_absolute_archive"
614	fi
615
616	if [[ "$base" == "*zoneroot*" ]]; then
617		ARCHIVE_BASE=$ZONEROOT
618	elif [[ "$base" == "*zonepath*" ]]; then
619		ARCHIVE_BASE=$ZONEPATH
620	else
621		# We need to be in the dir above the ZONEPATH but we need to
622		# validate that $base matches the final component of ZONEPATH.
623		bname=`basename $ZONEPATH`
624
625		if [[ "$bname" != "$base" ]]; then
626			umnt_fs
627			fatal "$e_mismatch_archive" "$base" "$bname"
628		fi
629		ARCHIVE_BASE=`dirname $ZONEPATH`
630	fi
631}
632
633#
634# Unpack cpio archive into zoneroot.
635#
636install_cpio()
637{
638	stage1=$1
639	archive=$2
640
641	get_archive_base "$stage1" "$archive" "cpio -it"
642
643	cpioopts="-idmfE $fscpiofile"
644
645	vlog "cd \"$ARCHIVE_BASE\" && $stage1 \"$archive\" | cpio $cpioopts"
646
647	# Ignore errors from cpio since we expect some errors depending on
648	# how the archive was made.
649	( cd "$ARCHIVE_BASE" && $stage1 "$archive" | cpio $cpioopts )
650
651	post_unpack
652
653	return 0
654}
655
656#
657# Unpack pax archive into zoneroot.
658#
659install_pax()
660{
661	archive=$1
662
663	get_archive_base "cat" "$archive" "pax"
664
665	if [[ -s $fspaxfile ]]; then
666		filtopt="-c $(/usr/bin/cat $fspaxfile)"
667	fi
668
669	vlog "cd \"$ARCHIVE_BASE\" && pax -r -f \"$archive\" $filtopt"
670
671	# Ignore errors from pax since we expect some errors depending on
672	# how the archive was made.
673	( cd "$ARCHIVE_BASE" && pax -r -f "$archive" $filtopt )
674
675	post_unpack
676
677	return 0
678}
679
680#
681# Unpack UFS dump into zoneroot.
682#
683install_ufsdump()
684{
685	archive=$1
686
687	vlog "cd \"$ZONEROOT\" && ufsrestore rf \"$archive\""
688
689	#
690	# ufsrestore goes interactive if you ^C it.  To prevent that,
691	# we make sure its stdin is not a terminal.
692	#
693	( cd "$ZONEROOT" && ufsrestore rf "$archive" < /dev/null )
694	result=$?
695
696	post_unpack
697
698	return $result
699}
700
701#
702# Copy directory hierarchy into zoneroot.
703#
704install_dir()
705{
706	source_dir=$1
707
708	cpioopts="-pdm"
709
710	first=1
711	filt=$(for i in $(cat $fspaxfile)
712		do
713			echo $i | egrep -s "/" && continue
714			if [[ $first == 1 ]]; then
715				printf "^%s" $i
716				first=0
717			else
718				printf "|^%s" $i
719			fi
720		done)
721
722	list=$(cd "$source_dir" && ls -d * | egrep -v "$filt")
723	flist=$(for i in $list
724	do
725		printf "%s " "$i"
726	done)
727	findopts="-xdev ( -type d -o -type f -o -type l ) -print"
728
729	vlog "cd \"$source_dir\" && find $flist $findopts | "
730	vlog "cpio $cpioopts \"$ZONEROOT\""
731
732	# Ignore errors from cpio since we expect some errors depending on
733	# how the archive was made.
734	( cd "$source_dir" && find $flist $findopts | \
735	    cpio $cpioopts "$ZONEROOT" )
736
737	post_unpack
738
739	return 0
740}
741
742#
743# This is a common function for laying down a zone image from a variety of
744# different sources.  This can be used to either install a fresh zone or as
745# part of zone migration during attach.
746#
747# The first argument specifies the type of image: archive, directory or stdin.
748# The second argument specifies the image itself.  In the case of stdin, the
749# second argument specifies the format of the stream (cpio, flar, etc.).
750# Any validation or post-processing on the image is done elsewhere.
751#
752# This function calls a 'sanity_check' function which must be provided by
753# the script which includes this code.
754#
755install_image()
756{
757	intype=$1
758	insrc=$2
759
760	if [[ -z "$intype" || -z "$insrc" ]]; then
761		return 1
762	fi
763
764	filetype="unknown"
765	filetypename="unknown"
766	stage1="cat"
767
768	if [[ "$intype" == "directory" ]]; then
769		if [[ "$insrc" == "-" ]]; then
770			# Indicates that the existing zonepath is prepopulated.
771			filetype="existing"
772			filetypename="existing"
773		else
774			if [[ "$(echo $insrc | cut -c 1)" != "/" ]]; then
775				fatal "$e_path_abs" "$insrc"
776			fi
777
778			if [[ ! -e "$insrc" ]]; then
779				log "$e_not_found" "$insrc"
780				fatal "$e_install_abort"
781			fi
782
783			if [[ ! -r "$insrc" ]]; then
784				log "$e_not_readable" "$insrc"
785				fatal "$e_install_abort"
786			fi
787
788			if [[ ! -d "$insrc" ]]; then
789				log "$e_not_dir"
790				fatal "$e_install_abort"
791			fi
792
793			sanity_check $insrc
794
795			filetype="directory"
796			filetypename="directory"
797		fi
798
799	else
800		# Common code for both archive and stdin stream.
801
802		if [[ "$intype" == "archive" ]]; then
803			if [[ ! -f "$insrc" ]]; then
804				log "$e_unknown_archive"
805				fatal "$e_install_abort"
806			fi
807			ftype="$(LC_ALL=C file $insrc | cut -d: -f 2)"
808		else
809			# For intype == stdin, the insrc parameter specifies
810			# the stream format coming on stdin.
811			ftype="$insrc"
812			insrc="-"
813		fi
814
815		# Setup vars for the archive type we have.
816		case "$ftype" in
817		*cpio*)		filetype="cpio"
818				filetypename="cpio archive"
819			;;
820		*bzip2*)	filetype="bzip2"
821				filetypename="bzipped cpio archive"
822			;;
823		*gzip*)		filetype="gzip"
824				filetypename="gzipped cpio archive"
825			;;
826		*ufsdump*)	filetype="ufsdump"
827				filetypename="ufsdump archive"
828			;;
829		"flar")
830				filetype="flar"
831				filetypename="flash archive"
832			;;
833		"flash")
834				filetype="flar"
835				filetypename="flash archive"
836			;;
837		*Flash\ Archive*)
838				filetype="flar"
839				filetypename="flash archive"
840			;;
841		"tar")
842				filetype="tar"
843				filetypename="tar archive"
844			;;
845		*USTAR\ tar\ archive)
846				filetype="tar"
847				filetypename="tar archive"
848			;;
849		"pax")
850				filetype="xustar"
851				filetypename="pax (xustar) archive"
852			;;
853		*USTAR\ tar\ archive\ extended\ format*)
854				filetype="xustar"
855				filetypename="pax (xustar) archive"
856			;;
857		"zfs")
858				filetype="zfs"
859				filetypename="ZFS send stream"
860			;;
861		*ZFS\ snapshot\ stream*)
862				filetype="zfs"
863				filetypename="ZFS send stream"
864			;;
865		*)		log "$e_unknown_archive"
866				fatal "$e_install_abort"
867			;;
868		esac
869	fi
870
871	vlog "$filetypename"
872
873	# Check for a non-empty root if no '-d -' option.
874	if [[ "$filetype" != "existing" ]]; then
875		cnt=$(ls $ZONEROOT | wc -l)
876		if (( $cnt != 0 )); then
877			fatal "$e_root_full" "$ZONEROOT"
878		fi
879	fi
880
881	fstmpfile=$(/usr/bin/mktemp -t -p /var/tmp)
882	if [[ -z "$fstmpfile" ]]; then
883		fatal "$e_tmpfile"
884	fi
885
886	# Make sure we always have the files holding the directories to filter
887	# out when extracting from a CPIO or PAX archive.  We'll add the fs
888	# entries to these files in get_fs_info()
889	fscpiofile=$(/usr/bin/mktemp -t -p /var/tmp fs.cpio.XXXXXX)
890	if [[ -z "$fscpiofile" ]]; then
891		rm -f $fstmpfile
892		fatal "$e_tmpfile"
893	fi
894
895	# Filter out these directories.
896	echo 'dev/*' >>$fscpiofile
897	echo 'devices/*' >>$fscpiofile
898	echo 'devices' >>$fscpiofile
899	echo 'proc/*' >>$fscpiofile
900	echo 'tmp/*' >>$fscpiofile
901	echo 'var/run/*' >>$fscpiofile
902	echo 'system/contract/*' >>$fscpiofile
903	echo 'system/object/*' >>$fscpiofile
904
905	fspaxfile=$(/usr/bin/mktemp -t -p /var/tmp fs.pax.XXXXXX)
906	if [[ -z "$fspaxfile" ]]; then
907		rm -f $fstmpfile $fscpiofile
908		fatal "$e_tmpfile"
909	fi
910
911	printf "%s " \
912	    "dev devices proc tmp var/run system/contract system/object" \
913	    >>$fspaxfile
914
915	# Set up any fs mounts so the archive will install into the correct
916	# locations.
917	get_fs_info
918	mnt_fs
919	if (( $? != 0 )); then
920		umnt_fs >/dev/null 2>&1
921		rm -f $fstmpfile $fscpiofile $fspaxfile
922		fatal "$mount_failed"
923	fi
924
925	if [[ "$filetype" == "existing" ]]; then
926		log "$no_installing"
927	else
928		log "$installing"
929	fi
930
931	#
932	# Install the image into the zonepath.
933	#
934	unpack_result=0
935	stage1="cat"
936	if [[ "$filetype" == "gzip" ]]; then
937		stage1="gzcat"
938		filetype="cpio"
939	elif [[ "$filetype" == "bzip2" ]]; then
940		stage1="bzcat"
941		filetype="cpio"
942	fi
943
944	if [[ "$filetype" == "cpio" ]]; then
945		install_cpio "$stage1" "$insrc"
946		unpack_result=$?
947
948	elif [[ "$filetype" == "flar" ]]; then
949		( cd "$ZONEROOT" && $stage1 $insrc | install_flar )
950		unpack_result=$?
951
952	elif [[ "$filetype" == "xustar" ]]; then
953		install_pax "$insrc"
954		unpack_result=$?
955
956	elif [[ "$filetype" = "tar" ]]; then
957		vlog "cd \"$ZONEROOT\" && tar -xf \"$insrc\""
958		# Ignore errors from tar since we expect some errors depending
959		# on how the archive was made.
960		( cd "$ZONEROOT" && tar -xf "$insrc" )
961		unpack_result=0
962		post_unpack
963
964	elif [[ "$filetype" == "ufsdump" ]]; then
965		install_ufsdump "$insrc"
966		unpack_result=$?
967
968	elif [[ "$filetype" == "directory" ]]; then
969		install_dir "$insrc"
970		unpack_result=$?
971
972	elif [[ "$filetype" == "zfs" ]]; then
973		#
974		# Given a 'zfs send' stream file, receive the snapshot into
975		# the zone's dataset.  We're getting the original system's
976		# zonepath dataset.  Destroy the existing dataset created
977		# above since this recreates it.
978		#
979		if [[ -z "$DATASET" ]]; then
980			fatal "$f_nodataset"
981		fi
982		/usr/sbin/zfs destroy "$DATASET"
983		if (( $? != 0 )); then
984			log "$f_zfsdestroy" "$DATASET"
985		fi
986
987		vlog "$stage1 $insrc | zfs receive -F $DATASET"
988		( $stage1 $insrc | /usr/sbin/zfs receive -F $DATASET )
989		unpack_result=$?
990	fi
991
992	# Clean up any fs mounts used during unpacking.
993	umnt_fs
994	rm -f $fstmpfile $fscpiofile $fspaxfile
995
996	chmod 700 $zonepath
997
998	(( $unpack_result != 0 )) && fatal "$f_unpack_failed"
999
1000	# Verify this is a valid image.
1001	sanity_check $ZONEROOT
1002
1003	return 0
1004}
1005
1006# Setup i18n output
1007TEXTDOMAIN="SUNW_OST_OSCMD"
1008export TEXTDOMAIN
1009
1010e_cannot_wrap=$(gettext "%s: error: wrapper file already exists")
1011e_baddir=$(gettext "Invalid '%s' directory within the zone")
1012e_badfile=$(gettext "Invalid '%s' file within the zone")
1013e_path_abs=$(gettext "Pathname specified to -a '%s' must be absolute.")
1014e_not_found=$(gettext "%s: error: file or directory not found.")
1015e_install_abort=$(gettext "Installation aborted.")
1016e_not_readable=$(gettext "Cannot read directory '%s'")
1017e_not_dir=$(gettext "Error: must be a directory")
1018e_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.")
1019e_absolute_archive=$(gettext "Error: archive contains absolute paths instead of relative paths.")
1020e_mismatch_archive=$(gettext "Error: the archive top-level directory (%s) does not match the zonepath (%s).")
1021e_tmpfile=$(gettext "Unable to create temporary file")
1022e_root_full=$(gettext "Zonepath root %s exists and contains data; remove or move aside prior to install.")
1023f_mkdir=$(gettext "Unable to create directory %s.")
1024f_chmod=$(gettext "Unable to chmod directory %s.")
1025f_chown=$(gettext "Unable to chown directory %s.")
1026f_hwcap_info=$(gettext "HWCAP: %s\n")
1027f_sanity_hwcap=$(gettext \
1028"The image was created with an incompatible libc.so.1 hwcap lofs mount.\n"\
1029"       The zone will not boot on this platform.  See the zone's\n"\
1030"       documentation for the recommended way to create the archive.")
1031
1032m_analyse_archive=$(gettext "Analysing the archive")
1033
1034not_readable=$(gettext "Cannot read file '%s'")
1035not_flar=$(gettext "Input is not a flash archive")
1036bad_flar=$(gettext "Flash archive is a corrupt")
1037bad_zfs_flar=$(gettext "Flash archive contains a ZFS send stream.\n\tRecreate the flar using the -L option with cpio or pax.")
1038f_unpack_failed=$(gettext "Unpacking the archive failed")
1039unknown_archiver=$(gettext "Archiver %s is not supported")
1040cmd_not_exec=$(gettext "Required command '%s' not executable!")
1041
1042#
1043# Exit values used by the script, as #defined in <sys/zone.h>
1044#
1045#	ZONE_SUBPROC_OK
1046#	===============
1047#	Installation was successful
1048#
1049#	ZONE_SUBPROC_USAGE
1050#	==================
1051#	Improper arguments were passed, so print a usage message before exiting
1052#
1053#	ZONE_SUBPROC_NOTCOMPLETE
1054#	========================
1055#	Installation did not complete, but another installation attempt can be
1056#	made without an uninstall
1057#
1058#	ZONE_SUBPROC_FATAL
1059#	==================
1060#	Installation failed and an uninstall will be required before another
1061#	install can be attempted
1062#
1063ZONE_SUBPROC_OK=0
1064ZONE_SUBPROC_USAGE=253
1065ZONE_SUBPROC_NOTCOMPLETE=254
1066ZONE_SUBPROC_FATAL=255
1067
1068