#
# 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 http://www.opensolaris.org/os/licensing.
# 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 2007 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
#
# Copyright (c) 2013, 2016 by Delphix. All rights reserved.
#
. $STF_SUITE/include/libtest.shlib
. $STF_SUITE/tests/functional/history/history.cfg
function run_and_verify
{
typeset user pool
while getopts "p:u:" opt; do
case $opt in
p)
pool=$OPTARG
;;
u)
user=$OPTARG
;;
esac
done
shift $(($OPTIND - 1))
pool=${pool:-$TESTPOOL}
user=${user:-"root"}
fullcmd="$1"
flags="$2"
histcmd=$(echo $fullcmd | sed 's/\/usr\/sbin\///g')
cmd=$(echo $histcmd | awk '{print $1}')
subcmd=$(echo $histcmd | awk '{print $2}')
# If we aren't running zpool or zfs, something is wrong
[[ $cmd == "zpool" || $cmd == "zfs" ]] || \
log_fail "run_and_verify called with \"$cmd ($fullcmd)\""
# If this is a 'zfs receive' truncate the stdin redirect
[[ $subcmd == "receive" || $subcmd == "recv" ]] && \
histcmd=${histcmd%% <*}
# Run the command as the specified user, and find the new history.
zpool history $flags $pool > $OLD_HISTORY 2>/dev/null
if [[ $user == "root" ]]; then
log_must eval "$fullcmd"
else
log_must su $user -c "eval $fullcmd"
fi
zpool history $flags $pool > $TMP_HISTORY 2>/dev/null
diff $OLD_HISTORY $TMP_HISTORY | grep "^> " | sed 's/^> //g' \
> $NEW_HISTORY
# Verify what's common to every case, regardless of zpool history flags.
grep "$histcmd" $NEW_HISTORY >/dev/null 2>&1 || \
log_fail "Didn't find \"$histcmd\" in pool history"
# If 'zpool history' was called without any flags, then we're done.
[[ -z $flags ]] && return
# Verify the new history in cases that are more interesting because
# additional information is logged with -i or -l.
[[ $flags =~ "i" ]] && log_must verify_$subcmd "$histcmd" "$subcmd" \
"$flags"
[[ $flags =~ "l" ]] && log_must verify_long "$histcmd" "$user" "$flags"
}
function verify_long
{
typeset cmd=$1
typeset user=$2
typeset flags=$3
[[ $flags =~ "l" ]] || return 1
typeset uid=$(id -u $user)
typeset hname=$(hostname)
if ! is_global_zone; then
hname=$hname:$(zonename)
fi
grep "$cmd \[user $uid ($user) on $hname\]" $NEW_HISTORY >/dev/null \
2>&1
if [[ $? != 0 ]]; then
log_note "Couldn't find long information for \"$cmd\""
return 1
fi
return 0
}
function verify_hold
{
typeset cmd=$1
typeset subcmd=$2
typeset flags=$3
[[ $flags =~ "i" ]] || return 1
typeset tag=$(echo $cmd | awk '{print $4}')
typeset fullname=${cmd##* }
typeset dsname=${fullname%%@*}
typeset snapname=${fullname##*@}
# This works whether or not the hold was recursive
for ds in $(zfs list -r -Ho name -t snapshot $dsname | \
grep "@$snapname"); do
grep "$subcmd $ds ([0-9]*) tag=$tag" $NEW_HISTORY \
>/dev/null 2>&1
if [[ $? != 0 ]]; then
log_note "Didn't find hold on $ds with $tag"
return 1
fi
done
return 0
}
function verify_release
{
# hold and release formats only differ by the subcommand name, so
# simply reuse the hold function.
verify_hold "$1" "release" "$3"
}
function verify_rollback
{
typeset cmd=$1
typeset flags=$3
[[ $flags =~ "i" ]] || return 1
typeset fullname=${cmd##* }
typeset dsname=${fullname%%@*}
typeset parent_fs=${dsname##*/}
typeset rb_fs=${dsname}/%rollback
typeset snapname=${fullname##*@}
grep "clone swap $rb_fs ([0-9]*) parent=$parent_fs" $NEW_HISTORY \
>/dev/null 2>&1
if [[ $? != 0 ]]; then
log_note "Didn't find rollback clone swap in pool history"
return 1
fi
grep "destroy $rb_fs" $NEW_HISTORY >/dev/null 2>&1
if [[ $? != 0 ]]; then
log_note "Didn't find rollback destroy in pool history"
return 1
fi
return 0
}
function verify_inherit
{
typeset cmd=$1
typeset flags=$3
[[ $flags =~ "i" ]] || return 1
typeset dsname=${cmd##* }
typeset prop=${cmd% *}
prop=${prop##* }
# This works whether or not the inherit was recursive
for ds in $(zfs list -r -Ho name -t filesystem $dsname); do
grep "$subcmd $ds ([0-9]*) ${prop}=" $NEW_HISTORY >/dev/null \
2>&1
if [[ $? != 0 ]]; then
log_note "Didn't find inherit history for $ds"
return 1
fi
done
return 0
}
function verify_allow
{
typeset cmd=$1
typeset subcmd=$2
typeset flags=$3
[[ $flags =~ "i" ]] || return 1
[[ $subcmd == "allow" ]] && subcmd="update"
[[ $subcmd == "unallow" ]] && subcmd="remove"
typeset is_set lflag dflag dsname gname gid uname uid opt str code tmp
#
# Here, we determine three things:
# - Whether we're operating on a set or an indivdual permission (which
# dictates the case of the first character in the code)
# - The name of the dataset we're operating on.
# - Whether the operation applies locally or to descendent datasets (or
# both)
#
echo $cmd | awk '{i = NF - 1; print $i}' | grep '@' >/dev/null \
2>&1 && is_set=1
dsname=${cmd##* }
[[ $cmd =~ "-l " ]] && lflag=1
[[ $cmd =~ "-d " ]] && dflag=1
if [[ -z $lflag && -z $dflag ]]; then
lflag=1
dflag=1
fi
#
# For each of the five cases below, the operation is essentially the
# same. First, use the command passed in to determine what the code at
# the end of the pool history will be. The specifics of the code are
# described in a block comment at the top of dsl_deleg.c. Once that's
# been assembled, check for its presence in the history, and return
# success or failure accordingly.
#
if [[ $cmd =~ "-s " ]]; then
str="s-\$@"
[[ -n $is_set ]] && str="S-\$@"
tmp=${cmd#*@}
code="$str${tmp% *}"
grep "permission $subcmd $dsname ([0-9]*) $code" \
$NEW_HISTORY >/dev/null 2>&1
if [[ $? != 0 ]]; then
log_note "Couldn't find $code in $NEW_HISTORY"
return 1
fi
elif [[ $cmd =~ "-c " ]]; then
str="c-\$"
[[ -n $is_set ]] && str="C-\$"
tmp=${cmd#*-c}
code="$str${tmp% *}"
grep "permission $subcmd $dsname ([0-9]*) $code" \
$NEW_HISTORY >/dev/null 2>&1
if [ $? != 0 ]]; then
log_note "Couldn't find $code in $NEW_HISTORY"
return 1
fi
elif [[ $cmd =~ "-u " ]]; then
str="u"
[[ -n $is_set ]] && str="U"
tmp=${cmd##*-u }
opt=$(echo $tmp | awk '{print $2}')
uid=$(id -u ${tmp%% *})
if [[ -n $lflag ]]; then
code="${str}l\$$uid $opt"
grep "permission $subcmd $dsname ([0-9]*) $code" \
$NEW_HISTORY >/dev/null 2>&1
if [ $? != 0 ]]; then
log_note "Couldn't find $code in $NEW_HISTORY"
return 1
fi
fi
if [[ -n $dflag ]]; then
code="${str}d\$$uid $opt"
grep "permission $subcmd $dsname ([0-9]*) $code" \
$NEW_HISTORY >/dev/null 2>&1
if [ $? != 0 ]]; then
log_note "Couldn't find $code in $NEW_HISTORY"
return 1
fi
fi
elif [[ $cmd =~ "-g " ]]; then
str="g"
[[ -n $is_set ]] && str="G"
tmp=${cmd##*-g }
opt=$(echo $tmp | awk '{print $2}')
gid=$(awk -F: "/^${tmp%% *}:/ {print \$3}" /etc/group)
if [[ -n $lflag ]]; then
code="${str}l\$$gid $opt"
grep "permission $subcmd $dsname ([0-9]*) $code" \
$NEW_HISTORY >/dev/null 2>&1
if [ $? != 0 ]]; then
log_note "Couldn't find $code in $NEW_HISTORY"
return 1
fi
fi
if [[ -n $dflag ]]; then
code="${str}d\$$gid $opt"
grep "permission $subcmd $dsname ([0-9]*) $code" \
$NEW_HISTORY >/dev/null 2>&1
if [ $? != 0 ]]; then
log_note "Couldn't find $code in $NEW_HISTORY"
return 1
fi
fi
elif [[ $cmd =~ "-e " ]]; then
str="e"
[[ -n $is_set ]] && str="E"
opt=${cmd##*-e }
opt=${opt%% *}
if [[ -n $lflag ]]; then
code="${str}l\$ $opt"
grep "permission $subcmd $dsname ([0-9]*) $code" \
$NEW_HISTORY >/dev/null 2>&1
if [ $? != 0 ]]; then
log_note "Couldn't find $code in $NEW_HISTORY"
return 1
fi
fi
if [[ -n $dflag ]]; then
code="${str}d\$ $opt"
grep "permission $subcmd $dsname ([0-9]*) $code" \
$NEW_HISTORY >/dev/null 2>&1
if [ $? != 0 ]]; then
log_note "Couldn't find $code in $NEW_HISTORY"
return 1
fi
fi
else
log_note "Can't parse command \"$cmd\""
return 1
fi
return 0
}
function verify_unallow
{
#
# The unallow and allow history have the same format, except the former
# logs "permission removed" and the latter "permission updated" so
# simply reuse the allow function.
#
verify_allow "$1" "unallow" "$3"
}
function verify_destroy
{
typeset cmd=$1
typeset flags=$3
# This function doesn't currently verifiy the zpool command.
[[ ${cmd%% *} == "zfs" ]] || return 1
[[ $flags =~ "i" ]] || return 1
typeset dsname=${cmd##* }
[[ $dsname =~ "@" ]] && typeset is_snap=1
if [[ -n $is_snap ]]; then
grep "ioctl destroy_snaps" $NEW_HISTORY >/dev/null 2>&1
if [[ $? != 0 ]]; then
log_note "Didn't find ioctl while destroying $dsname"
return 1
fi
fi
# This should be present for datasets and snapshots alike
grep "destroy $dsname" $NEW_HISTORY >/dev/null 2>&1
if [[ $? != 0 ]]; then
log_note "Didn't find \"destroy\" for $dsname"
return 1
fi
return 0
}
function verify_snapshot
{
typeset cmd=$1
typeset flags=$3
[[ $flags =~ "i" ]] || return 1
typeset fullname=${cmd##* }
typeset dsname=${fullname%%@*}
typeset snapname=${fullname##*@}
grep "\[txg:[0-9]*\] $subcmd $fullname ([0-9]*)" $NEW_HISTORY \
>/dev/null 2>&1
if [[ $? != 0 ]]; then
log_note "Didn't find snapshot command for $fullname"
return 1
fi
# This works whether or not the snapshot was recursive
for ds in $(zfs list -r -Ho name -t snapshot $dsname | \
grep "@$snapname"); do
grep "^[ ]* $ds$" $NEW_HISTORY >/dev/null 2>&1
if [[ $? != 0 ]]; then
log_note "Didn't find \"ioctl snapshot\" for $ds"
return 1
fi
done
return 0
}