#!/bin/sh

#######################################################################
# Copyright (C) 2008-2015 by NetApp.  All Rights Reserved.
#
#######################################################################

#
# Command usage function
#
usage()
{
	if [ ${PRINT_USAGE} -eq 1 ]; then
		msg "Usage: INSTALL [-check] [-1] update|copy|install image1|image2 dir|tarball [dev]"
		msg "  -check - will perform everything up to the point of writing to the device"
		msg "  -1 - will cause install to install only on the specified image"
		msg "  update|copy|install - operation to perform"
		msg "  image1|image2 - the currently running image"
		msg "  dir|tarball - the location of the files to install or a gzipped tarball"
		msg "  dev - optional device containing the boot device used for the"
		msg "        install operation"
	fi
}


# For managing firmware environment variables!
. /etc/netapp_common.subr

#
# Register a handler for sigint to cleanup temp files
SIG="INT"
trap "sig_handler ${SIG}"  SIG${SIG}
SIG="TERM"
trap "sig_handler ${SIG}"  SIG${SIG}

# This is a list of files that will be installed to the boot device.  The
# list may be modified at run time.  The list will also be used in free space
# calculations.
INSTALL_FILES="BUILD CHECKSUM COMPAT.TXT INSTALL VERSION cap.xml fw.tgz kernel metadata.xml netapp_ipspace_limit_check netapp_nfs_netgroup_check netapp_security_config_check platform.ko rootfs.img"

# This is a list of files to be extracted to facilitate checks (metadata, free
# space, etc) before the actual installation
PRE_EXTRACT_FILES="BUILD CHECKSUM COMPAT.TXT VERSION metadata.xml netapp_ipspace_limit_check netapp_nfs_netgroup_check netapp_security_config_check"

# This is the diagnostic executable file name; that will be invoked by loader.
DIAG_EXEC_RUNTIME_NAME="bzImage"

# image.tgz root path that contains all files
IMAGE_ROOT="netboot"
FAST_CHKSUM_ALGO_WORK_DIR="/mroot/etc/software/csdir"

# Create a temp working directory
TMP=$(mktemp -d /tmp/install.tmp.XXXXX)

# While this is set, any encounterred errors will be treated as usage errors
PRINT_USAGE=1
ramdisk_created=0

FDISK=fdisk
DISKINFO=diskinfo
# Define a mount point for the boot device
if [ -n "${CFCARD_MNTPT}" ]; then
	CF_MNT=${CFCARD_MNTPT}
else
	# CFCARD_MNTPT might not be defined if coming from older (eg: tricky)
	# systems, so provide a default of /cfcard
	CF_MNT="/cfcard"
	SetCFcardMntPt "${CF_MNT}"
fi

#
# Convert to uppercase
#
toupper()
{
	echo $1 | sed y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIGKLMNOPQRSTUVWXYZ/
}

#
# Trim leading and trailing white space
#
trim()
{
	echo $1 | sed 's/^[ \t]*//' | sed 's/[ \t]*$//'
}

#
# Check for mounted file system
# Input: $1 --- name of file system, such as "/cfcard"
#
is_mounted()
{
        local file_system=$1

        if [ ! -d "$file_system" ]; then
                # error due to missing input for file system
                exit 1
        fi

        df -k $file_system | grep -q $file_system
}

#
# Get available disk space of a file system
# The available disk space is the fourth output column of 'df' command.
# Example:
#     or-094% df -k /cfcard
#     Filesystem 1024-blocks    Used   Avail Capacity  Mounted on
#     /dev/da0s1     7319208 1110840 6208368    15%    /cfcard
#
#     or-094% df -k /cfcard | grep /cfcard | awk '{print $4}'
#     6208368
#
# Input: $1 --- name of file system
#
fs_available_space()
{
        local file_system=$1

        if [ ! -d "$file_system" ]; then
                # error due to missing input for file system
                exit 1
        fi

        df -k $file_system | grep $file_system | awk '{print $4}'
}

#
# Get file system size
# The number of blocks of a file system is the second output column of 'df' command.
# Example:
#     or-094% df -k /cfcard
#     Filesystem 1024-blocks    Used   Avail Capacity  Mounted on
#     /dev/da0s1     7319208 1110840 6208368    15%    /cfcard
#
#     or-094% df -k /cfcard | grep /cfcard | awk '{print $2}'
#     7319208
#
# Input: $1 --- name of file system
#
fs_size()
{
        local file_system=$1

        if [ ! -d "$file_system" ]; then
                # error due to missing input for file system
                exit 1
        fi

        df -k $file_system | grep $file_system | awk '{print $2}'
}

#
# Make a directory
# NOTE: this now assumes it is passed an absolute path (which has
# always been true).  See burt 295028 for why this was changed.
#
create_dir()
{
	if [ ! -d $1 ]; then
		# Don't use mkdir -p on our /cfcard partitions due to burt 295028
		cd /
		for dir in `echo $1 | tr "/" " "`
		do
			mkdir $dir > /dev/null 2>&1
			cd $dir > /dev/null 2>&1
		done
		cd /
		if [ ! -d $1 ]; then
			msg "Unable to create the directory: $1"
			return 1
		fi
		msg "Directory $1 created"
	fi
	return 0
}

#
# Copy files( src, dest, file_list )
#
# If installing from a tarball, the souce directory is ignored.
copy_files()
{
	local srcdir=$1
	shift
	local dstdir=$1
	shift
	local file_list="$@"

	# Give the new partition table a chance to get to the device
	# and let any pending I/O settle down.
	msg "Syncing device..."
	sync ; sleep 3

	if [ $tarball ]; then
		msg "Extracting to $dstdir..."
		tar -qxvzf $tarball -C $dstdir $file_list
		if [ $? -ne 0 ]; then
			msg "Unable to extract '$(basename $tarball)' to $dstdir - this is VERY bad"
			return 1
		fi

                # move all installed files to /cfcard image top directory
                # then remove the tarball image top directory which should be empty now
                mv -f $dstdir/$IMAGE_ROOT/* $dstdir
                rm -rf $dstdir/$IMAGE_ROOT
	else
		for file in $file_list; do
			msg "Copying $file from $srcdir to $dstdir..."
			cp $srcdir/$file $dstdir
			if [ $? -ne 0 ]; then
				msg "Unable to copy the file $srcdir/$file to $dstdir - this is VERY bad"
				return 1
			fi
		done
	fi
	touch $dstdir/_installtime
	return 0
}

write_fdisk_config()
{
	echo "${FDISK_SCRIPT}" > ${TMP_MNT}/dskfmt
}

write_fdisk_secondary_config()
{
	echo "${FDISK_SECONDARY_SCRIPT}" > ${TMP_MNT}/dskfmt
}

#
# Get a kernel environment variable
#
get_kenv()
{
	kenv $1 2> /dev/null
	return $?
}

#
# Clean up some state, then exit.
# This allows install to be called again (specifically during
# a netboot), if it happens to fail the first time.
#
cleanup()
{
	if [ -d "${TMP}" ]; then
		rm -rf "${TMP}"
	fi

        if [ -d "${CF_MNT}/boot" ]; then
                rm -rf "${CF_MNT}/boot"
        fi

        if [ -d "${FAST_CHKSUM_ALGO_WORK_DIR}" ]; then
                rm -rf "${FAST_CHKSUM_ALGO_WORK_DIR}"
        fi

        if [ ${ramdisk_created} -eq 1 ]; then
                # toss out the ramdisk
	        umount $TMP_MNT
	        mdconfig -d -u$mddev
	elif [ -n "$TMP_MNT" ]; then
	        # if $TMP_MNT has been used, try to unmmount it
	        # depending on when this is called, it could be a
	        # ramdisk, or a boot device, or not even mounted at all.
		umount $TMP_MNT 2> /dev/null
	fi

	if [ "${op}" = "install" ]; then
		umount $CF_MNT 2> /dev/null
	fi
	sync
}

cleanup_and_exit()
{
	cleanup
	exit $1
}

#
# Error and exit function
#
err_exit()
{
	echo "ERROR: $1." >&2
	usage
	cleanup_and_exit 1
}

sig_handler()
{
	# Which signal did we catch?
	local sig=$1
	# unregister trap handler, revert to default handler.
	trap SIG${sig}

	msg "Exiting due to signal ${sig}."

	# delete temp files, etc
	cleanup

	# exit by invoking the default handler, which will tell the parent we
	# were killed by a signal.
	kill -s SIG${sig} $$
}

#
# Output a message
#
msg()
{
	echo "$1"
}

#
# Compute a numeric representation of the major and minor
# portions of the version string.
#
compute_version()
{
    local version=0
    local tmp=`echo $1 | sed 's/[a-zA-Z].*__//'`
    tmp=`echo $tmp | sed 's/[a-zA-Z].*//'`
    local major=`echo $tmp | awk -F. '{printf("%d", $1)}'`
    local minor=`echo $tmp | awk -F. '{printf("%d", $2)}'`

    if [ -z $major ]; then
            major=0
    fi

    # Data ONTAP 8(BR) is greater than 10(Tricky), so 8 becomes 80.
    # If there is a version 11 or greater, it must also be greater than
    # version 8, so 11  becomes 110.
    if [ $major -gt 7 -a \
         $major -lt 10 -o \
         $major -ge 11 ]; then
        major=`expr $major \* 10`
    fi
    version=`expr $version \* 100 + $major`


    if [ -z $minor ]; then
            minor=0
    fi
    version=`expr $version \* 100 + $minor`

    return $version
}

# Notify the dblade if a new version of Data ONTAP has been installed
# This information is used for NDU data mining.
notify_dblade()
{
	# Compare the new BUILD and VERSION files with what ever is in the
	# root directory of the currently running build.  Any differences
	# should trigger the "ndu_takeover" flag to be set within the dblade.

	diff -q /BUILD $otherdir/BUILD > /dev/null 2>&1
	rc_diff_build=$?
	diff -q /VERSION $otherdir/VERSION > /dev/null 2>&1
	rc_diff_vers=$?
	if [ $rc_diff_build -ne 0 -o \
	     $rc_diff_vers -ne 0 ]; then
		# OK to fail.  zsmcli might not exist (Tricky), or
		# dblade might not be loaded (install from boot menu).
		# ignore any errors.
		zsmcli "system-set-new-ontap-version-downloaded" > /dev/null 2>&1 ||
		true
	fi
	return 0
}

#
# Brand Check is used to verify the software brand matches the
# branding of the underlying hardware.
#
# Input: BUILD file will be used to check for the software branding name
#
# The logic works as folows:
# 1) The BUILD file will be used to check for the software branding
# 2) The hardware branding is obtainied from the SYS_MODEL environment variable
# 3) If a mismatch is detected, abort the installation.
#
brand_check()
{
	buildfile=$1

	# The SwBrand, software branding, is obtained from the TYPE field in the BUILD file.
	# The TYPE field can be one of the following:
	# TYPE:
	# TYPE: IBM
	# TYPE: SIM
	# TYPE: REGULAR
	# TYPE: DEBUG
	# TYPE: DEBUG IBM
	# TYPE: DEBUG SIM
	# TYPE: DEBUG REGULAR
	# We only care about "IBM" brand. Every thing else is treated as null string.
	if grep 'TYPE:.\+IBM' ${buildfile} >/dev/null ; then
		SwBrand="IBM"
		SwType="IBM ONTAP"
	else
		SwType="NetApp ONTAP"
	fi

	# The HwBrand,  hardware branding, is obtained from the SYS_MODEL environment variable.
	# The SYS_MODEL can be one of the following:
	# IBM:
	#	IBM-XXXX
	# NetApp:
	#	FASXXXX
	# VSIM:
	#	SIMBOX
	# VSA:
	#	DOvXXX
	#	FCvXXX
	# We only care about "IBM" brand. Every thing else is treated as null string.
	if get_kenv SYS_MODEL | grep "IBM"  >/dev/null ; then
		HwBrand="IBM"
		HwType="product of IBM"
	else
		HwType="product of NetApp"
	fi

	# branding check
	if [ "${SwBrand}" != "${HwBrand}" ]; then
		err_exit "Cannot install ${SwType} on ${HwType}"
	fi
}

# Prefix the string found in $1 to each string found in $2 ... $n
prefix_pattern()
{
	PREFIX=$1
	shift

        for fitem in $@; do
            echo -n "${PREFIX}${fitem} "
        done
}

#
# If it's a tarball then untar a few things to temp.
#
pre_extract()
{
	if [ "${tarball}" ]; then
                local extract_file_list="$(prefix_pattern "${IMAGE_ROOT}/" ${PRE_EXTRACT_FILES})"
		tar -qxzf ${tarball} -C ${TMP} ${extract_file_list} ||
		    err_exit "Unable to extract '$(basename ${tarball})' to ${TMP}"
		newdir="${TMP}/${IMAGE_ROOT}"

		filetype=`tar -qxf ${tarball} -O ${IMAGE_ROOT}/kernel | file -`
	else
		filetype=$(file $newdir/kernel)
	fi
}

#
# Ensure that kernel matches the machine type
#
kernel_arch_check()
{
	case $machine in
	x86_64)
		echo $filetype | grep -q 'x86-64'
		if [ $? -eq 1 ]; then
			err_exit "Attempt to install an invalid kernel binary on target hardware: $filetype"
		fi
		;;
	x86)
		echo $filetype | grep -q 'Intel 80386'
		if [ $? -eq 1 ]; then
			err_exit "Attempt to install an invalid kernel binary on target hardware: $filetype"
		fi
		;;
	*)
		err_exit "Invalid machine type: $machine"
		;;
	esac
	msg "Kernel binary matches install machine type"
}

#
# Hostname Check is used to verify that the current node hostname
# does not contain any potentially problematic characters that are
# not supported by the new Data ONTAP version, preventing it from
# booting.
#
# Specifically, this check limits the hostname to A-Z, a-z, 0-9,
# "-" and "_".
#
hostname_check()
{
	hostname=$(/bin/hostname)
	echo $hostname | grep -qE '[^A-Za-z0-9_-]'
	if [ $? -eq 0 ]; then
		err_exit "Invalid local hostname '$hostname'. The hostname can only contain the following characters: A-Z, a-z, 0-9, \"-\" and \"_\". Use the \"system node rename\" command to rename the node before installing this image"
	fi
}

#
# Ensure that the file checksums match
#
# parameters are:
#	-package
#		verify checksums in the package
#	-dir path
#		verify the checksums in the specified directory path
#	-fw
#		verify checksums from the fw.tgz package
#
verify_checksums()
{
	if [ "$1" = "-package" ]; then
		check_package="1"
		source="Package"
		checksum_dir="$newdir"
		checksum_file="${checksum_dir}/CHECKSUM"

		# Grab just the file name from the list of MD5SUMs:
		# "MD5 (rootfs.img) = e5d7e73a4321cb1f941b14c9d0fc3644" --> "rootfs.img"
		files_to_checksum=$(cut -d' ' -f2  ${checksum_file}) ||
				err_exit "${checksum_file} file not found"
		files_to_checksum=$(echo ${files_to_checksum} | sed 's/[()]//g')
                files_to_checksum="$(prefix_pattern "${IMAGE_ROOT}/" ${files_to_checksum})"

	elif [ "$1" = "-dir" ]; then
		check_package="0"
		source="Installed"
		checksum_dir="$2"
		checksum_file="${checksum_dir}/CHECKSUM"
		if [ ! -f "${checksum_file}" ]; then
			err_exit "${checksum_file} file not found"
		fi
		files_to_checksum="${INSTALL_FILES}"
	elif [ "$1" = "-fw" ]; then
		check_package="0"
		source="Firmware"
		checksum_dir="${CF_MNT}"
		checksum_file="${CF_MNT}/common/CHECKSUM"
		if [ ! -f "${checksum_file}" ]; then
			err_exit "${checksum_file} file not found"
		fi

		# Grab just the file name from the list of MD5SUMs:
		# "MD5 (rootfs.img) = e5d7e73a4321cb1f941b14c9d0fc3644" --> "rootfs.img"
		files_to_checksum=$(sed -e 's/^MD5 (\([^)]*\)).*$/\1/' \
		    ${checksum_file}) ||
		    err_exit "Error reading checksums from ${checksum_file}"
	else
		err_exit "Invalid parameters to verify_checksums"
	fi

	# Ensure the CHECKSUM file itself is not in the list.
	files_to_checksum=$(echo ${files_to_checksum} | sed 's/CHECKSUM //')

	for i in ${files_to_checksum}; do
		if [ "$check_package" = "1" -a $tarball ]; then
			tar -qvtf "${tarball}" -O "${i}"  2>/dev/null ||
			    err_exit "Required file '${i}' not found in '$(basename ${tarball})'"
			md5sum=`tar -qxvzf $tarball -O $i  2>/dev/null | md5`
		else
			[ -f "${checksum_dir}/${i}" ] ||
			    err_exit "Required file '${i}' not found in '${checksum_dir}'"
			md5sum=`md5 -q "${checksum_dir}/${i}"`
		fi
		# Look for the calculated MD5 in the list of stored checksums
		[ -n "${md5sum}" ] && grep -q "$md5sum" "${checksum_file}" 2> /dev/null
		if [ $? -ne 0 ]; then
			err_exit "MD5 checksum failure on ${source} file: '$i'"
		fi
	done
	msg "${source} MD5 checksums pass"
}

verify_pkg_checksum()
{
	checksum_file="${newdir}/CHECKSUM"

	# Grab just the file name from the list of MD5SUMs:
	# "MD5 (rootfs.img) = e5d7e73a4321cb1f941b14c9d0fc3644" --> "rootfs.img"
	files_to_checksum=$(cut -d' ' -f2  ${checksum_file}) ||
	        err_exit "${checksum_file} file not found"
	files_to_checksum=$(echo ${files_to_checksum} | sed 's/[()]//g')
        files_to_checksum="$(prefix_pattern "${IMAGE_ROOT}/" ${files_to_checksum})"

	# Ensure the CHECKSUM file itself is not in the list.
        files_to_checksum=$(echo ${files_to_checksum} | sed 's/CHECKSUM //')

        cd "${FAST_CHKSUM_ALGO_WORK_DIR}" 2> /dev/null
        tar -qxzf $tarball ${files_to_checksum} 2> /dev/null
        for i in ${files_to_checksum}; do
            if [ ! -e $i ]; then
                err_exit "Required file '${i}' not retrieved from ${tarball}"
            else
		md5sum=`md5 -q $i`
            fi

            # Look for the calculated MD5 in the list of stored checksums
            [ -n "${md5sum}" ] && grep -q "$md5sum" "${checksum_file}" 2> /dev/null
	    if [ $? -ne 0 ]; then
		err_exit "MD5 checksum failure on Package file: '$i'"
	    fi
	done
	msg "Tarball file MD5 checksums pass"
        cd "${current_dir}" 2> /dev/null
}

# This Function is used to
# 1. Calculate free space along with directory(second arg) with first argument "size"
# 2. Delete directory(second arg) contents along with some stale images of 7G when first arg "delete"
delete_old_files()
{
        local option=$1
        local tmpdir=$2
        if [ "$option" = "size" ]; then
                local available=$(fs_available_space "/cfcard")
                for file in ${tmpdir}/*; do
                        if [ -e $file ]; then
                                temp=`du -k $file | awk '{print $1}'`
                                available=`expr $available + $temp`
                        fi
                done
                for file in ${olddir}/*; do
                        if [ -e $file ]; then
                                temp=`du -k $file | awk '{print $1}'`
                                available=`expr $available + $temp`
                        fi
                done
                echo $available
        elif [  "$option" = "delete" ]; then
                rm -rf ${tmpdir}/* 2> /dev/null
                if [ -f ${currentdir}/kernel ]; then
                        rm -rf ${olddir}/* 2> /dev/null
                fi
        fi

}

#
# If this is not an INSTALL operation, compute the available disk space
# available = the free space on the device + the space of the files to be replaced.
#
# NOTES:
#	This function expects INSTALL_FILES to have already been set
#	appropriately by the parse_metadata function.
#
free_space_check()
{
	if [ $op != 'INSTALL' ]; then
		if [ ! -d /cfcard ]; then
			err_exit "No /cfcard directory found"
		fi

                #
                # Check whether "/cfcard" is mounted.
                # This is to avoid wasting time to list all other unrelated
                # file systems.  If there is stale mounted file systems,
                # 'df -k | grep -q "cfcard"' will timeout when listing the staled file system.
                # See burt 912918
                #
                is_mounted "/cfcard"
		if [ $? != 0 ]; then
			err_exit "Boot device not mounted"
		fi

		available=$(delete_old_files size $otherdir)
		availableM=`expr $available / 1024`
		msg "Available space on boot device is $availableM MB"

		#
		# Compute the required disk space - the space of the new files.
		#
		required=0
                local extract_file_list="$(prefix_pattern "${IMAGE_ROOT}/" ${INSTALL_FILES})"
		for file in ${extract_file_list}; do
			if [ $tarball ]; then
				temp=`grep $file ${TMP}/filelist | awk '{print $5}'`
				temp=`expr $temp / 1024`
			else
				temp=`du -k $newdir/$file | awk '{print $1}'`
			fi
			required=`expr $required + $temp`
		done

		#
		# Ensure that the device will not be more than 95% full
		#
                slack=$(fs_size "/cfcard")
		slack=`expr $slack / 20`
		required=`expr $required + $slack`
		# convert to MB
		requiredM=`expr $required / 1024`

		msg "Required  space on boot device is $requiredM MB"
		if [ $required -gt $available ]; then
			err_exit "Insufficient space available on device"
		fi

	#
	# Otherwise check that the media size is at least 1GB
	#
	else
		msize=`$DISKINFO $cfdev` || err_exit "Unable to read diskinfo from device $cfdev"
		msize=`echo $msize | awk '{print $3}'`

                if [ -z "$MIN_BOOTDEVICE_SIZE" ]; then
                        # Set minimum boot device size as 900M
                        $MIN_BOOTDEVICE_SIZE=900000000
                fi

                if [ $msize -lt "$MIN_BOOTDEVICE_SIZE" ]; then
                        # boot device insert is smaller than minimum size supported.
                        msizeM=$(( msize / 1024 / 1024 ))
                        MIN_BOOTDEVICE_SIZE=$(( $MIN_BOOTDEVICE_SIZE / 1024 / 1024 ))
			msg "The version of Data ONTAP you are attempting to install is not"
			msg "supported on a boot device of $msizeM MB capacity."
			err_exit "Size of boot device is less than $MIN_BOOTDEVICE_SIZE MB"
		fi
	fi
}


#
# Rename the custom platfs to a generic platfs file name
# Parameters:
#	$1=directory		The path to the file to be renamed.
#	$2=cust_platfs		The name of the custom platfs to be renamed.
# Returns:
# 	0		success
#	1 or non-zero 	on error
#
rename_platfs()
{
	local directory=$1
	local cust_platfs=$2

	if [ ! -d "${directory}" ]; then
		return 1
	fi

	if [ ! -f "${directory}/${cust_platfs}" ]; then
		return 1
	fi

	if [ ${cust_platfs} = "platfs.img" ]; then
		# no need to rename
		return 0
	fi

	mv ${directory}/${cust_platfs} ${directory}/platfs.img

}

#
# Rename the platform specific ODM diagnostic executable name to a name is known by loader.
# Parameters:
#	$1=directory		The path to the file to be renamed.
# Returns:
#	0		success
#	1 or non-zero	on error
#
rename_diag_image()
{
	local directory=$1

        # Skip renaming ODM diagnostic file if it is not supported.
        if [ -z "${ODM_DIAG_SRC_EXEC}" ]; then
                return 0
        fi

	if [ ! -d "${directory}" ]; then
		return 1
	fi

	if [ -e "${directory}/${ODM_DIAG_SRC_EXEC}" ]; then
                if ! mv ${directory}/${ODM_DIAG_SRC_EXEC} ${directory}/${DIAG_EXEC_RUNTIME_NAME}; then
                        msg "Failed to rename diagnostic executable file name."
                        return 1
                fi
        fi
}

#
# Generate a temp file containing an XSLT.  The caller is expected to clean up
# the temp file.  The result of running this transform will be the "value" of
# the requested platform parameter.  For example, the name of the platfs.img
# file to be used on this platform.
#
# Input parameters:
#	$1= boardname identifier, eg: SB_XXX
#	$2= tag, eg: platfs
# Output:
#	The name of the temp file containing the XSLT.
xslt_gen()
{
	local boardname=$1
	local tag=$2
	local tmp_xslt=$(mktemp ${TMP}/xslt.XXXXX)
	cat <<-XSLT > ${tmp_xslt}
	<?xml version="1.0" encoding="ISO-8859-1"?>
	<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
		<xsl:output method="text" omit-xml-declaration="yes" />

		<xsl:template match="/">
			<xsl:apply-templates select="/package_metadata/platform[@name='${boardname}']/${tag}" />
		</xsl:template>

	</xsl:stylesheet>
	XSLT
	echo ${tmp_xslt}
}


# Prevent upgrade or install on platforms with a Flash Cache adaptor and 4GiB
# of memory or less.  See burts 693390 and 539729
flash_cache_check()
{
	# 5 GiB = 4 + 1 for NVMEM
	local fas3210_mem=$((5 * 1024 * 1024 * 1024))
        local virgo_devid="0x774c1275"
	local fc_override_var="ext-cache-4g-override"

        # If memsize <= FAS3210 memory && PCI vendor id for Virgo is found,
        # we have an illegal config
        local fc_memsize
	fc_memsize="$(sysctl hw.total_physmem)" || msg "Unable to read system memory information"
        fc_memsize="$(echo "${fc_memsize}" | awk '{print $2}')"
        if [ "${fc_memsize}" -gt "${fas3210_mem}" ]; then
		# System has enough memory, return success
		return 0
	fi

	if ! (pciconf -l | grep -q "${virgo_devid}" 2> /dev/null); then
		# flash cache is not present, return success
		return 0
	fi

	# We have a low mem system with flash cache, but
        # allow override by bootarg:
        local fc_override="$(get_kenv "${fc_override_var}")"
        if [ "${fc_override}" = "true" ]; then
		msg "'$(get_kenv SYS_MODEL)' does not support Flash Cache in this release of Data ONTAP,"
		msg "but '${fc_override_var}' is '${fc_override}'."
                return 0
        fi

	# If we haven't returned success by now, this is a failure
	err_exit "Model '$(get_kenv SYS_MODEL)' does not support Flash Cache in this release of Data ONTAP"
}


#
# Special case: versions are identical -> this becomes a copy operation
# Only do this if the operation is not an INSTALL.
#
# NOTES:
#	This function should be called as part of the compat_check() function,
#	since it could change an UPDATE op into a COPY op.  COPY is used to
#	intentionally bypass the compat check on what would otherwise be an
#	UPDATE operation.
#
op_should_be_COPY()
{
	if [ $op = 'INSTALL' ]; then
		return
	fi

	diff /VERSION ${newdir}/VERSION > /dev/null 2>&1
	if [ $? -eq 0 ]; then
		op='COPY'
	fi
}

# 8.3 and beyond are C-mode only.  Disallow upgrades from a 7-mode system to
# this release.
no_7_mode_check()
{
	if get_kenv "bootarg.init.boot_clustered" | grep -q -i "true"; then
		# The system is in C-mode;
		# allow install:
		return
	fi

	# The system is not in C-mode, so it must be in 7-Mode;
	# abort install
	local error_string
	error_string="Clustered Data ONTAP cannot be installed on a 7-mode system"
	err_exit "${error_string}"
}

# Check that the current VERSION file exists
# and that versions are compatible.
compat_check()
{
	op_should_be_COPY

	# COPY is used to intentionally bypass the compatibility check
	# INSTALL and UPDATE are subject to checks
	if [ $op = 'COPY' ]; then
		return
	fi

	# VERSION file must exist
	if [ ! -e /VERSION ]; then
		err_exit "/VERSION file does not exist"
	fi

	local error_string="The running version $(cat /VERSION)"
	error_string="${error_string} is not compatible with the package"
	error_string="${error_string} version $(cat ${newdir}/VERSION)"

	# Check basic compatibility
	# If a regex pattern in COMPAT.TXT matches the current /VERSION
	# string, then we are compatible.
	if ! grep -qEf ${newdir}/COMPAT.TXT /VERSION; then
		err_exit "${error_string}"
	fi

    if [ $op != 'INSTALL' ]; then
		no_7_mode_check
	fi

	msg "Versions are compatible"
}

#
# Given the platform boardname and a tag name, parse the metadata.xml file and
# return the value of the requested tag
#
# Input parameters:
#	$1= boardname identifier
#	$2= tag
# Output:
#	The content of "tag"
# Return:
#	The return code of the xsltproc(1) command
platform_xpath()
{
	local boardname=$1
	local tag=$2
	local xslt_file=$(xslt_gen ${boardname} ${tag})
	# Output is captuire by this function's caller
	xsltproc ${xslt_file} ${newdir}/metadata.xml
	local rc=$?
	rm -f ${xslt_file}
	return ${rc}

}

# Ensure the given command line tool exists
# Input paramters:
#	$1	The command to verify is present on the system and in the path.
# Return:
#	The script will exit if the command is not found.
tool_check()
{
	local tool_cmd="$1"

	local error_string="The running version $(cat /VERSION)"
	error_string="${error_string} does not have the tool set necessary to"
	error_string="${error_string} complete the ${op} operation"

	if ! which ${tool_cmd} > /dev/null; then
		msg "Command '${tool_cmd}' was not found."
		err_exit "${error_string}"
	fi
}

is_file_in_package()
{
	local file=${1}

	if [ -n "${tarball}" ]; then
		if grep -w -q "${file}" ${TMP}/filelist; then
			return 0
		fi
	else
		if [ -e "${newdir}/${file}" ]; then
			return 0
		fi
	fi

	return 1
}


parse_metadata()
{
	# Ensure that the versions are compatible.  Do this early, otherwise
	# we might not have essential tools (such as xsltproc) or other
	# unexpected incompatibilities
	compat_check

	# verify xsltproc is available for use
	tool_check xsltproc

	# Which platform are we on?
	local boardname=$(get_kenv BOARDNAME)

	# BOARDNAME is readonly from the LOADER.  Allow a user to override
	# the BOARDNAME with an arbitrary value by setting BOARDNAME_OVERRIDE.
	local boardname_override=$(get_kenv BOARDNAME_OVERRIDE)
	if [ -n "${boardname_override}" ]; then
		msg "BOARDNAME_OVERRIDE='${boardname_override}' will replace the default value of BOARDNAME='${boardname}'."
		boardname="${boardname_override}"
	fi

	# Which platfs.img file should be used?
	# This is global, to allow for re-use during the rename to platfs.img
	PLATFS_NAME=$(platform_xpath ${boardname} platfs)

	# add platfs.img to the list of files to be installed:
	INSTALL_FILES="${INSTALL_FILES} ${PLATFS_NAME}"

	TRIM_CMD="$(platform_xpath ${boardname} trim_cmd)"
	NEW_KEY_CMD="$(platform_xpath ${boardname} new_key_cmd)"
	FDISK_SCRIPT="$(platform_xpath ${boardname} fdisk_script)"
        FDISK_SECONDARY_SCRIPT="$(platform_xpath ${boardname} fdisk_secondary_script)"
	NEWFS_CMD="$(platform_xpath ${boardname} newfs_cmd)"
	MOUNT_CMD="$(platform_xpath ${boardname} mount_cmd)"
        MIN_BOOTDEVICE_SIZE="$(platform_xpath ${boardname} minimum_bootdevice_size)"
        INSTALL_LOADER="$(platform_xpath ${boardname} install_loader_efi)"
        if [ ! -z "${INSTALL_LOADER}" ]; then
                INSTALL_LOADER=$(toupper ${INSTALL_LOADER})
        fi

        ODM_DIAG_SRC_EXEC="$(platform_xpath ${boardname} odm_diag_image)"
        local tarball_diag_path="$(prefix_pattern "${IMAGE_ROOT}/" ${ODM_DIAG_SRC_EXEC})"
        if [ -n "${ODM_DIAG_SRC_EXEC}" ] && \
            is_file_in_package "${tarball_diag_path}"; then
                INSTALL_FILES="${INSTALL_FILES} ${ODM_DIAG_SRC_EXEC}"
        fi

	# Add extra platform-specific files
	PLATFORM_INSTALL_FILES="$(platform_xpath ${boardname} platform_specific_install_files)"

	if [ "$PLATFORM_INSTALL_FILES" != "" ]; then
		INSTALL_FILES="${INSTALL_FILES} ${PLATFORM_INSTALL_FILES}"
	fi

	# Optionally install the pobj.db file
	if is_file_in_package "pobj.db"; then
		INSTALL_FILES="${INSTALL_FILES} pobj.db"
	fi

	# Make sure the metadata file provided all the values that we expect,
	# else we exit.
	if [ -z "${PLATFS_NAME}" -o \
	     -z "${FDISK_SCRIPT}" -o \
	     -z "${NEWFS_CMD}" -o \
	     -z "${MOUNT_CMD}" ]; then
		err_exit "Model '$(get_kenv SYS_MODEL)' is not supported by this package"
	fi
}

#
# Ensure the files for the new image exist
#
# NOTES:
#	This function expects INSTALL_FILES to have already been set
#	appropriately by the parse_metadata function.
#
file_presence_check()
{
	local pkg_source
	local file

	if [ $tarball ]; then
		pkg_source="${tarball}"
	else
		pkg_source="${newdir}"
	fi

        local extract_file_list="$(prefix_pattern "${IMAGE_ROOT}/" ${INSTALL_FILES})"
	for file in ${extract_file_list}; do
		if ! is_file_in_package "${file}"; then
			err_exit "'${file}' does not exist in ${pkg_source}"
		fi
	done
}


#
# For the INSTALL operation check for the existance of the device and
# ensure that it is not currently mounted.
#
device_mount_check()
{
	if [ $op != 'INSTALL' ]; then
		return
	fi

	if [ ! -c $cfdev ]; then
		err_exit "Cannot find boot device file $cfdev"
	fi

	mount | grep -q $cfdev
	if [ $? = 0 ]; then
		err_exit "Cannot initialize a mounted filesystem"
	fi
	msg "Install device found"

	if [ $oneonly ]; then
		msg "Install command will only setup $current"
	fi
}

#
# Get system BOARDNAME
#
get_board_name()
{
	# Which platform are we on?
	local boardname=$(get_kenv BOARDNAME)

	# BOARDNAME is readonly from the LOADER.  Allow a user to override
	# the BOARDNAME with an arbitrary value by setting BOARDNAME_OVERRIDE.
	local boardname_override=$(get_kenv BOARDNAME_OVERRIDE)
	if [ -n "${boardname_override}" ]; then
		boardname="${boardname_override}"
	fi

        echo "${boardname}"
}

#
# Install EFI Loader Files
#
setup_loader_files()
{
        # Check install_loader_efi attribute set in build metadata file.
        # Skip setting up loader files if the xml tag is not defined;
        # or not set to true.
        if [ "${INSTALL_LOADER}null" = "null" -o \
             "${INSTALL_LOADER}" != "TRUE" ]; then
                return 0;
        fi

        local boardname=`get_board_name`
        local loader_zipsrc="${CF_MNT}/x86_64/firmware/${boardname}/firmware2.img"
        local loader_zipdest="${CF_MNT}/x86_64/firmware/${boardname}/loader.zip"

        if [ -e "${loader_zipsrc}" ]; then
                msg "Install loader files"
                dd bs=256 skip=2 < ${loader_zipsrc} > ${loader_zipdest} 2> /dev/null
                if ! unzip -d ${CF_MNT} ${loader_zipdest} > /dev/null; then
                        msg "Failed to unzip loader.zip"
                        return 1
                fi

                if [ -e ${loader_zipdest} ]; then
                        rm -f ${loader_zipdest}
                fi

                # perform loader file installation for INSTALL mode only
                if [ $op = 'INSTALL' ]; then
                        # Invoke loader script to install files onto primary boot device
                        local install_loader_script="${CF_MNT}/boot/x86-64/install_loader_files.sh"
                        if [ -e "${install_loader_script}" ]; then
                                ${install_loader_script} "${CF_MNT}" "${CF_MNT}" "${boardname}"
                        else
                                msg "Failed to find loader script."
                        fi
                fi
        fi

        return 0
}

#
# Setup secondary boot device
#
setup_recovery_media()
{
    local boot_src=$(get_kenv EXTERNAL_BOOT_DEV)
    boot_src=$(toupper ${boot_src})
    local booted_from=$(get_kenv BOOTED_FROM)
    booted_from=$(toupper ${booted_from})
    if [ "${booted_from}" = "NETWORK" -a "${boot_src}" = "SEC" ]; then
        # if it is performing boot recovery using image on the secondary
        # disk, skip changing the content on the secondary disk.
        return 0
    fi

    local recovery_dev=$(get_kenv ntap.secondary.bootmedia)
    if [ $recovery_dev ]; then
        recovery_part=$recovery_dev
        recovery_dev=`echo $recovery_dev | sed 's/s.*//'`
        if [ ! -e $recovery_dev ]; then
            msg "Secondary boot media is not detected."
            return 0
        fi
    else
        return 0
    fi

    if [ $op = 'INSTALL' ]; then
        # partition the disk
        write_fdisk_secondary_config

	$FDISK -i -f $TMP_MNT/dskfmt $recovery_dev
	if [ $? != 0 ]; then
            msg "Failed to partition secondary boot media"
            return 0
	fi
	sync ; sleep 3;

	# create a new filesystem on partition 1
	$(printf "${NEWFS_CMD}" "${recovery_part}")
	if [ $? != 0 ]; then
	    msg "Unable to create a filesystem on the secondary boot media"
            return 0
	fi
	msg "New secondary boot device filesystem created"
	sync
    fi

    # mount the new filesystem for copying over files
    local RECOVERY_MNT="/mnt/recoverydisk"
    if [ ! -d ${RECOVERY_MNT} ]; then
        mkdir "/mnt/recoverydisk"
    fi

    $(printf "${MOUNT_CMD}" "${recovery_part}" "${RECOVERY_MNT}")
    if [ $? != 0 ]; then
        msg "Unable to mount the secondary boot device filesystem"
        return 0
    else
        sync
    fi

    cp ${tarball} ${RECOVERY_MNT}

    # Invoke loader script to install to secondary disk
    local boardname=`get_board_name`
    local install_loader_script="${CF_MNT}/boot/x86-64/install_loader_files.sh"
    if [ -e "${install_loader_script}" ]; then
            ${install_loader_script} "${CF_MNT}" "${RECOVERY_MNT}" "${boardname}"
    fi

    msg "Boot recovery files installed on secondary device."
    umount ${RECOVERY_MNT}
}

#
# Validate the machine type
#
machine=`uname -m`
case $machine in
	amd64)	machine=x86_64
		def_cfdev=/dev/ad0
	;;
	i386)	machine=x86
		def_cfdev=/dev/ad4
	;;
	*)	err_exit "Unrecognized machine type: $machine"
	;;
esac

# If this is not a LOADER based system, then this will be empty
LOADER_VERSION=`get_kenv LOADER_VERSION`

nfs_netgroup_check()
{
    if [ $op = 'UPDATE' ]; then
	msg "NFS netgroup check script is invoked."
        sudo vcontext -v 4294967295 $newdir/netapp_nfs_netgroup_check netapp_nfs_netgroup_check
        if [ $? != 0 ]; then
            msg "NFS netgroup check script has failed"
            err_exit "netgroup check failed"
        fi
        msg "NFS netgroup check script has run successfully."
    fi
}

security_config_check()
{
    if [ $op = 'UPDATE' ]; then
	msg "security config check script is invoked."
        sudo vcontext -v 4294967295 $newdir/netapp_security_config_check netapp_security_config_check
        if [ $? != 0 ]; then
            msg "security config check script has failed"
            err_exit "security config check failed"
        fi
        msg "security config check script has run successfully."
    fi
}

ipspace_limit_check()
{
    if [ $op = 'UPDATE' ]; then
        msg "IPspace limit checker script is invoked."
        sudo vcontext -v 4294967295 $newdir/netapp_ipspace_limit_check netapp_ipspace_limit_check
        if [ $? != 0 ]; then
            msg "Retry the image installation after taking corrective action."
            err_exit "IPspace limit check failed"
        fi
        msg "IPspace limit checker script has validated configuration."
    fi
}

#
# Validate the number of paramters
#
if [ `toupper $1`null = "-CHECK"null ]; then
	checkonly=1
	shift
	msg "INSTALL running in check-only mode: the image will be validated only"
fi

if [ $1null = "-1"null ]; then
	oneonly=1
	shift
fi

if [ $# -lt 3 -o $# -gt 4 ]; then
	err_exit "Invalid number of parameters specified"
fi

op=`toupper $1`
current=$2
location=$3
cfdev=$4

#
# Only accept copy, update and install as operations
#
if [ $op != 'COPY' -a $op != 'UPDATE'  -a $op != 'INSTALL' ]; then
	err_exit "Invalid operation specified"
fi
msg "Mode of operation is $op"

#
# Only accept image1 and image2 as the location of the currently running image
#
if [ `toupper $current` = 'IMAGE1' ]; then
	current=image1
	other=image2
elif [ `toupper $current` = 'IMAGE2' ]; then
	current=image2
	other=image1
else
	err_exit "Invalid current image specified"
fi

#
# Ensure the that currently running image is really the currently running image.
#
imagestr=`get_kenv bootarg.init.rootimage`
if [ $? -ne 0 ]; then
	imagestr=`get_kenv kernelname`
fi
if [ $imagestr ]; then
	echo $imagestr | grep -q $other 2> /dev/null
	if [ $? -eq 0 ]; then
		err_exit "Cannot write to a currently running image"
	fi
fi

currentdir=/cfcard/$machine/freebsd/$current
otherdir=/cfcard/$machine/freebsd/$other
olddir=/cfcard/backup/$machine/kernel

msg "Current image is $current"
msg "Alternate image is $other"

#
# Is it a directory or a tarball?
#
if [ ! -d $location ]; then
	tar -tvzf $location > ${TMP}/filelist
	if [ $? -ne 0 ]; then
		err_exit "Invalid location specified - must be a directory or a gzipped tarball"
	fi
	tarball=$location
else
	newdir=$location
fi

#
# If this in an INSTALL operation then check for a valid optional device
#
if [ $op = "INSTALL" ]; then
	if [ $cfdev ]; then
		if [ ! -c $cfdev ]; then
			err_exit "Device $cfdev does not exist"
		fi
		cfpart=$cfdev"s1"
	else
		cfdev=`get_kenv ntap.init.cfdevice`
		if [ $cfdev ]; then
			cfpart=$cfdev
			cfdev=`echo $cfdev | sed 's/s.*//'`
		else
			cfdev=$def_cfdev
			cfpart=$cfdev"s1"
		fi
	fi

	msg "Using device $cfdev for install"
fi

# After this point, we assume we've been invoked correctly, so don't print the
# script usage.
PRINT_USAGE=0


pre_extract
if [ $checkonly ]; then
        # Using mkdir failure to determine whether /mroot/etc/software is available
        # depends on "/" as read-only.  This may change in the future.
        # Thus need to revisit this scheme in the future.
        mkdir -p "${FAST_CHKSUM_ALGO_WORK_DIR}" 2> /dev/null
        if [ $? -eq 0 ]; then
            verify_pkg_checksum
            rm -r ${FAST_CHKSUM_ALGO_WORK_DIR}
        else
            verify_checksums -package
        fi
fi

parse_metadata
file_presence_check
free_space_check
kernel_arch_check
hostname_check
flash_cache_check
brand_check "${newdir}/BUILD"
device_mount_check
nfs_netgroup_check
security_config_check
ipspace_limit_check

# Cleanup temp files
rm -rf ${TMP}

#
# If running as check only then stop here.
#
if [ $checkonly ]; then
	cleanup_and_exit 0
fi
msg "Getting ready to install image"

#
# If this is an INSTALL operation then format the device
#
if [ $op = 'INSTALL' ]; then


	# whipup a ramdisk to place a file specifing partitioning info for fdisk
	mddev=`mdconfig -a -tmalloc -s1024`
	if [ $? != 0 ]; then
		err_exit "Unable to allocate a small ramdisk"
	fi

	newfs /dev/$mddev
	if [ $? != 0 ]; then
		mdconfig -d -u$mddev
		err_exit "Unable to newfs ramdisk"
	fi
	sync

	TMP_MNT=`mktemp -d /tmp/mnt.XXXXX`
	mount /dev/$mddev $TMP_MNT
	if [ $? != 0 ]; then
		mdconfig -d -u$mddev
		err_exit "Unable to mount ramdisk"
	fi
        ramdisk_created=1

	# Trim the boot device before partition and installation
	# If the trim process failed,Installation continues
	if [ -n "${TRIM_CMD}" ]; then
		$(printf "${TRIM_CMD}" "${cfdev}")
	fi

	write_fdisk_config

	# partition the disk
	$FDISK -i -f $TMP_MNT/dskfmt $cfdev
	if [ $? != 0 ]; then
		err_exit "Unable to initialize the boot device partition table"
	fi
	sync ; sleep 3;
	msg "Partitioning of boot device complete"

	# On platform like Razor where mSata device is partitioned into the
	# boot device and destage device.Trim the whole mSata device in the
	# above step actually erased the encryption key in the destage partition.
	#
	# In case customer chooses to continue boot(less likley but possible)
	# DeStage in AC power loss case can't proceed without encryption key,
	# therefore, re-generate the encryption key here.
	if [ -n "${NEW_KEY_CMD}" ]; then
		${NEW_KEY_CMD}
	fi

	# create a new filesystem on partition 1
	$(printf "${NEWFS_CMD}" "${cfpart}")
	if [ $? != 0 ]; then
		err_exit "Unable to create a filesystem on the boot device"
	fi
	msg "New boot device filesystem created"
	sync

	# mount the new filesystem for copying over files
	$(printf "${MOUNT_CMD}" "${cfpart}" "${CF_MNT}")
	if [ $? != 0 ]; then
		err_exit "Unable to mount boot device filesystem"
	fi
	sync
	msg "New filesystem mounted"

        if [ "$LOADER_VERSION" != "" ]; then
		# Restore loader's environment to the boot device since we blew
		# it away when the boot device was formatted
		SaveLoaderEnv ||
			msg "Warning: Could not preserve Loader environment."
		sync
	fi

	currentdir=${CF_MNT}/$machine/freebsd/$current
	otherdir=${CF_MNT}/$machine/freebsd/$other
	olddir=${CF_MNT}/backup/$machine/kernel
fi

#
# If destination directory does not exist then create it.
#
create_dir $otherdir
if [ $? != 0 ]; then
	cleanup_and_exit 1
fi
if [ $op = 'INSTALL' ]; then
	create_dir $currentdir
	if [ $? != 0 ]; then
		cleanup_and_exit 1
	fi
fi

#
# Copy the files
#
extract_file_list="$(prefix_pattern "${IMAGE_ROOT}/" ${INSTALL_FILES})"
if [ $op = 'INSTALL' -a $oneonly ]; then
	delete_old_files delete $currentdir
	copy_files "${newdir}" "${currentdir}" "${extract_file_list}"
	if [ $? != 0 ]; then
		cleanup_and_exit 1
	fi
	verify_checksums -dir $currentdir
	rename_platfs "${currentdir}" "${PLATFS_NAME}"
        rename_diag_image "${currentdir}"

	# Swap for fw install
	otherdir=$currentdir
else
	if [ $op = 'INSTALL' ]; then
		delete_old_files delete $currentdir
		copy_files "${newdir}" "${currentdir}" "${extract_file_list}"
		if [ $? != 0 ]; then
			cleanup_and_exit 1
		fi
		verify_checksums -dir $currentdir
		rename_platfs "${currentdir}" "${PLATFS_NAME}"
                rename_diag_image "${currentdir}"

		# we don't need two copies of fw.tgz
		rm -f ${currentdir}/fw.tgz
	fi

	delete_old_files delete $otherdir
	copy_files "${newdir}" "${otherdir}" "${extract_file_list}"
	if [ $? != 0 ]; then
		cleanup_and_exit 1
	fi
	verify_checksums -dir $otherdir
	rename_platfs "${otherdir}" "${PLATFS_NAME}"
        rename_diag_image "${otherdir}"
fi

#
# Check for fw.tgz in this package and install it
#
if [ -f $otherdir/fw.tgz ]; then
	# See if we have enough space to install this.
	# Assume we need twice the size of the tarball
	fw_needed=`ls -l $otherdir/fw.tgz | awk '{print $5}'`
	fw_needed=`expr $fw_needed \* 2 / 1024`
	avail=`df -k $otherdir | grep -v Avail | awk '{print $4}'`
	avail=`expr $avail \* 90 / 100` # 10% head room

	if [ $fw_needed -gt $avail ]; then
		msg "Not enough space to install diagnostics and firmware files"
	else
		msg "Installing diagnostic and firmware files"
		# use the directory hierarchy built into the tar file
		tar -xzf $otherdir/fw.tgz -C ${CF_MNT}
		if [ $? != 0 ]; then
			err_exit "Failed to install diagnostic and firmware files"
		fi

		verify_checksums -fw

                # install loader files
                if ! setup_loader_files; then
                        err_exit "Failed to install loader files"
                fi

		# Successfully extracted, no need to keep the package
		rm -f ${otherdir}/fw.tgz
	fi
fi

# Setup Boot Recovery Disk
setup_recovery_media
rm -rf "${CF_MNT}/boot"

# After the install, and before we unmount and exit,
# tell the dblade to set a flag.
notify_dblade

#
# If this was an install operation then unmount the boot device.
#
if [ $op = "INSTALL" ]; then
	umount ${CF_MNT}
	if [ $? != 0 ]; then
		err_exit "Unable to unmount the boot device"
	fi
	sync
fi

cleanup_and_exit 0
