# SPDX-License-Identifier: CDDL-1.0
#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or https://opensource.org/licenses/CDDL-1.0.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#

#
# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
# Use is subject to license terms.
#

#
# Copyright (c) 2014, 2016 by Delphix. All rights reserved.
#

. $STF_SUITE/include/libtest.shlib

set -A VALID_NAME_CHAR a b c d e f g h i j k l m n o p q r s t u v w x y z \
    0 1 2 3 4 5 6 7 8 9 ':' '-' '.' '_'
set -A INVALID_NAME_CHAR A B C D E F G H I J K L M N O P Q R S T U V W X Y Z \
    '`' '~' '!' '@' '#' '$' '%' '^' '&' '(' ')' '+' '=' '|' "\\" '{' '[' ']' \
    '}' ';' '"' '<' ',' '>' '?' '/' ' '
set -A ALL_CHAR ${VALID_NAME_CHAR[*]} ${INVALID_NAME_CHAR[*]}

#
# Firstly, set the property value to dataset. Then checking if the property
# value is equal with the expected value, according to the expected result.
#
# $1 property value
# $2 property name
# $3 dataset
# $4 expected result
#
function set_n_check_prop
{
	typeset expect_value=$1
	typeset prop=$2
	typeset dataset=$3
	typeset expect_result=${4:-true}

	typeset old_value=""
	typeset cur_value=""

	[[ -n $prop ]] && old_value=$(get_prop $prop $dataset)

	if [[ $expect_result == true ]]; then
		[[ -z $prop || -z $dataset ]] && \
			log_fail "property or dataset isn't defined."

		log_must zfs set $prop=$expect_value $dataset
		if [[ $expect_value == "gzip-6" ]]; then
			expect_value="gzip"
		fi

		[[ -n $prop ]] && cur_value=$(get_prop $prop $dataset)

		case $prop in
			reservation|reserv|quota )
				if [[ $expect_value == "none" ]]; then
					[[ $cur_value != "0" ]] && \
						log_fail "The '$dataset' '$prop' value" \
						"'$cur_value' is not expected."
				elif [[ $cur_value != $expect_value ]]; then
					log_fail "The '$dataset' '$prop' value '$cur_value'" \
					"does not equal the expected value '$expect_value'."
				fi
				;;
			* )
				if [[ $cur_value != $expect_value ]]; then
					log_fail "The '$dataset' '$prop' value '$cur_value'" \
					"does not equal the expected value '$expect_value'."
				fi
				;;
		esac

	else
		log_mustnot zfs set $prop=$expect_value $dataset

		[[ -n $prop ]] && cur_value=$(get_prop $prop $dataset)
		
		wait_freeing

		if [[ "$expect_value" != "" && "$cur_value" != "$old_value" ]];
		then
			log_fail "The '$dataset' '$prop' value '$cur_value'" \
				"should equal '$old_value'."
		fi
	fi
}

#
# Cleanup all the user properties of the pool and the dataset reside it.
#
# $1 pool name
#
function cleanup_user_prop
{
	typeset pool=$1
	typeset dtst=$(zfs list -H -r -o name -t filesystem,volume $pool)

	typeset user_prop
	for dt in $dtst; do
		user_prop=$(zfs get -H -o property all $dtst | grep ":")

		typeset prop
		for prop in $user_prop; do
			zfs inherit $prop $dt ||
				log_must zfs inherit $prop $dt
		done
	done
}

#
# Random select character from the specified character set and combine into a
# random string
#
# $1 character set name
# $2 String length
#
function random_string
{
	typeset char_set=${1:-VALID_NAME_CHAR}
	typeset -i len=${2:-5}

	eval typeset -i count=\${#$char_set[@]}

	# No consumers want an empty string.
	((len == 0)) && len=3

	typeset str
	typeset -i i=0
	while ((i < len)); do
		typeset -i ind
		((ind = RANDOM % count))
		eval str=\${str}\${$char_set[\$ind]}

		((i += 1))
	done

	echo "$str"
}

#
# Get valid user defined property name
#
# $1 user defined property name length
#
function valid_user_property
{
	typeset -i sumlen=${1:-10}
	((sumlen < 2 )) && sumlen=2
	typeset -i len
	((len = RANDOM % sumlen))
	typeset part1 part2

	while true; do
		part1="$(random_string VALID_NAME_CHAR $len)"
		if [[ "$part1" == "-"* ]]; then
			continue
		fi
		break
	done
	((len = sumlen - (len + 1)))

	while true; do
		part2="$(random_string VALID_NAME_CHAR $len)"
		if [[ -z $part1 && -z $part2 ]]; then
			continue
		fi
		break
	done

	echo "${part1}:${part2}"
}

#
# Get invalid user defined property name
#
# $1 user defined property name length
#
function invalid_user_property
{
	typeset -i sumlen=${1:-10}
	((sumlen == 0)) && sumlen=1
	typeset -i len
	((len = RANDOM % sumlen))

	typeset part1 part2
	while true; do
		part1="$(random_string VALID_NAME_CHAR $len)"
		((len = sumlen - len))
		part2="$(random_string INVALID_NAME_CHAR $len)"

		# Avoid $part1 is *:* and $part2 is "=*"
		if [[ "$part1" == *":"* && "$part2" == "="* ]]; then
			continue
		fi
		break
	done

	echo "${part1}${part2}"
}

#
# Get user property value
#
# $1 user defined property name length
#
function user_property_value
{
	typeset -i len=${1:-100}

	random_string ALL_CHAR $len
}

#
# Check if the user property is identical to the expected value.
#
# $1 dataset
# $2 user property
# $3 expected value
#
function check_user_prop
{
	typeset dtst=$1
	typeset user_prop="$2"
	typeset expect_value="$3"
	typeset value=$(zfs get -p -H -o value "$user_prop" $dtst 2>&1)

	[ "$expect_value" = "$value" ]
}

#
# Get source of the dataset
#
function get_source
{
	typeset prop=$1
	typeset dataset=$2

	zfs get -H -o source $prop $dataset ||
                log_fail "Unable to get $prop source for dataset $dataset"
}

#
# Verify property $2 is set from source $4 on dataset $1 and has value $3.
#
# $1 checked dataset
# $2 user property
# $3 property value
# $4 source
#
# Returns: 0 if both expected source and value match, 1 otherwise
#
function check_prop_source
{
        typeset dataset="$1"
        typeset prop="$2"
        typeset value="$3"
        typeset source="$4"
        typeset chk_value=$(get_prop "$prop" "$dataset")
        typeset chk_source=$(get_source "$prop" "$dataset")

	if [[ "$chk_value" != "$value" || "$chk_source" != "$source" ]]
	then
		log_note "expected (value '$value', source '$source')," \
			"got (value '$chk_value', source '$chk_source')"
		return 1
	else
		return 0
	fi
}

#
# Verify target dataset $1 inherit property $2 from dataset $3.
#
# $1 checked dataset
# $2 property
# $3 inherited dataset
#
# Returns: 0 if property has expected value and is inherited, 1 otherwise
#
function check_prop_inherit
{
        typeset checked_dtst="$1"
        typeset prop="$2"
        typeset inherited_dtst="$3"
        typeset inherited_value=$(get_prop "$prop" "$inherited_dtst")
        typeset value=$(get_prop "$prop" "$checked_dtst")
        typeset source=$(get_source "$prop" "$checked_dtst")

        [ "$value" = "$inherited_value" ] &&
            [ "$source" = "inherited from $inherited_dtst" ]
}

#
# Verify property $2 received value on dataset $1 has value $3
#
# $1 checked dataset
# $2 property name
# $3 checked value
#
# Returns: 0 if property has expected value and is received, 1 otherwise
#
function check_prop_received
{
        typeset dataset="$1"
        typeset prop="$2"
        typeset value="$3"

        received=$(zfs get -H -o received "$prop" "$dataset") ||
                log_fail "Unable to get $prop received value for dataset $dataset"
        [ "$received" = "$value" ]
}

#
# Verify user property $2 is not set on dataset $1
#
# $1 checked dataset
# $2 property name
#
# Returns: 0 if property is missing (not set), 1 otherwise
#
function check_prop_missing
{
        typeset dataset="$1"
        typeset prop="$2"

        value=$(zfs get -H -o value "$prop" "$dataset") ||
                log_fail "Unable to get $prop value for dataset $dataset"
        [ "$value" = "-" ]
}
