#!/bin/ksh
# 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 (c) 2016, 2018 by Delphix. All rights reserved.
#

. $STF_SUITE/include/libtest.shlib
. $STF_SUITE/tests/functional/rsend/rsend.kshlib
. $STF_SUITE/tests/functional/redacted_send/redacted.cfg

if ! is_linux; then
	alias udevadm=:
fi

function setup_dataset
{
	typeset ds_name=$1
	typeset opts=$2
	typeset file_create_func=$3
	typeset sendfs="$POOL/$ds_name"
	[[ -n $file_create_func ]] || file_create_func=setup_common

	log_must zfs create $opts $sendfs

	$file_create_func $sendfs

	log_must zfs snapshot $sendfs@snap
	log_must zfs clone $opts $sendfs@snap $POOL/${ds_name}_clone
	log_must zfs snapshot $POOL/${ds_name}_clone@snap
}

function setup_common
{
	typeset sendfs=$1

	typeset mntpnt=$(get_prop mountpoint $sendfs)
	typeset bs=$(get_prop recsize $sendfs)
	log_must dd if=/dev/urandom of=$mntpnt/f1 bs=$bs count=16
	log_must dd if=/dev/urandom of=$mntpnt/f2 bs=$bs count=32
}

function setup_embedded
{
	typeset sendfs=$1

	typeset recsize
	typeset mntpnt=$(get_prop mountpoint $sendfs)
	for recsize in 512 1024 2048 4096 8192 16384; do
		if is_illumos; then
			log_must mkholes -d $((recsize - 8)):8 $mntpnt/$recsize
		else
			log_must dd if=/dev/urandom of=$mntpnt/$recsize bs=8 \
			    count=1 seek=$(((recsize / 8) - 1))
		fi
	done
}

function setup_holes
{
	typeset sendfs=$1

	typeset mntpnt=$(get_prop mountpoint $sendfs)
	typeset M=$((1024 * 1024))

	if is_illumos; then
		log_must mkholes -d 0:$((8 * M)) $mntpnt/f1
		log_must mkholes -d 0:$M -d $((7 * M)):$M $mntpnt/f2
		log_must mkholes -d $M:$((6 * M)) -h $((7 * M)):$M $mntpnt/f3
		log_must mkholes -h 0:$((8 * M)) $mntpnt/f4
	else
		log_must dd if=/dev/urandom of=$mntpnt/f1 bs=8M count=1

		log_must dd if=/dev/urandom of=$mntpnt/f2 bs=1M count=1
		log_must dd if=/dev/urandom of=$mntpnt/f2 bs=1M count=1 seek=7 \
		    conv=notrunc

		log_must dd if=/dev/urandom of=$mntpnt/f3 bs=1M count=6 seek=1
		log_must truncate -s $((8 * M)) $mntpnt/f3

		log_must truncate -s $((8 * M)) $mntpnt/f4
	fi

	log_must zfs create $sendfs/manyrm
	for i in {1..256}; do
		log_must stride_dd -i /dev/urandom -o $mntpnt/manyrm/f$i -b 512 \
		    -c $(random_int_between 1 100) -s $(random_int_between 1 4)
	done

	log_must zfs snapshot $sendfs/manyrm@snap
	log_must zfs clone $sendfs/manyrm@snap $sendfs/manyrm_clone
	log_must zfs snapshot $sendfs/manyrm_clone@snap
}

function setup_incrementals
{
	typeset sendfs=$1

	typeset mntpnt=$(get_prop mountpoint $sendfs)
	typeset bs=$(get_prop recsize $sendfs)
	log_must dd if=/dev/urandom of=$mntpnt/f1 bs=$bs count=16
	log_must dd if=/dev/urandom of=$mntpnt/f2 bs=$bs count=32
	log_must mkdir $mntpnt/d1
	log_must eval "cat $mntpnt/f1 $mntpnt/f2 >$mntpnt/d1/f1"
	log_must zfs snapshot $sendfs@snap0

	log_must zfs clone $sendfs@snap0 $POOL/hole
	mntpnt=$(get_prop mountpoint $POOL/hole)
	log_must dd if=/dev/zero of=$mntpnt/f2 bs=$bs count=16 conv=notrunc
	log_must zfs snapshot $POOL/hole@snap

	log_must zfs clone $sendfs@snap0 $POOL/stride3
	mntpnt=$(get_prop mountpoint $POOL/stride3)
	log_must stride_dd -i /dev/urandom -o $mntpnt/f2 -b $bs -c 11 -s 3
	log_must zfs snapshot $POOL/stride3@snap

	log_must zfs clone $sendfs@snap0 $POOL/stride5
	mntpnt=$(get_prop mountpoint $POOL/stride5)
	log_must stride_dd -i /dev/urandom -o $mntpnt/f2 -b $bs -c 7 -s 5
	log_must zfs snapshot $POOL/stride5@snap

	log_must zfs clone $sendfs@snap0 $POOL/int
	log_must zfs snapshot $POOL/int@snap

	log_must zfs clone $POOL/int@snap $POOL/rm
	mntpnt=$(get_prop mountpoint $POOL/rm)
	log_must rm -rf $mntpnt/[df][12]
	log_must zfs snapshot $POOL/rm@snap

	log_must zfs clone $POOL/int@snap $POOL/write
	mntpnt=$(get_prop mountpoint $POOL/write)
	log_must dd if=/dev/urandom of=$mntpnt/f1 bs=512 count=16 conv=notrunc
	log_must dd if=/dev/urandom of=$mntpnt/d1/f1 bs=512 count=16 seek=16 \
	    conv=notrunc
	log_must zfs snapshot $POOL/write@snap
}

function setup_mounts
{
	typeset sendfs=$1

	typeset mntpnt=$(get_prop mountpoint $sendfs)
	log_must touch $mntpnt/empty
	log_must dd if=/dev/urandom of=$mntpnt/contents1 bs=512 count=2
	log_must dd if=/dev/urandom of=$mntpnt/contents2 bs=512 count=2
	log_must mkdir $mntpnt/dir1
	log_must touch $mntpnt/dir1/empty
	log_must dd if=/dev/urandom of=$mntpnt/dir1/contents1 bs=512 count=2
	log_must dd if=/dev/urandom of=$mntpnt/dir1/contents2 bs=512 count=2
	log_must mkdir $mntpnt/dir1/dir2
	log_must touch $mntpnt/dir1/dir2/empty
	log_must dd if=/dev/urandom of=$mntpnt/dir1/dir2/file bs=512 count=2

	log_must zfs create -s -V 16p $sendfs/vol
	log_must zfs snapshot $sendfs/vol@snap
	log_must zfs clone $sendfs/vol@snap $sendfs/vol_clone
	log_must zfs snapshot $sendfs/vol_clone@snap
}

function mount_redacted
{
	typeset flag=''
	while getopts "f" opt; do
		case $opt in
		f)
			flag='-f'
			;;
		esac
	done
	shift $(($OPTIND - 1))

	typeset ds=$1
	log_must set_tunable32 ALLOW_REDACTED_DATASET_MOUNT 1
	zfs mount $flag -oro $ds || return 1
	log_must set_tunable32 ALLOW_REDACTED_DATASET_MOUNT 0
	return 0
}

function unmount_redacted
{
	typeset ds=$1

	zfs unmount $ds
}

#
# This function calls a utility that prints out the ranges where a file
# and its redacted counterpart differ, each range on a new line like this:
#
# 0,131072
# 1966080,131072
# 3932160,131072
#
# The output is then checked against a variable containing the expected
# output to verify the redacted ranges are the ones expected.
#
function compare_files
{
	typeset sendfs=$1
	typeset recvfs=$2
	typeset file=$3
	typeset expected="$4"
	typeset tmpfile="$tmpdir/get_file.out"

	log_must mount_redacted -f $recvfs

	typeset file1="$(get_prop mountpoint $sendfs)/$file"
	typeset file2="$(get_prop mountpoint $recvfs)/$file"
	log_note "Comparing $file1 and $file2"
	[[ -f $file1 ]] || log_fail "File $file1 does not exist."
	[[ -f $file2 ]] || log_fail "File $file2 does not exist."

	log_must eval "get_diff $file1 $file2 >$tmpfile"
	typeset range="$(<$tmpfile)"
	log_must unmount_redacted $recvfs
	[[ "$expected" = "$range" ]] || log_fail "Unexpected range: $range"
}

function redacted_cleanup
{
	typeset ds_list=$@
	typeset ds

	for ds in $ds_list; do
		zfs destroy -R $ds
	done

	set_tunable32 ALLOW_REDACTED_DATASET_MOUNT 0
	rm -f $(get_prop mountpoint $POOL)/tmp/*
}

# Retrieve the redaction list of a bookmark or snapshot, using
# the property or zdb output, as requested.
function get_guid_list
{
	typeset filename=$1
	typeset dataset=$2
	typeset use_zdb=${3:-false}

	if $use_zdb; then
		guid_list=$(zdb -vvvv $dataset | sed -e 's/,//g' \
		    -ne 's/^.*Snapshots: \[\(.*\)\]/\1/p')
	else
		guid_list=$(get_prop redact_snaps $dataset)
	fi

	for guid in $(echo $guid_list | tr ',' ' '); do
		echo $guid
	done | sort >$filename
}
