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, 2016 by Delphix. All rights reserved. 29# 30 31. $STF_SUITE/include/libtest.shlib 32. $STF_SUITE/tests/functional/history/history.cfg 33 34function 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 92function 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 116function 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 143function 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 150function 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 179function 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 203function 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 341function 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 351function 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 381function 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} 411