1 #
   2 # CDDL HEADER START
   3 #
   4 # The contents of this file are subject to the terms of the
   5 # Common Development and Distribution License (the "License").
   6 # You may not use this file except in compliance with the License.
   7 #
   8 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9 # or http://www.opensolaris.org/os/licensing.
  10 # See the License for the specific language governing permissions
  11 # and limitations under the License.
  12 #
  13 # When distributing Covered Code, include this CDDL HEADER in each
  14 # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15 # If applicable, add the following below this CDDL HEADER, with the
  16 # fields enclosed by brackets "[]" replaced with your own identifying
  17 # information: Portions Copyright [yyyy] [name of copyright owner]
  18 #
  19 # CDDL HEADER END
  20 #
  21 
  22 #
  23 # Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  24 # Use is subject to license terms.
  25 #
  26 
  27 #
  28 # Copyright (c) 2013 by Delphix. All rights reserved.
  29 #
  30 
  31 . $STF_SUITE/include/libtest.shlib
  32 . $STF_SUITE/tests/functional/history/history.cfg
  33 
  34 function run_and_verify
  35 {
  36         typeset user pool
  37         while getopts "p:u:" opt; do
  38                 case $opt in
  39                 p)
  40                         pool=$OPTARG
  41                         ;;
  42                 u)
  43                         user=$OPTARG
  44                         ;;
  45                 esac
  46         done
  47         shift $(($OPTIND - 1))
  48 
  49         pool=${pool:-$TESTPOOL}
  50         user=${user:-"root"}
  51         fullcmd="$1"
  52         flags="$2"
  53 
  54         histcmd=$($ECHO $fullcmd | $SED 's/\/usr\/sbin\///g')
  55         cmd=$($ECHO $histcmd | $AWK '{print $1}')
  56         subcmd=$($ECHO $histcmd | $AWK '{print $2}')
  57 
  58         # If we aren't running zpool or zfs, something is wrong
  59         [[ $cmd == "zpool" || $cmd == "zfs" ]] || \
  60             log_fail "run_and_verify called with \"$cmd ($fullcmd)\""
  61 
  62         # If this is a 'zfs receive' truncate the stdin redirect
  63         [[ $subcmd == "receive" || $subcmd == "recv" ]] && \
  64             histcmd=${histcmd%% <*}
  65 
  66         # Run the command as the specified user, and find the new history.
  67         $ZPOOL history $flags $pool > $OLD_HISTORY 2>/dev/null
  68         if [[ $user == "root" ]]; then
  69                 log_must eval "$fullcmd"
  70         else
  71                 log_must $SU $user -c "eval $fullcmd"
  72         fi
  73         $ZPOOL history $flags $pool > $TMP_HISTORY 2>/dev/null
  74         $DIFF $OLD_HISTORY $TMP_HISTORY | $GREP "^> " | $SED 's/^> //g' \
  75             > $NEW_HISTORY
  76 
  77         # Verify what's common to every case, regardless of zpool history flags.
  78         $GREP "$histcmd" $NEW_HISTORY >/dev/null 2>&1 || \
  79             log_fail "Didn't find \"$histcmd\" in pool history"
  80 
  81         # If 'zpool history' was called without any flags, then we're done.
  82         [[ -z $flags ]] && return
  83 
  84         # Verify the new history in cases that are more interesting because
  85         # additional information is logged with -i or -l.
  86 
  87         [[ $flags =~ "i" ]] && log_must verify_$subcmd "$histcmd" "$subcmd" \
  88             "$flags"
  89         [[ $flags =~ "l" ]] && log_must verify_long "$histcmd" "$user" "$flags"
  90 }
  91 
  92 function verify_long
  93 {
  94         typeset cmd=$1
  95         typeset user=$2
  96         typeset flags=$3
  97 
  98         [[ $flags =~ "l" ]] || return 1
  99 
 100         typeset uid=$($ID -u $user)
 101         typeset hname=$($HOSTNAME)
 102         if ! is_global_zone; then
 103                 hname=$hname:$($ZONENAME)
 104         fi
 105 
 106         $GREP "$cmd \[user $uid ($user) on $hname\]" $NEW_HISTORY >/dev/null \
 107             2>&1
 108         if [[ $? != 0 ]]; then
 109                 log_note "Couldn't find long information for \"$cmd\""
 110                 return 1
 111         fi
 112 
 113         return 0
 114 }
 115 
 116 function verify_hold
 117 {
 118         typeset cmd=$1
 119         typeset subcmd=$2
 120         typeset flags=$3
 121 
 122         [[ $flags =~ "i" ]] || return 1
 123 
 124         typeset tag=$($ECHO $cmd | $AWK '{print $4}')
 125         typeset fullname=${cmd##* }
 126         typeset dsname=${fullname%%@*}
 127         typeset snapname=${fullname##*@}
 128 
 129         # This works whether or not the hold was recursive
 130         for ds in $($ZFS list -r -Ho name -t snapshot $dsname | \
 131             $GREP "@$snapname"); do
 132                 $GREP "$subcmd $ds ([0-9]*) tag=$tag" $NEW_HISTORY \
 133                     >/dev/null 2>&1
 134                 if [[ $? != 0 ]]; then
 135                         log_note "Didn't find hold on $ds with $tag"
 136                         return 1
 137                 fi
 138         done
 139 
 140         return 0
 141 }
 142 
 143 function verify_release
 144 {
 145         # hold and release formats only differ by the subcommand name, so
 146         # simply reuse the hold function.
 147         verify_hold "$1" "release" "$3"
 148 }
 149 
 150 function verify_rollback
 151 {
 152         typeset cmd=$1
 153         typeset flags=$3
 154 
 155         [[ $flags =~ "i" ]] || return 1
 156 
 157         typeset fullname=${cmd##* }
 158         typeset dsname=${fullname%%@*}
 159         typeset parent_fs=${dsname##*/}
 160         typeset rb_fs=${dsname}/%rollback
 161         typeset snapname=${fullname##*@}
 162 
 163         $GREP "clone swap $rb_fs ([0-9]*) parent=$parent_fs" $NEW_HISTORY \
 164             >/dev/null 2>&1
 165         if [[ $? != 0 ]]; then
 166                 log_note "Didn't find rollback clone swap in pool history"
 167                 return 1
 168         fi
 169 
 170         $GREP "destroy $rb_fs" $NEW_HISTORY >/dev/null 2>&1
 171         if [[ $? != 0 ]]; then
 172                 log_note "Didn't find rollback destroy in pool history"
 173                 return 1
 174         fi
 175 
 176         return 0
 177 }
 178 
 179 function verify_inherit
 180 {
 181         typeset cmd=$1
 182         typeset flags=$3
 183 
 184         [[ $flags =~ "i" ]] || return 1
 185 
 186         typeset dsname=${cmd##* }
 187         typeset prop=${cmd% *}
 188         prop=${prop##* }
 189 
 190         # This works whether or not the inherit was recursive
 191         for ds in $($ZFS list -r -Ho name -t filesystem $dsname); do
 192                 $GREP "$subcmd $ds ([0-9]*) ${prop}=" $NEW_HISTORY >/dev/null \
 193                     2>&1
 194                 if [[ $? != 0 ]]; then
 195                         log_note "Didn't find inherit history for $ds"
 196                         return 1
 197                 fi
 198         done
 199 
 200         return 0
 201 }
 202 
 203 function verify_allow
 204 {
 205         typeset cmd=$1
 206         typeset subcmd=$2
 207         typeset flags=$3
 208 
 209         [[ $flags =~ "i" ]] || return 1
 210         [[ $subcmd == "allow" ]] && subcmd="update"
 211         [[ $subcmd == "unallow" ]] && subcmd="remove"
 212         typeset is_set lflag dflag dsname gname gid uname uid opt str code tmp
 213 
 214         #
 215         # Here, we determine three things:
 216         # - Whether we're operating on a set or an indivdual permission (which
 217         #   dictates the case of the first character in the code)
 218         # - The name of the dataset we're operating on.
 219         # - Whether the operation applies locally or to descendent datasets (or
 220         #   both)
 221         #
 222         $ECHO $cmd  | $AWK '{i = NF - 1; print $i}' | $GREP '@' >/dev/null \
 223             2>&1 && is_set=1
 224         dsname=${cmd##* }
 225         [[ $cmd =~ "-l " ]] && lflag=1
 226         [[ $cmd =~ "-d " ]] && dflag=1
 227         if [[ -z $lflag && -z $dflag ]]; then
 228                 lflag=1
 229                 dflag=1
 230         fi
 231 
 232         #
 233         # For each of the five cases below, the operation is essentially the
 234         # same. First, use the command passed in to determine what the code at
 235         # the end of the pool history will be. The specifics of the code are
 236         # described in a block comment at the top of dsl_deleg.c. Once that's
 237         # been assembled, check for its presence in the history, and return
 238         # success or failure accordingly.
 239         #
 240         if [[ $cmd =~ "-s " ]]; then
 241                 str="s-\$@"
 242                 [[ -n $is_set ]] && str="S-\$@"
 243                 tmp=${cmd#*@}
 244                 code="$str${tmp% *}"
 245                 $GREP "permission $subcmd $dsname ([0-9]*) $code" \
 246                     $NEW_HISTORY >/dev/null 2>&1
 247                 if [[ $? != 0 ]]; then
 248                          log_note "Couldn't find $code in $NEW_HISTORY"
 249                          return 1
 250                  fi
 251         elif [[ $cmd =~ "-c " ]]; then
 252                 str="c-\$"
 253                 [[ -n $is_set ]] && str="C-\$"
 254                 tmp=${cmd#*-c}
 255                 code="$str${tmp% *}"
 256                 $GREP "permission $subcmd $dsname ([0-9]*) $code" \
 257                     $NEW_HISTORY >/dev/null 2>&1
 258                 if [ $? != 0 ]]; then
 259                          log_note "Couldn't find $code in $NEW_HISTORY"
 260                          return 1
 261                 fi
 262         elif [[ $cmd =~ "-u " ]]; then
 263                 str="u"
 264                 [[ -n $is_set ]] && str="U"
 265                 tmp=${cmd##*-u }
 266                 opt=$($ECHO $tmp | $AWK '{print $2}')
 267                 uid=$($ID -u ${tmp%% *})
 268                 if [[ -n $lflag ]]; then
 269                         code="${str}l\$$uid $opt"
 270                         $GREP "permission $subcmd $dsname ([0-9]*) $code" \
 271                             $NEW_HISTORY >/dev/null 2>&1
 272                         if [ $? != 0 ]]; then
 273                                  log_note "Couldn't find $code in $NEW_HISTORY"
 274                                  return 1
 275                         fi
 276                 fi
 277                 if [[ -n $dflag ]]; then
 278                         code="${str}d\$$uid $opt"
 279                         $GREP "permission $subcmd $dsname ([0-9]*) $code" \
 280                             $NEW_HISTORY >/dev/null 2>&1
 281                         if [ $? != 0 ]]; then
 282                                  log_note "Couldn't find $code in $NEW_HISTORY"
 283                                  return 1
 284                         fi
 285                 fi
 286         elif [[ $cmd =~ "-g " ]]; then
 287                 str="g"
 288                 [[ -n $is_set ]] && str="G"
 289                 tmp=${cmd##*-g }
 290                 opt=$($ECHO $tmp | $AWK '{print $2}')
 291                 gid=$($AWK -F: "/^${tmp%% *}:/ {print \$3}" /etc/group)
 292                 if [[ -n $lflag ]]; then
 293                         code="${str}l\$$gid $opt"
 294                         $GREP "permission $subcmd $dsname ([0-9]*) $code" \
 295                             $NEW_HISTORY >/dev/null 2>&1
 296                         if [ $? != 0 ]]; then
 297                                  log_note "Couldn't find $code in $NEW_HISTORY"
 298                                  return 1
 299                         fi
 300                 fi
 301                 if [[ -n $dflag ]]; then
 302                         code="${str}d\$$gid $opt"
 303                         $GREP "permission $subcmd $dsname ([0-9]*) $code" \
 304                             $NEW_HISTORY >/dev/null 2>&1
 305                         if [ $? != 0 ]]; then
 306                                  log_note "Couldn't find $code in $NEW_HISTORY"
 307                                  return 1
 308                         fi
 309                 fi
 310         elif [[ $cmd =~ "-e " ]]; then
 311                 str="e"
 312                 [[ -n $is_set ]] && str="E"
 313                 opt=${cmd##*-e }
 314                 opt=${opt%% *}
 315                 if [[ -n $lflag ]]; then
 316                         code="${str}l\$ $opt"
 317                         $GREP "permission $subcmd $dsname ([0-9]*) $code" \
 318                             $NEW_HISTORY >/dev/null 2>&1
 319                         if [ $? != 0 ]]; then
 320                                  log_note "Couldn't find $code in $NEW_HISTORY"
 321                                  return 1
 322                         fi
 323                 fi
 324                 if [[ -n $dflag ]]; then
 325                         code="${str}d\$ $opt"
 326                         $GREP "permission $subcmd $dsname ([0-9]*) $code" \
 327                             $NEW_HISTORY >/dev/null 2>&1
 328                         if [ $? != 0 ]]; then
 329                                  log_note "Couldn't find $code in $NEW_HISTORY"
 330                                  return 1
 331                         fi
 332                 fi
 333         else
 334                 log_note "Can't parse command \"$cmd\""
 335                 return 1
 336         fi
 337 
 338         return 0
 339 }
 340 
 341 function verify_unallow
 342 {
 343         #
 344         # The unallow and allow history have the same format, except the former
 345         # logs "permission removed" and the latter "permission updated" so
 346         # simply reuse the allow function.
 347         #
 348         verify_allow "$1" "unallow" "$3"
 349 }
 350 
 351 function verify_destroy
 352 {
 353         typeset cmd=$1
 354         typeset flags=$3
 355 
 356         # This function doesn't currently verifiy the zpool command.
 357         [[ ${cmd%% *} == "zfs" ]] || return 1
 358         [[ $flags =~ "i" ]] || return 1
 359 
 360         typeset dsname=${cmd##* }
 361         [[ $dsname =~ "@" ]] && typeset is_snap=1
 362 
 363         if [[ -n $is_snap ]]; then
 364                 $GREP "ioctl destroy_snaps" $NEW_HISTORY >/dev/null 2>&1
 365                 if [[ $? != 0 ]]; then
 366                         log_note "Didn't find ioctl while destroying $dsname"
 367                         return 1
 368                 fi
 369         fi
 370 
 371         # This should be present for datasets and snapshots alike
 372         $GREP "destroy $dsname" $NEW_HISTORY >/dev/null 2>&1
 373         if [[ $? != 0 ]]; then
 374                 log_note "Didn't find \"destroy\" for $dsname"
 375                 return 1
 376         fi
 377 
 378         return 0
 379 }
 380 
 381 function verify_snapshot
 382 {
 383         typeset cmd=$1
 384         typeset flags=$3
 385 
 386         [[ $flags =~ "i" ]] || return 1
 387 
 388         typeset fullname=${cmd##* }
 389         typeset dsname=${fullname%%@*}
 390         typeset snapname=${fullname##*@}
 391 
 392         $GREP "\[txg:[0-9]*\] $subcmd $fullname ([0-9]*)" $NEW_HISTORY \
 393             >/dev/null 2>&1
 394         if [[ $? != 0 ]]; then
 395                 log_note "Didn't find snapshot command for $fullname"
 396                 return 1
 397         fi
 398 
 399         # This works whether or not the snapshot was recursive
 400         for ds in $($ZFS list -r -Ho name -t snapshot $dsname | \
 401             $GREP "@$snapname"); do
 402                 $GREP "^[ ]* $ds$" $NEW_HISTORY >/dev/null 2>&1
 403                 if [[ $? != 0 ]]; then
 404                         log_note "Didn't find \"ioctl snapshot\" for $ds"
 405                         return 1
 406                 fi
 407         done
 408 
 409         return 0
 410 }