#!/bin/sh

# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
# Use is subject to license terms.
#
# ident	"@(#)lumount_zones.sh	1.7	09/07/19 SMI"
#
# This is a helper script for lumount and luumount.  It mounts and
# unmounts zones in the specified boot environment.

PATH=/usr/bin:/usr/sbin:/sbin
export PATH

LUBIN=${LUBIN:=/usr/lib/lu}
. $LUBIN/lulib

force_mount=false
do_unmount=false
while getopts fru c; do
	case $c in
	f)	force_mount=true;;
	r)	;;
	u)	do_unmount=true;;

	*)	$LUPRINTF -Eelp2 "`gettext 'unknown option; use [-fru]'`"
		exit 1;;
	esac
done
shift `/bin/expr $OPTIND - 1`

if [ $# -ne 1 ]; then
	$LUPRINTF -Eel2 "`gettext 'usage: lumount_zones [-fru] <root-path>'`"
	exit 1
fi

BE_ROOT="$1"

if [ ! -d "$BE_ROOT" ]; then
	$LUPRINTF -Eelp2 "`gettext '<%s> is not a directory'`" "$BE_ROOT"
	exit 1
fi

case "$BE_ROOT" in
/*)	;;
*)	$LUPRINTF -Eelp2 "`gettext '<%s> is not an absolute path'`" "$BE_ROOT"
	exit 1;;
esac

lulib_zone_check "$BE_ROOT"

# discover the BE name
if [ -f "$BE_ROOT/etc/lu/.BE_CONFIG" ]; then
    . "$BE_ROOT/etc/lu/.BE_CONFIG"
    BE_NAME="$LUBECF_BE_NAME"
else
    # no zones to mount or unmount
    exit 0
fi

SAVEIFS="$IFS"

if $do_unmount; then
	# Undo any mounts related to this ABE on the running system.
	lulib_list_zones |
	while IFS=: read zoneid zonename status zonepath uuid more; do
		IFS="$SAVEIFS"
		if [ $zonename = global ]; then
			continue
		fi
		if [ $status = mounted ]; then
			dir="$zonepath/lu$BE_ROOT"
		else
			dir="$zonepath/root$BE_ROOT"
		fi
		[ -d "$dir" ] || continue
		# Kill pkgserv, if it runs
		pkgadm sync -R "$dir" -q > /dev/null 2>&1
		# Unmount everything under '$dir'
		mount -p | fgrep "$dir" | sort -r -k 3,3 -t' ' |
		while read special raw mountpoint fstype fsckpass atboot \
		    mountopt more
		do
			if lulib_is_subdir $mountpoint "$dir"; then
				lulib_unmount_pathname $mountpoint
			fi
		done
		rmdir "$dir" || exit 1
	done
	# Unmount the zones in the ABE
	[ -x /sbin/zfs ] && zfs list -t filesystem -Ho name,zpdata:zn,zpdata:rbe |
	nawk -v BE=$BE_NAME '$3 == BE {print $1 ":" $2 }' > /tmp/.lmz.list.$$
	lulib_list_zones "$BE_ROOT" > /tmp/.lmz_zonelist.$$
	cat /tmp/.lmz_zonelist.$$ |
	while IFS=: read zoneid zonename status zonepath uuid more; do
		IFS="$SAVEIFS"
		[ $zonename = global ] && continue
		# Kill pkgserv, if it runs
		pkgadm sync -R "$zonepath" -q > /dev/null 2>&1
		if [ $status = mounted ]; then
			zoneadm -R "$BE_ROOT" -z "$zonename" unmount
		fi

		/usr/sbin/umount "$zonepath"
		[ ! -s /tmp/.lmz.list.$$ ] && continue

		dataset=`nawk -F: -v ZN=$zonename '$2 == ZN {print $1; exit}' /tmp/.lmz.list.$$`
		if [ -n "$dataset" ]; then
			cmflag=`zfs list -Ho canmount "$dataset"`
			if [ "$cmflag" = "noauto" ]; then
				zfs unmount "$dataset"
			else
				zfs set canmount=off "$dataset"
			fi
		fi
	done
	rm -f /tmp/.lmz_zonelist.$$ /tmp/lmz.list.$$
	exit 0
fi

# Do a lofs-mount zfs datasets under the BE.
# Additionally, mount any canmount=noauto datasets that are
# descendents of the root
touch /tmp/.lmz.list.$$
if [ -x /sbin/zfs ]; then
	rootfstype=`df -n "$BE_ROOT" | cut -d: -f2 | awk '{print $1}'`
	if [ "$rootfstype" = "zfs" ]; then
		zfs list -t filesystem -Ho name,canmount,mounted -r "$BE_ROOT" |
		grep noauto | grep 'no$' | while read dataset foo bar; do
			mount_prop=`zfs get -Ho value mountpoint $dataset`
			if [ $mount_prop != "legacy" ] ; then
			    /sbin/zfs inherit mountpoint "$dataset"
			    /sbin/zfs mount "$dataset"
			fi
		done
	fi
	/sbin/zfs list -t filesystem -Ho name,zpdata:zn,zpdata:rbe,mounted |
	/bin/nawk -v BE=$BE_NAME '$3 == BE {print $1 ":" $2 ":" $4}' > /tmp/.lmz.list.$$
fi

# Iterate through the installed zones in the ABE.  Mount up each one.
all_nonglobal_zones "$BE_ROOT" |
while read zonename; do
	nawk -F: -v ZN=$zonename '$2 == ZN {print $1 " " $3; exit}' \
	    /tmp/.lmz.list.$$ | while read dataset ismounted; do
		ds_mntpt=`zoneadm -R "$BE_ROOT" -z $zonename list -p |
		    cut -d: -f4`
		newdir="/`expr $ds_mntpt :  \"$BE_ROOT/\"'\(.*\)'`"
		if [ "$ismounted" = "no" ]; then
			cmflag=`zfs list -Ho canmount "$dataset"`
			if [ "$cmflag" = "off" ]; then
				/sbin/zfs set canmount=on "$dataset"
			fi
			mount_prop=`zfs get -Ho value mountpoint $dataset`
			if [ $mount_prop = "legacy" ] ; then
			    if lulib_path_writable "$ds_mntpt" "700"; then
			        /sbin/mount -F zfs "$dataset" "$ds_mntpt"
			    else
			        /sbin/mount -F zfs "$dataset" "$newdir"
			        /sbin/mount -F lofs "$newdir" "$ds_mntpt"
			    fi
			    break
			fi
			/sbin/zfs set mountpoint="$newdir" "$dataset"
			/sbin/zfs mount "$dataset"
		fi
		if [ ! -d "$ds_mntpt" ]; then
			# dataset is mounted, but not in the 'right' spot
			zfs set mountpoint="$ds_mntpt" "$dataset"
		else
			# dataset is mounted correctly; lofs it under the ABE
			mount -F lofs "$newdir" "$ds_mntpt"
		fi
		chmod 700 "$ds_mntpt"
	done

	if zoneadm -R "$BE_ROOT" -z $zonename mount || $force_mount; then
		# If we succeeded or are forcing the operation, then we're all
		# set.  Ignore zones that don't mount correctly when "forcing."
		:
	else
		$LUPRINTF -Eelp2 \
		    "`gettext 'unable to mount zone <%s> in <%s>'`" \
		    $zonename "$BE_ROOT"
		exit 1
	fi
done || exit 1
rm -f /tmp/.lmz.list.$$

# Now iterate through all running and mounted zones on the running system.
# For each one, if there's a mounted zone in the ABE, then construct an
# image of that zone under the same alternate root mount point using lofs.
#
# Note that we intentionally do not expose the non-global zone administrator
# to the oddities of "mounted" zones.  He sees only what's under /a.
lulib_list_zones |
while IFS=: read zoneid zonename status zonepath uuid more; do
	IFS="$SAVEIFS"
	if [ $zonename = global ]; then
		continue
	fi
	if [ $status = ready -o $status = running ]; then
		zoneroot="$zonepath/root"
	elif [ $status = mounted ]; then
		zoneroot="$zonepath/lu"
	else
		continue
	fi
	# The use of 'while' here has two purposes.  First, if zoneadm
	# returns nothing, then there's nothing to do.  Second, if it
	# does return something, then we need to have that in the same
	# subshell as the 'read'.  Also, the last argument to 'read'
	# has embedded separators (spaces) preserved, which is why the
	# path is last.
	zoneadm -R "$BE_ROOT" -z "$zonename" -u "$uuid" list -p |
	while IFS=: read abe_zoneid abe_zonename abe_status abe_zonepath \
	    abe_uuid more; do
		IFS="$SAVEIFS"
		# We need to resolve $abe_zonepath/lu path here, since 
		# it is what zoneadmd does when figuring out where to mount
		# the zone. This works for shared as well as for unshared 
		# zone root filesystems. 
		abe_zoneroot="`lulib_resolve_lofs \"$BE_ROOT\" \
		    \"$abe_zonepath/lu\"`/a"
		newmount="$zoneroot$BE_ROOT"
		if [ ! -d "$newmount" ]; then
			mkdir -p "$newmount" >/dev/null 2>&1 || continue
		fi
		mount -p | fgrep "$abe_zoneroot" |
		while read special raw mountpoint fstype fsckpass atboot \
		    mountopt more; do
			# Compute the directory relative to the ABE zone root
			if [ $mountpoint = "$abe_zoneroot" ]; then
				newdir=/
			else
				newdir="/`expr $mountpoint : \
				    \"$abe_zoneroot/\"'\(.*\)'`"
				# If this is a false match, ignore it.
				if [ $? != 0 ]; then
					continue
				fi
			fi
			newmount="$zoneroot$BE_ROOT/$newdir"
			[ -d "$newmount" ] || continue
			if [ $fstype = lofs ]; then
				mount -F lofs -o "$mountopt" $special \
				    "$newmount"
			else
				mount -F lofs $mountpoint "$newmount"
			fi
		done
	done
done

exit 0
