common.ksh revision 12734:76969fc28795
1212793Sdim#
2212793Sdim# CDDL HEADER START
3212793Sdim#
4212793Sdim# The contents of this file are subject to the terms of the
5212793Sdim# Common Development and Distribution License (the "License").
6212793Sdim# You may not use this file except in compliance with the License.
7212793Sdim#
8212793Sdim# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9212793Sdim# or http://www.opensolaris.org/os/licensing.
10212793Sdim# See the License for the specific language governing permissions
11212793Sdim# and limitations under the License.
12212793Sdim#
13212793Sdim# When distributing Covered Code, include this CDDL HEADER in each
14212793Sdim# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15249423Sdim# If applicable, add the following below this CDDL HEADER, with the
16296417Sdim# fields enclosed by brackets "[]" replaced with your own identifying
17218893Sdim# information: Portions Copyright [yyyy] [name of copyright owner]
18212793Sdim#
19276479Sdim# CDDL HEADER END
20249423Sdim#
21249423Sdim# Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
22249423Sdim#
23288943Sdim
24249423Sdim#
25249423Sdim# Send the error message to the screen and to the logfile.
26249423Sdim#
27212793Sdimerror()
28212793Sdim{
29212793Sdim        typeset fmt="$1"
30276479Sdim        shift
31276479Sdim
32212793Sdim        printf "${MSG_PREFIX}ERROR: ${fmt}\n" "$@"
33212793Sdim        [[ -n $LOGFILE ]] && printf "[$(date)] ERROR: ${fmt}\n" "$@" >&2
34212793Sdim}
35212793Sdim
36296417Sdimfatal()
37234353Sdim{
38212793Sdim        typeset fmt="$1"
39212793Sdim        shift
40212793Sdim
41212793Sdim	error "$fmt" "$@"
42218893Sdim	exit $EXIT_CODE
43212793Sdim}
44212793Sdim
45212793Sdimfail_fatal() {
46212793Sdim	printf "ERROR: "
47234353Sdim	printf "$@"
48296417Sdim	printf "\n"
49218893Sdim	exit $ZONE_SUBPROC_FATAL
50296417Sdim}
51296417Sdim
52296417Sdim#
53296417Sdim# Send the provided printf()-style arguments to the screen and to the logfile.
54212793Sdim#
55212793Sdimlog()
56218893Sdim{
57218893Sdim        typeset fmt="$1"
58218893Sdim        shift
59218893Sdim
60276479Sdim        printf "${MSG_PREFIX}${fmt}\n" "$@"
61218893Sdim        [[ -n $LOGFILE ]] && printf "[$(date)] ${MSG_PREFIX}${fmt}\n" "$@" >&2
62276479Sdim}
63212793Sdim
64296417Sdim#
65212793Sdim# Print provided text to the screen if the shell variable "OPT_V" is set.
66212793Sdim# The text is always sent to the logfile.
67212793Sdim#
68212793Sdimvlog()
69212793Sdim{
70218893Sdim        typeset fmt="$1"
71218893Sdim        shift
72218893Sdim
73218893Sdim        [[ -n $OPT_V ]] && printf "${MSG_PREFIX}${fmt}\n" "$@"
74218893Sdim        [[ -n $LOGFILE ]] && printf "[$(date)] ${MSG_PREFIX}${fmt}\n" "$@" >&2
75212793Sdim}
76212793Sdim
77212793Sdim#
78212793Sdim# Validate that the directory is safe.
79212793Sdim#
80212793Sdim# It is possible for a malicious zone root user to modify a zone's filesystem
81212793Sdim# so that modifications made to the zone's filesystem by administrators in the
82212793Sdim# global zone modify the global zone's filesystem.  We can prevent this by
83212793Sdim# ensuring that all components of paths accessed by scripts are real (i.e.,
84218893Sdim# non-symlink) directories.
85280031Sdim#
86212793Sdim# NOTE: The specified path should be an absolute path as would be seen from
87218893Sdim# within the zone.  Also, this function does not check parent directories.
88212793Sdim# If, for example, you need to ensure that every component of the path
89212793Sdim# '/foo/bar/baz' is a directory and not a symlink, then do the following:
90218893Sdim#
91218893Sdim#	safe_dir /foo
92218893Sdim#	safe_dir /foo/bar
93218893Sdim#	safe_dir /foo/bar/baz
94218893Sdim#
95218893Sdimsafe_dir()
96218893Sdim{
97212793Sdim	typeset dir="$1"
98212793Sdim
99212793Sdim	if [[ -h $ZONEROOT/$dir || ! -d $ZONEROOT/$dir ]]; then
100218893Sdim		fatal "$e_baddir" "$dir"
101212793Sdim	fi
102212793Sdim}
103212793Sdim
104212793Sdim# Like safe_dir except the dir doesn't have to exist.
105212793Sdimsafe_opt_dir()
106218893Sdim{
107212793Sdim	typeset dir="$1"
108212793Sdim
109212793Sdim	[[ ! -e $ZONEROOT/$dir ]] && return
110212793Sdim
111218893Sdim	if [[ -h $ZONEROOT/$dir || ! -d $ZONEROOT/$dir ]]; then
112280031Sdim		fatal "$e_baddir" "$dir"
113218893Sdim	fi
114288943Sdim}
115288943Sdim
116288943Sdim# Only make a copy if we haven't already done so.
117288943Sdimsafe_backup()
118249423Sdim{
119249423Sdim	typeset src="$1"
120249423Sdim	typeset dst="$2"
121249423Sdim
122288943Sdim	if [[ ! -h $src && ! -h $dst && ! -d $dst && ! -f $dst ]]; then
123288943Sdim		/usr/bin/cp -p $src $dst || fatal "$e_badfile" "$src"
124288943Sdim	fi
125288943Sdim}
126288943Sdim
127288943Sdim# Make a copy even if the destination already exists.
128288943Sdimsafe_copy()
129288943Sdim{
130288943Sdim	typeset src="$1"
131288943Sdim	typeset dst="$2"
132288943Sdim
133288943Sdim	if [[ ! -h $src && ! -h $dst && ! -d $dst ]]; then
134288943Sdim		/usr/bin/cp -p $src $dst || fatal "$e_badfile" "$src"
135249423Sdim	fi
136288943Sdim}
137288943Sdim
138288943Sdim# Move a file
139288943Sdimsafe_move()
140288943Sdim{
141288943Sdim	typeset src="$1"
142288943Sdim	typeset dst="$2"
143249423Sdim
144288943Sdim	if [[ ! -h $src && ! -h $dst && ! -d $dst ]]; then
145288943Sdim		/usr/bin/mv $src $dst || fatal "$e_badfile" "$src"
146288943Sdim	fi
147288943Sdim}
148288943Sdim
149288943Sdimsafe_rm()
150288943Sdim{
151249423Sdim	if [[ ! -h $ZONEROOT/$1 && -f $ZONEROOT/$1 ]]; then
152249423Sdim		rm -f "$ZONEROOT/$1"
153249423Sdim	fi
154249423Sdim}
155212793Sdim
156212793Sdim#
157218893Sdim# Replace the file with a wrapper pointing to the native brand code.
158288943Sdim# However, we only do the replacement if the file hasn't already been
159288943Sdim# replaced with our wrapper.  This function expects the cwd to be the
160288943Sdim# location of the file we're replacing.
161218893Sdim#
162212793Sdim# Some of the files we're replacing are hardlinks to isaexec so we need to 'rm'
163212793Sdim# the file before we setup the wrapper while others are hardlinks to rc scripts
164212793Sdim# that we need to maintain.
165218893Sdim#
166234353Sdimsafe_replace()
167234353Sdim{
168218893Sdim	typeset filename="$1"
169212793Sdim	typeset runname="$2"
170212793Sdim	typeset mode="$3"
171212793Sdim	typeset own="$4"
172212793Sdim	typeset rem="$5"
173276479Sdim
174212793Sdim	if [ -h $filename -o ! -f $filename ]; then
175212793Sdim		return
176212793Sdim	fi
177212793Sdim
178218893Sdim	egrep -s "Solaris Brand Replacement" $filename
179212793Sdim	if [ $? -eq 0 ]; then
180218893Sdim		return
181280031Sdim	fi
182212793Sdim
183218893Sdim	safe_backup $filename $filename.pre_p2v
184212793Sdim	if [ $rem = "remove" ]; then
185212793Sdim		rm -f $filename
186212793Sdim	fi
187212793Sdim
188212793Sdim	cat <<-END >$filename || exit 1
189296417Sdim	#!/bin/sh -p
190296417Sdim	#
191296417Sdim	# Solaris Brand Replacement
192296417Sdim	#
193212793Sdim	# Attention.  This file has been replaced with a new version for
194212793Sdim	# use in a virtualized environment.  Modification of this script is not
195212793Sdim	# supported and all changes will be lost upon reboot.  The
196212793Sdim	# {name}.pre_p2v version of this file is a backup copy of the
197218893Sdim	# original and should not be deleted.
198296417Sdim	#
199296417Sdim	END
200296417Sdim
201296417Sdim	echo ". $runname \"\$@\"" >>$filename || exit 1
202296417Sdim
203296417Sdim	chmod $mode $filename
204296417Sdim	chown $own $filename
205296417Sdim}
206218893Sdim
207296417Sdimsafe_wrap()
208296417Sdim{
209212793Sdim	typeset filename="$1"
210212793Sdim	typeset runname="$2"
211212793Sdim	typeset mode="$3"
212212793Sdim	typeset own="$4"
213212793Sdim
214212793Sdim	if [ -f $filename ]; then
215212793Sdim		log "$e_cannot_wrap" "$filename"
216212793Sdim		exit 1
217212793Sdim	fi
218212793Sdim
219212793Sdim	cat <<-END >$filename || exit 1
220212793Sdim	#!/bin/sh
221234353Sdim	#
222234353Sdim	# Solaris Brand Wrapper
223234353Sdim	#
224234353Sdim	# Attention.  This file has been created for use in a
225234353Sdim	# virtualized environment.  Modification of this script
226234353Sdim	# is not supported and all changes will be lost upon reboot.
227234353Sdim	#
228234353Sdim	END
229234353Sdim
230234353Sdim	echo ". $runname \"\$@\"" >>$filename || exit 1
231234353Sdim
232234353Sdim	chmod $mode $filename
233234353Sdim	chown $own $filename
234234353Sdim}
235234353Sdim
236234353Sdim#
237234353Sdim# Read zonecfg fs entries and save the relevant data, one entry per
238234353Sdim# line.
239234353Sdim# This assumes the properties from the zonecfg output, e.g.:
240234353Sdim#	fs:
241234353Sdim#		dir: /opt
242234353Sdim#		special: /opt
243234353Sdim#		raw not specified
244234353Sdim#		type: lofs
245234353Sdim#		options: [noexec,ro,noatime]
246234353Sdim#
247234353Sdim# and it assumes the order of the fs properties as above.
248234353Sdim#
249234353Sdimget_fs_info()
250234353Sdim{
251234353Sdim	zonecfg -z $zonename info fs | nawk '{
252234353Sdim		if ($1 == "options:") {
253234353Sdim			# Remove brackets.
254280031Sdim			options=substr($2, 2, length($2) - 2);
255280031Sdim			printf("%s %s %s %s\n", dir, type, special, options);
256234353Sdim		} else if ($1 == "dir:") {
257234353Sdim			dir=$2;
258234353Sdim		} else if ($1 == "special:") {
259234353Sdim			special=$2;
260234353Sdim		} else if ($1 == "type:") {
261234353Sdim			type=$2
262234353Sdim		}
263234353Sdim	}' >> $fstmpfile
264234353Sdim}
265234353Sdim
266234353Sdim#
267234353Sdim# Mount zonecfg fs entries into the zonepath.
268234353Sdim#
269234353Sdimmnt_fs()
270234353Sdim{
271234353Sdim	if [ ! -s $fstmpfile ]; then
272234353Sdim		return;
273234353Sdim	fi
274234353Sdim
275234353Sdim	# Sort the fs entries so we can handle nested mounts.
276234353Sdim	sort $fstmpfile | nawk -v zonepath=$zonepath '{
277234353Sdim		if (NF == 4)
278234353Sdim			options="-o " $4;
279234353Sdim		else
280234353Sdim			options=""
281243830Sdim
282243830Sdim		# Create the mount point.  Ignore errors since we might have
283243830Sdim		# a nested mount with a pre-existing mount point.
284243830Sdim		cmd="/usr/bin/mkdir -p " zonepath "/root" $1 " >/dev/null 2>&1"
285243830Sdim		system(cmd);
286234353Sdim
287234353Sdim		cmd="/usr/sbin/mount -F " $2 " " options " " $3 " " \
288234353Sdim		    zonepath "/root" $1;
289234353Sdim		if (system(cmd) != 0) {
290234353Sdim			printf("command failed: %s\n", cmd);
291234353Sdim			exit 1;
292234353Sdim		}
293234353Sdim	}' >>$LOGFILE
294234353Sdim}
295234353Sdim
296234353Sdim#
297234353Sdim# Unmount zonecfg fs entries from the zonepath.
298234353Sdim#
299234353Sdimumnt_fs()
300234353Sdim{
301234353Sdim	if [ ! -s $fstmpfile ]; then
302234353Sdim		return;
303234353Sdim	fi
304234353Sdim
305234353Sdim	# Reverse sort the fs entries so we can handle nested unmounts.
306234353Sdim	sort -r $fstmpfile | nawk -v zonepath=$zonepath '{
307296417Sdim		cmd="/usr/sbin/umount " zonepath "/root" $1
308296417Sdim		if (system(cmd) != 0) {
309296417Sdim			printf("command failed: %s\n", cmd);
310296417Sdim		}
311296417Sdim	}' >>$LOGFILE
312296417Sdim}
313296417Sdim
314296417Sdim# Find the dataset mounted on the zonepath.
315296417Sdimget_zonepath_ds() {
316296417Sdim	ZONEPATH_DS=`/usr/sbin/zfs list -H -t filesystem -o name,mountpoint | \
317296417Sdim	    /usr/bin/nawk -v zonepath=$1 '{
318296417Sdim		if ($2 == zonepath)
319296417Sdim			print $1
320296417Sdim	}'`
321296417Sdim
322296417Sdim	if [ -z "$ZONEPATH_DS" ]; then
323296417Sdim		fail_fatal "$f_no_ds"
324296417Sdim	fi
325296417Sdim}
326296417Sdim
327296417Sdim#
328296417Sdim# Perform any cleanup in the zoneroot after unpacking the archive.
329296417Sdim#
330296417Sdimpost_unpack()
331296417Sdim{
332296417Sdim	( cd "$ZONEROOT" && \
333296417Sdim	    find . \( -type b -o -type c \) -exec rm -f "{}" \; )
334296417Sdim}
335296417Sdim
336296417Sdim#
337296417Sdim# Determine flar compression style from identification file.
338296417Sdim#
339296417Sdimget_compression()
340296417Sdim{
341296417Sdim	typeset ident=$1
342296417Sdim	typeset line=$(grep "^files_compressed_method=" $ident)
343296417Sdim
344296417Sdim	print ${line##*=}
345296417Sdim}
346296417Sdim
347296417Sdim#
348296417Sdim# Determine flar archive style from identification file.
349296417Sdim#
350296417Sdimget_archiver()
351296417Sdim{
352296417Sdim        typeset ident=$1
353296417Sdim        typeset line=$(grep "^files_archived_method=" $ident)
354296417Sdim
355296417Sdim        print ${line##*=}
356296417Sdim}
357296417Sdim
358296417Sdim#
359296417Sdim# Unpack flar into current directory (which should be zoneroot).  The flash
360212793Sdim# archive is standard input.  See flash_archive(4) man page.
361276479Sdim# 
362276479Sdim# We can't use "flar split" since it will only unpack into a directory called
363276479Sdim# "archive".  We need to unpack in place in order to properly handle nested
364212793Sdim# fs mounts within the zone root.  This function does the unpacking into the
365218893Sdim# current directory.
366212793Sdim#
367218893Sdim# This code is derived from the gen_split() function in /usr/sbin/flar so
368212793Sdim# we keep the same style as the original.
369212793Sdim#
370212793Sdiminstall_flar()
371296417Sdim{
372212793Sdim	typeset result
373212793Sdim        typeset archiver_command
374212793Sdim        typeset archiver_arguments
375212793Sdim
376212793Sdim	vlog "cd $ZONEROOT && $stage1 "$insrc" | install_flar"
377212793Sdim
378212793Sdim	# Read cookie
379212793Sdim	read -r input_line
380212793Sdim	if (( $? != 0 )); then
381212793Sdim		log "$not_readable" "$install_media"
382212793Sdim		return 1
383212793Sdim	fi
384212793Sdim	# The cookie has format FlAsH-aRcHiVe-m.n where m and n are integers.
385212793Sdim	if [[ ${input_line%%-[0-9]*.[0-9]*} != "FlAsH-aRcHiVe" ]]; then
386212793Sdim		log "$not_flar"
387296417Sdim		return 1
388296417Sdim	fi
389296417Sdim
390296417Sdim	while [ true ]
391212793Sdim	do
392212793Sdim		# We should always be at the start of a section here
393218893Sdim		read -r input_line
394234353Sdim		if [[ ${input_line%%=*} != "section_begin" ]]; then
395234353Sdim			log "$bad_flar"
396234353Sdim			return 1
397234353Sdim		fi
398234353Sdim		section_name=${input_line##*=}
399296417Sdim
400296417Sdim		# If we're at the archive, we're done skipping sections.
401296417Sdim		if [[ "$section_name" == "archive" ]]; then
402296417Sdim			break
403296417Sdim		fi
404296417Sdim		
405296417Sdim		#
406296417Sdim		# Save identification section to a file so we can determine
407296417Sdim		# how to unpack the archive.
408296417Sdim		#
409296417Sdim		if [[ "$section_name" == "identification" ]]; then
410296417Sdim			/usr/bin/rm -f identification
411296417Sdim			while read -r input_line
412234353Sdim			do
413296417Sdim				if [[ ${input_line%%=*} == \
414234353Sdim				    "section_begin" ]]; then
415212793Sdim					/usr/bin/rm -f identification
416212793Sdim					log "$bad_flar"
417218893Sdim					return 1
418212793Sdim				fi
419212793Sdim
420				if [[ $input_line == \
421				    "section_end=$section_name" ]]; then
422					break;
423				fi
424				echo $input_line >> identification
425			done
426
427			continue
428		fi
429
430		#
431		# Otherwise skip past this section; read lines until detecting
432		# section_end.  According to flash_archive(4) we can have
433		# an arbitrary number of sections but the archive section
434		# must be last.
435		#
436		success=0
437		while read -r input_line
438		do
439			if [[ $input_line == "section_end=$section_name" ]];
440			then
441				success=1
442				break
443			fi
444			# Fail if we miss the end of the section
445			if [[ ${input_line%%=*} == "section_begin" ]]; then
446				/usr/bin/rm -f identification
447				log "$bad_flar"
448				return 1
449			fi
450		done
451		if (( $success == 0 )); then
452			#
453			# If we get here we read to the end of the file before
454			# seeing the end of the section we were reading.
455			#
456			/usr/bin/rm -f identification
457			log "$bad_flar"
458			return 1
459		fi
460	done
461
462	# Check for an archive made from a ZFS root pool.
463	egrep -s "^rootpool=" identification
464        if (( $? == 0 )); then
465		/usr/bin/rm -f identification
466                log "$bad_zfs_flar"
467                return 1
468        fi
469
470	# Get the information needed to unpack the archive.
471	archiver=$(get_archiver identification)
472	if [[ $archiver == "pax" ]]; then
473		# pax archiver specified
474		archiver_command="/usr/bin/pax"
475		if [[ -s $fspaxfile ]]; then
476			archiver_arguments="-r -p e -c \
477			    $(/usr/bin/cat $fspaxfile)"
478		else
479			archiver_arguments="-r -p e"
480		fi
481	elif [[ $archiver == "cpio" || -z $archiver ]]; then
482		# cpio archived specified OR no archiver specified - use default
483		archiver_command="/usr/bin/cpio"
484		archiver_arguments="-icdumfE $fscpiofile"
485	else
486		# unknown archiver specified
487		log "$unknown_archiver" $archiver
488		return 1
489	fi
490
491	if [[ ! -x $archiver_command ]]; then
492		/usr/bin/rm -f identification
493		log "$cmd_not_exec" $archiver_command
494		return 1
495	fi 
496
497	compression=$(get_compression identification)
498
499	# We're done with the identification file
500	/usr/bin/rm -f identification
501
502	# Extract archive
503	if [[ $compression == "compress" ]]; then
504		/usr/bin/zcat | \
505		    $archiver_command $archiver_arguments 2>/dev/null
506	else
507		$archiver_command $archiver_arguments 2>/dev/null
508	fi
509	result=$?
510
511	post_unpack
512
513	(( $result != 0 )) && return 1
514
515	return 0 
516}
517
518#
519# Get the archive base.
520#
521# We must unpack the archive in the right place within the zonepath so
522# that files are installed into the various mounted filesystems that are set
523# up in the zone's configuration.  These are already mounted for us by the
524# mntfs function.
525#
526# Archives can be made of either a physical host's root file system or a
527# zone's zonepath.  For a physical system, if the archive is made using an
528# absolute path (/...) we can't use it.  For a zone the admin can make the
529# archive from a variety of locations;
530#
531#   a) zonepath itself: This will be a single dir, probably named with the
532#      zone name, it will contain a root dir and under the root we'll see all
533#      the top level dirs; etc, var, usr...  We must be above the ZONEPATH
534#      when we unpack the archive but this will only work if the the archive's
535#      top-level dir name matches the ZONEPATH base-level dir name.  If not,
536#      this is an error.
537#
538#   b) inside the zonepath: We'll see root and it will contain all the top
539#      level dirs; etc, var, usr....  We must be in the ZONEPATH when we unpack
540#      the archive.
541#
542#   c) inside the zonepath root: We'll see all the top level dirs, ./etc,
543#      ./var, ./usr....  This is also the case we see when we get an archive
544#      of a physical sytem.  We must be in ZONEROOT when we unpack the archive.
545#
546# Note that there can be a directory named "root" under the ZONEPATH/root
547# directory.
548#
549# This function handles the above possibilities so that we reject absolute
550# path archives and figure out where in the file system we need to be to
551# properly unpack the archive into the zone.  It sets the ARCHIVE_BASE
552# variable to the location where the achive should be unpacked.
553#
554get_archive_base()
555{
556	stage1=$1
557	archive=$2
558	stage2=$3
559
560	vlog "$m_analyse_archive"
561
562	base=`$stage1 $archive | $stage2 2>/dev/null | nawk -F/ '{
563		# Check for an absolute path archive
564		if (substr($0, 1, 1) == "/")
565			exit 1
566
567		if ($1 != ".")
568			dirs[$1] = 1
569		else
570			dirs[$2] = 1
571	}
572	END {
573		for (d in dirs) {
574			cnt++
575			if (d == "bin")  sawbin = 1
576			if (d == "etc")  sawetc = 1
577			if (d == "root") sawroot = 1
578			if (d == "var")  sawvar = 1
579                }
580
581		if (cnt == 1) {
582			# If only one top-level dir named root, we are in the
583			# zonepath, otherwise this must be an archive *of*
584			# the zonepath so print the top-level dir name.
585			if (sawroot)
586				print "*zonepath*"
587			else
588				for (d in dirs) print d
589		} else {
590			# We are either in the zonepath or in the zonepath/root
591			# (or at the top level of a full system archive which
592			# looks like the zonepath/root case).  Figure out which
593			# one.
594			if (sawroot && !sawbin && !sawetc && !sawvar)
595				print "*zonepath*"
596			else
597				print "*zoneroot*"
598		}
599	}'`
600
601	if (( $? != 0 )); then
602		umnt_fs
603		fatal "$e_absolute_archive"
604	fi
605
606	if [[ "$base" == "*zoneroot*" ]]; then
607		ARCHIVE_BASE=$ZONEROOT
608	elif [[ "$base" == "*zonepath*" ]]; then
609		ARCHIVE_BASE=$ZONEPATH
610	else
611		# We need to be in the dir above the ZONEPATH but we need to
612		# validate that $base matches the final component of ZONEPATH.
613		bname=`basename $ZONEPATH`
614
615		if [[ "$bname" != "$base" ]]; then
616			umnt_fs
617			fatal "$e_mismatch_archive" "$base" "$bname"
618		fi
619		ARCHIVE_BASE=`dirname $ZONEPATH`
620	fi
621}
622
623#
624# Unpack cpio archive into zoneroot.
625#
626install_cpio()
627{
628	stage1=$1
629	archive=$2
630
631	get_archive_base "$stage1" "$archive" "cpio -it"
632
633	cpioopts="-idmfE $fscpiofile"
634
635	vlog "cd \"$ARCHIVE_BASE\" && $stage1 \"$archive\" | cpio $cpioopts"
636
637	# Ignore errors from cpio since we expect some errors depending on
638	# how the archive was made.
639	( cd "$ARCHIVE_BASE" && $stage1 "$archive" | cpio $cpioopts )
640
641	post_unpack
642
643	return 0
644}
645
646#
647# Unpack pax archive into zoneroot.
648#
649install_pax()
650{
651	archive=$1
652
653	get_archive_base "cat" "$archive" "pax"
654
655	if [[ -s $fspaxfile ]]; then
656		filtopt="-c $(/usr/bin/cat $fspaxfile)"
657	fi
658
659	vlog "cd \"$ARCHIVE_BASE\" && pax -r -f \"$archive\" $filtopt"
660
661	# Ignore errors from pax since we expect some errors depending on
662	# how the archive was made.
663	( cd "$ARCHIVE_BASE" && pax -r -f "$archive" $filtopt )
664
665	post_unpack
666
667	return 0
668}
669
670#
671# Unpack UFS dump into zoneroot.
672#
673install_ufsdump()
674{
675	archive=$1
676
677	vlog "cd \"$ZONEROOT\" && ufsrestore rf \"$archive\""
678
679	#
680	# ufsrestore goes interactive if you ^C it.  To prevent that,
681	# we make sure its stdin is not a terminal.
682	#
683	( cd "$ZONEROOT" && ufsrestore rf "$archive" < /dev/null )
684	result=$?
685
686	post_unpack
687
688	return $result
689}
690
691#
692# Copy directory hierarchy into zoneroot.
693#
694install_dir()
695{
696	source_dir=$1
697
698	cpioopts="-pdm"
699
700	first=1
701	filt=$(for i in $(cat $fspaxfile)
702		do
703			echo $i | egrep -s "/" && continue
704			if [[ $first == 1 ]]; then
705				printf "^%s" $i
706				first=0
707			else
708				printf "|^%s" $i
709			fi
710		done)
711
712	list=$(cd "$source_dir" && ls -d * | egrep -v "$filt")
713	flist=$(for i in $list
714	do
715		printf "%s " "$i"
716	done)
717	findopts="-xdev ( -type d -o -type f -o -type l ) -print"
718
719	vlog "cd \"$source_dir\" && find $flist $findopts | "
720	vlog "cpio $cpioopts \"$ZONEROOT\""
721
722	# Ignore errors from cpio since we expect some errors depending on
723	# how the archive was made.
724	( cd "$source_dir" && find $flist $findopts | \
725	    cpio $cpioopts "$ZONEROOT" )
726
727	post_unpack
728
729	return 0
730}
731
732#
733# This is a common function for laying down a zone image from a variety of
734# different sources.  This can be used to either install a fresh zone or as
735# part of zone migration during attach.
736#
737# The first argument specifies the type of image: archive, directory or stdin.
738# The second argument specifies the image itself.  In the case of stdin, the
739# second argument specifies the format of the stream (cpio, flar, etc.).
740# Any validation or post-processing on the image is done elsewhere.
741#
742# This function calls a 'sanity_check' function which must be provided by
743# the script which includes this code.
744#
745install_image()
746{
747	intype=$1
748	insrc=$2
749
750	if [[ -z "$intype" || -z "$insrc" ]]; then
751		return 1
752	fi
753
754	filetype="unknown"
755	filetypename="unknown"
756	stage1="cat"
757
758	if [[ "$intype" == "directory" ]]; then
759		if [[ "$insrc" == "-" ]]; then
760			# Indicates that the existing zonepath is prepopulated.
761			filetype="existing"
762			filetypename="existing"
763		else
764			if [[ "$(echo $insrc | cut -c 1)" != "/" ]]; then
765				fatal "$e_path_abs" "$insrc"
766			fi
767
768			if [[ ! -e "$insrc" ]]; then
769				log "$e_not_found" "$insrc"
770				fatal "$e_install_abort"
771			fi
772
773			if [[ ! -r "$insrc" ]]; then
774				log "$e_not_readable" "$insrc"
775				fatal "$e_install_abort"
776			fi
777
778			if [[ ! -d "$insrc" ]]; then
779				log "$e_not_dir"
780				fatal "$e_install_abort"
781			fi
782
783			sanity_check $insrc
784
785			filetype="directory"
786			filetypename="directory"
787		fi
788
789	else
790		# Common code for both archive and stdin stream.
791
792		if [[ "$intype" == "archive" ]]; then
793			if [[ ! -f "$insrc" ]]; then
794				log "$e_unknown_archive"
795				fatal "$e_install_abort"
796			fi
797			ftype="$(LC_ALL=C file $insrc | cut -d: -f 2)"
798		else
799			# For intype == stdin, the insrc parameter specifies
800			# the stream format coming on stdin.
801			ftype="$insrc"
802			insrc="-"
803		fi
804
805		# Setup vars for the archive type we have.
806		case "$ftype" in
807		*cpio*)		filetype="cpio"
808				filetypename="cpio archive"
809			;;
810		*bzip2*)	filetype="bzip2"
811				filetypename="bzipped cpio archive"
812			;;
813		*gzip*)		filetype="gzip"
814				filetypename="gzipped cpio archive"
815			;;
816		*ufsdump*)	filetype="ufsdump"
817				filetypename="ufsdump archive"
818			;;
819		"flar")
820				filetype="flar"
821				filetypename="flash archive"
822			;;
823		"flash")
824				filetype="flar"
825				filetypename="flash archive"
826			;;
827		*Flash\ Archive*)
828				filetype="flar"
829				filetypename="flash archive"
830			;;
831		"tar")
832				filetype="tar"
833				filetypename="tar archive"
834			;;
835		*USTAR\ tar\ archive)
836				filetype="tar"
837				filetypename="tar archive"
838			;;
839		"pax")
840				filetype="xustar"
841				filetypename="pax (xustar) archive"
842			;;
843		*USTAR\ tar\ archive\ extended\ format*)
844				filetype="xustar"
845				filetypename="pax (xustar) archive"
846			;;
847		"zfs")
848				filetype="zfs"
849				filetypename="ZFS send stream"
850			;;
851		*ZFS\ snapshot\ stream*)
852				filetype="zfs"
853				filetypename="ZFS send stream"
854			;;
855		*)		log "$e_unknown_archive"
856				fatal "$e_install_abort"
857			;;
858		esac
859	fi
860
861	vlog "$filetypename"
862
863	# Check for a non-empty root if no '-d -' option. 
864	if [[ "$filetype" != "existing" ]]; then
865		cnt=$(ls $ZONEROOT | wc -l)
866		if (( $cnt != 0 )); then
867			fatal "$e_root_full" "$ZONEROOT"
868		fi
869	fi
870
871	fstmpfile=$(/usr/bin/mktemp -t -p /var/tmp)
872	if [[ -z "$fstmpfile" ]]; then
873		fatal "$e_tmpfile"
874	fi
875
876	# Make sure we always have the files holding the directories to filter
877	# out when extracting from a CPIO or PAX archive.  We'll add the fs
878	# entries to these files in get_fs_info()
879	fscpiofile=$(/usr/bin/mktemp -t -p /var/tmp fs.cpio.XXXXXX)
880	if [[ -z "$fscpiofile" ]]; then
881		rm -f $fstmpfile
882		fatal "$e_tmpfile"
883	fi
884
885	# Filter out these directories.
886	echo 'dev/*' >>$fscpiofile
887	echo 'devices/*' >>$fscpiofile
888	echo 'devices' >>$fscpiofile
889	echo 'proc/*' >>$fscpiofile
890	echo 'tmp/*' >>$fscpiofile
891	echo 'var/run/*' >>$fscpiofile
892	echo 'system/contract/*' >>$fscpiofile
893	echo 'system/object/*' >>$fscpiofile
894
895	fspaxfile=$(/usr/bin/mktemp -t -p /var/tmp fs.pax.XXXXXX)
896	if [[ -z "$fspaxfile" ]]; then
897		rm -f $fstmpfile $fscpiofile
898		fatal "$e_tmpfile"
899	fi
900
901	printf "%s " \
902	    "dev devices proc tmp var/run system/contract system/object" \
903	    >>$fspaxfile
904
905	# Set up any fs mounts so the archive will install into the correct
906	# locations.
907	get_fs_info
908	mnt_fs
909	if (( $? != 0 )); then
910		umnt_fs >/dev/null 2>&1
911		rm -f $fstmpfile $fscpiofile $fspaxfile
912		fatal "$mount_failed"
913	fi
914
915	if [[ "$filetype" == "existing" ]]; then
916		log "$no_installing"
917	else
918		log "$installing"
919	fi
920
921	#
922	# Install the image into the zonepath.
923	#
924	unpack_result=0
925	stage1="cat"
926	if [[ "$filetype" == "gzip" ]]; then
927		stage1="gzcat"
928		filetype="cpio"
929	elif [[ "$filetype" == "bzip2" ]]; then
930		stage1="bzcat"
931		filetype="cpio"
932	fi
933
934	if [[ "$filetype" == "cpio" ]]; then
935		install_cpio "$stage1" "$insrc"
936		unpack_result=$?
937
938	elif [[ "$filetype" == "flar" ]]; then
939		( cd "$ZONEROOT" && $stage1 $insrc | install_flar )
940		unpack_result=$?
941
942	elif [[ "$filetype" == "xustar" ]]; then
943		install_pax "$insrc"
944		unpack_result=$?
945
946	elif [[ "$filetype" = "tar" ]]; then
947		vlog "cd \"$ZONEROOT\" && tar -xf \"$insrc\""
948		# Ignore errors from tar since we expect some errors depending
949		# on how the archive was made.
950		( cd "$ZONEROOT" && tar -xf "$insrc" )
951		unpack_result=0
952		post_unpack
953
954	elif [[ "$filetype" == "ufsdump" ]]; then
955		install_ufsdump "$insrc"
956		unpack_result=$?
957
958	elif [[ "$filetype" == "directory" ]]; then
959		install_dir "$insrc"
960		unpack_result=$?
961
962	elif [[ "$filetype" == "zfs" ]]; then
963		#
964		# Given a 'zfs send' stream file, receive the snapshot into
965		# the zone's dataset.  We're getting the original system's
966		# zonepath dataset.  Destroy the existing dataset created
967		# above since this recreates it.
968		#
969		if [[ -z "$DATASET" ]]; then
970			fatal "$f_nodataset"
971		fi
972		/usr/sbin/zfs destroy "$DATASET"
973		if (( $? != 0 )); then
974			log "$f_zfsdestroy" "$DATASET"
975		fi
976
977		vlog "$stage1 $insrc | zfs receive -F $DATASET"
978		( $stage1 $insrc | /usr/sbin/zfs receive -F $DATASET )
979		unpack_result=$?
980	fi
981
982	# Clean up any fs mounts used during unpacking.
983	umnt_fs
984	rm -f $fstmpfile $fscpiofile $fspaxfile
985
986	chmod 700 $zonepath
987
988	(( $unpack_result != 0 )) && fatal "$f_unpack_failed"
989
990	# Verify this is a valid image.
991	sanity_check $ZONEROOT
992
993	return 0
994}
995
996# Setup i18n output
997TEXTDOMAIN="SUNW_OST_OSCMD"
998export TEXTDOMAIN
999
1000e_cannot_wrap=$(gettext "%s: error: wrapper file already exists")
1001e_baddir=$(gettext "Invalid '%s' directory within the zone")
1002e_badfile=$(gettext "Invalid '%s' file within the zone")
1003e_path_abs=$(gettext "Pathname specified to -a '%s' must be absolute.")
1004e_not_found=$(gettext "%s: error: file or directory not found.")
1005e_install_abort=$(gettext "Installation aborted.")
1006e_not_readable=$(gettext "Cannot read directory '%s'")
1007e_not_dir=$(gettext "Error: must be a directory")
1008e_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.")
1009e_absolute_archive=$(gettext "Error: archive contains absolute paths instead of relative paths.")
1010e_mismatch_archive=$(gettext "Error: the archive top-level directory (%s) does not match the zonepath (%s).")
1011e_tmpfile=$(gettext "Unable to create temporary file")
1012e_root_full=$(gettext "Zonepath root %s exists and contains data; remove or move aside prior to install.")
1013f_mkdir=$(gettext "Unable to create directory %s.")
1014f_chmod=$(gettext "Unable to chmod directory %s.")
1015f_chown=$(gettext "Unable to chown directory %s.")
1016
1017
1018m_analyse_archive=$(gettext "Analysing the archive")
1019
1020not_readable=$(gettext "Cannot read file '%s'")
1021not_flar=$(gettext "Input is not a flash archive")
1022bad_flar=$(gettext "Flash archive is a corrupt")
1023bad_zfs_flar=$(gettext "Flash archive contains a ZFS send stream.\n\tRecreate the flar using the -L option with cpio or pax.")
1024f_unpack_failed=$(gettext "Unpacking the archive failed")
1025unknown_archiver=$(gettext "Archiver %s is not supported")
1026cmd_not_exec=$(gettext "Required command '%s' not executable!")
1027
1028#
1029# Exit values used by the script, as #defined in <sys/zone.h>
1030#
1031#	ZONE_SUBPROC_OK
1032#	===============
1033#	Installation was successful
1034#
1035#	ZONE_SUBPROC_USAGE
1036#	==================
1037#	Improper arguments were passed, so print a usage message before exiting
1038#
1039#	ZONE_SUBPROC_NOTCOMPLETE
1040#	========================
1041#	Installation did not complete, but another installation attempt can be
1042#	made without an uninstall
1043#
1044#	ZONE_SUBPROC_FATAL
1045#	==================
1046#	Installation failed and an uninstall will be required before another
1047#	install can be attempted
1048#
1049ZONE_SUBPROC_OK=0
1050ZONE_SUBPROC_USAGE=253
1051ZONE_SUBPROC_NOTCOMPLETE=254
1052ZONE_SUBPROC_FATAL=255
1053
1054