| # |
| # 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" |
| |
| if is_linux; then |
| histcmd=$(echo $fullcmd | sed 's/^.*\/\(zpool .*\).*$/\1/') |
| histcmd=$(echo $histcmd | sed 's/^.*\/\(zfs .*\).*$/\1/') |
| else |
| histcmd=$(echo $fullcmd | sed 's/\/usr\/sbin\///g') |
| fi |
| |
| 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 user_run $user "$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 |
| |
| typeset suffix="" |
| if is_linux; then |
| suffix=":linux" |
| fi |
| |
| grep "$cmd \[user $uid ($user) on $hname$suffix\]" \ |
| $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 individual 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 |
| } |