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 if is_illumos; then 55 histcmd=$(echo $fullcmd | sed 's/\/usr\/sbin\///g') 56 else 57 histcmd=$(echo $fullcmd | sed 's/^.*\/\(zpool .*\).*$/\1/') 58 histcmd=$(echo $histcmd | sed 's/^.*\/\(zfs .*\).*$/\1/') 59 fi 60 61 cmd=$(echo $histcmd | awk '{print $1}') 62 subcmd=$(echo $histcmd | awk '{print $2}') 63 64 # If we aren't running zpool or zfs, something is wrong 65 [[ $cmd == "zpool" || $cmd == "zfs" ]] || \ 66 log_fail "run_and_verify called with \"$cmd ($fullcmd)\"" 67 68 # If this is a 'zfs receive' truncate the stdin redirect 69 [[ $subcmd == "receive" || $subcmd == "recv" ]] && \ 70 histcmd=${histcmd%% <*} 71 72 # Run the command as the specified user, and find the new history. 73 zpool history $flags $pool > $OLD_HISTORY 2>/dev/null 74 if [[ $user == "root" ]]; then 75 log_must_busy eval "$fullcmd" 76 else 77 log_must_busy user_run $user "$fullcmd" 78 fi 79 zpool history $flags $pool > $TMP_HISTORY 2>/dev/null 80 diff $OLD_HISTORY $TMP_HISTORY | grep "^> " | sed 's/^> //g' \ 81 > $NEW_HISTORY 82 83 # Verify what's common to every case, regardless of zpool history flags. 84 grep "$histcmd" $NEW_HISTORY >/dev/null 2>&1 || \ 85 log_fail "Didn't find \"$histcmd\" in pool history" 86 87 # If 'zpool history' was called without any flags, then we're done. 88 [[ -z $flags ]] && return 89 90 # Verify the new history in cases that are more interesting because 91 # additional information is logged with -i or -l. 92 93 [[ $flags =~ "i" ]] && log_must verify_$subcmd "$histcmd" "$subcmd" \ 94 "$flags" 95 [[ $flags =~ "l" ]] && log_must verify_long "$histcmd" "$user" "$flags" 96} 97 98function verify_long 99{ 100 typeset cmd=$1 101 typeset user=$2 102 typeset flags=$3 103 104 [[ $flags =~ "l" ]] || return 1 105 106 typeset uid=$(id -u $user) 107 typeset hname=$(hostname) 108 if ! is_global_zone; then 109 hname=$hname:$(zonename) 110 fi 111 112 typeset suffix="" 113 if is_linux; then 114 suffix=":linux" 115 elif is_freebsd; then 116 suffix=":freebsd" 117 fi 118 119 grep -q "$cmd \[user $uid ($user) on $hname$suffix\]" $NEW_HISTORY 120 if [[ $? != 0 ]]; then 121 log_note "Couldn't find long information for \"$cmd\"" 122 return 1 123 fi 124 125 return 0 126} 127 128function verify_hold 129{ 130 typeset cmd=$1 131 typeset subcmd=$2 132 typeset flags=$3 133 134 [[ $flags =~ "i" ]] || return 1 135 136 typeset tag=$(echo $cmd | awk '{print $4}') 137 typeset fullname=${cmd##* } 138 typeset dsname=${fullname%%@*} 139 typeset snapname=${fullname##*@} 140 141 # This works whether or not the hold was recursive 142 for ds in $(zfs list -r -Ho name -t snapshot $dsname | \ 143 grep "@$snapname"); do 144 grep "$subcmd $ds ([0-9]*) tag=$tag" $NEW_HISTORY \ 145 >/dev/null 2>&1 146 if [[ $? != 0 ]]; then 147 log_note "Didn't find hold on $ds with $tag" 148 return 1 149 fi 150 done 151 152 return 0 153} 154 155function verify_release 156{ 157 # hold and release formats only differ by the subcommand name, so 158 # simply reuse the hold function. 159 verify_hold "$1" "release" "$3" 160} 161 162function verify_rollback 163{ 164 typeset cmd=$1 165 typeset flags=$3 166 167 [[ $flags =~ "i" ]] || return 1 168 169 typeset fullname=${cmd##* } 170 typeset dsname=${fullname%%@*} 171 typeset parent_fs=${dsname##*/} 172 typeset rb_fs=${dsname}/%rollback 173 typeset snapname=${fullname##*@} 174 175 grep "clone swap $rb_fs ([0-9]*) parent=$parent_fs" $NEW_HISTORY \ 176 >/dev/null 2>&1 177 if [[ $? != 0 ]]; then 178 log_note "Didn't find rollback clone swap in pool history" 179 return 1 180 fi 181 182 grep "destroy $rb_fs" $NEW_HISTORY >/dev/null 2>&1 183 if [[ $? != 0 ]]; then 184 log_note "Didn't find rollback destroy in pool history" 185 return 1 186 fi 187 188 return 0 189} 190 191function verify_inherit 192{ 193 typeset cmd=$1 194 typeset flags=$3 195 196 [[ $flags =~ "i" ]] || return 1 197 198 typeset dsname=${cmd##* } 199 typeset prop=${cmd% *} 200 prop=${prop##* } 201 202 # This works whether or not the inherit was recursive 203 for ds in $(zfs list -r -Ho name -t filesystem $dsname); do 204 grep "$subcmd $ds ([0-9]*) ${prop}=" $NEW_HISTORY >/dev/null \ 205 2>&1 206 if [[ $? != 0 ]]; then 207 log_note "Didn't find inherit history for $ds" 208 return 1 209 fi 210 done 211 212 return 0 213} 214 215function verify_allow 216{ 217 typeset cmd=$1 218 typeset subcmd=$2 219 typeset flags=$3 220 221 [[ $flags =~ "i" ]] || return 1 222 [[ $subcmd == "allow" ]] && subcmd="update" 223 [[ $subcmd == "unallow" ]] && subcmd="remove" 224 typeset is_set lflag dflag dsname gname gid uname uid opt str code tmp 225 226 # 227 # Here, we determine three things: 228 # - Whether we're operating on a set or an individual permission (which 229 # dictates the case of the first character in the code) 230 # - The name of the dataset we're operating on. 231 # - Whether the operation applies locally or to descendent datasets (or 232 # both) 233 # 234 echo $cmd | awk '{i = NF - 1; print $i}' | grep '@' >/dev/null \ 235 2>&1 && is_set=1 236 dsname=${cmd##* } 237 [[ $cmd =~ "-l " ]] && lflag=1 238 [[ $cmd =~ "-d " ]] && dflag=1 239 if [[ -z $lflag && -z $dflag ]]; then 240 lflag=1 241 dflag=1 242 fi 243 244 # 245 # For each of the five cases below, the operation is essentially the 246 # same. First, use the command passed in to determine what the code at 247 # the end of the pool history will be. The specifics of the code are 248 # described in a block comment at the top of dsl_deleg.c. Once that's 249 # been assembled, check for its presence in the history, and return 250 # success or failure accordingly. 251 # 252 if [[ $cmd =~ "-s " ]]; then 253 str="s-\$@" 254 [[ -n $is_set ]] && str="S-\$@" 255 tmp=${cmd#*@} 256 code="$str${tmp% *}" 257 grep "permission $subcmd $dsname ([0-9]*) $code" \ 258 $NEW_HISTORY >/dev/null 2>&1 259 if [[ $? != 0 ]]; then 260 log_note "Couldn't find $code in $NEW_HISTORY" 261 return 1 262 fi 263 elif [[ $cmd =~ "-c " ]]; then 264 str="c-\$" 265 [[ -n $is_set ]] && str="C-\$" 266 tmp=${cmd#*-c} 267 code="$str${tmp% *}" 268 grep "permission $subcmd $dsname ([0-9]*) $code" \ 269 $NEW_HISTORY >/dev/null 2>&1 270 if [ $? != 0 ]]; then 271 log_note "Couldn't find $code in $NEW_HISTORY" 272 return 1 273 fi 274 elif [[ $cmd =~ "-u " ]]; then 275 str="u" 276 [[ -n $is_set ]] && str="U" 277 tmp=${cmd##*-u } 278 opt=$(echo $tmp | awk '{print $2}') 279 uid=$(id -u ${tmp%% *}) 280 if [[ -n $lflag ]]; then 281 code="${str}l\$$uid $opt" 282 grep "permission $subcmd $dsname ([0-9]*) $code" \ 283 $NEW_HISTORY >/dev/null 2>&1 284 if [ $? != 0 ]]; then 285 log_note "Couldn't find $code in $NEW_HISTORY" 286 return 1 287 fi 288 fi 289 if [[ -n $dflag ]]; then 290 code="${str}d\$$uid $opt" 291 grep "permission $subcmd $dsname ([0-9]*) $code" \ 292 $NEW_HISTORY >/dev/null 2>&1 293 if [ $? != 0 ]]; then 294 log_note "Couldn't find $code in $NEW_HISTORY" 295 return 1 296 fi 297 fi 298 elif [[ $cmd =~ "-g " ]]; then 299 str="g" 300 [[ -n $is_set ]] && str="G" 301 tmp=${cmd##*-g } 302 opt=$(echo $tmp | awk '{print $2}') 303 gid=$(awk -F: "/^${tmp%% *}:/ {print \$3}" /etc/group) 304 if [[ -n $lflag ]]; then 305 code="${str}l\$$gid $opt" 306 grep "permission $subcmd $dsname ([0-9]*) $code" \ 307 $NEW_HISTORY >/dev/null 2>&1 308 if [ $? != 0 ]]; then 309 log_note "Couldn't find $code in $NEW_HISTORY" 310 return 1 311 fi 312 fi 313 if [[ -n $dflag ]]; then 314 code="${str}d\$$gid $opt" 315 grep "permission $subcmd $dsname ([0-9]*) $code" \ 316 $NEW_HISTORY >/dev/null 2>&1 317 if [ $? != 0 ]]; then 318 log_note "Couldn't find $code in $NEW_HISTORY" 319 return 1 320 fi 321 fi 322 elif [[ $cmd =~ "-e " ]]; then 323 str="e" 324 [[ -n $is_set ]] && str="E" 325 opt=${cmd##*-e } 326 opt=${opt%% *} 327 if [[ -n $lflag ]]; then 328 code="${str}l\$ $opt" 329 grep "permission $subcmd $dsname ([0-9]*) $code" \ 330 $NEW_HISTORY >/dev/null 2>&1 331 if [ $? != 0 ]]; then 332 log_note "Couldn't find $code in $NEW_HISTORY" 333 return 1 334 fi 335 fi 336 if [[ -n $dflag ]]; then 337 code="${str}d\$ $opt" 338 grep "permission $subcmd $dsname ([0-9]*) $code" \ 339 $NEW_HISTORY >/dev/null 2>&1 340 if [ $? != 0 ]]; then 341 log_note "Couldn't find $code in $NEW_HISTORY" 342 return 1 343 fi 344 fi 345 else 346 log_note "Can't parse command \"$cmd\"" 347 return 1 348 fi 349 350 return 0 351} 352 353function verify_unallow 354{ 355 # 356 # The unallow and allow history have the same format, except the former 357 # logs "permission removed" and the latter "permission updated" so 358 # simply reuse the allow function. 359 # 360 verify_allow "$1" "unallow" "$3" 361} 362 363function verify_destroy 364{ 365 typeset cmd=$1 366 typeset flags=$3 367 368 # This function doesn't currently verify the zpool command. 369 [[ ${cmd%% *} == "zfs" ]] || return 1 370 [[ $flags =~ "i" ]] || return 1 371 372 typeset dsname=${cmd##* } 373 [[ $dsname =~ "@" ]] && typeset is_snap=1 374 375 if [[ -n $is_snap ]]; then 376 grep "ioctl destroy_snaps" $NEW_HISTORY >/dev/null 2>&1 377 if [[ $? != 0 ]]; then 378 log_note "Didn't find ioctl while destroying $dsname" 379 return 1 380 fi 381 fi 382 383 # This should be present for datasets and snapshots alike 384 grep "destroy $dsname" $NEW_HISTORY >/dev/null 2>&1 385 if [[ $? != 0 ]]; then 386 log_note "Didn't find \"destroy\" for $dsname" 387 return 1 388 fi 389 390 return 0 391} 392 393function verify_snapshot 394{ 395 typeset cmd=$1 396 typeset flags=$3 397 398 [[ $flags =~ "i" ]] || return 1 399 400 typeset fullname=${cmd##* } 401 typeset dsname=${fullname%%@*} 402 typeset snapname=${fullname##*@} 403 404 grep "\[txg:[0-9]*\] $subcmd $fullname ([0-9]*)" $NEW_HISTORY \ 405 >/dev/null 2>&1 406 if [[ $? != 0 ]]; then 407 log_note "Didn't find snapshot command for $fullname" 408 return 1 409 fi 410 411 # This works whether or not the snapshot was recursive 412 for ds in $(zfs list -r -Ho name -t snapshot $dsname | \ 413 grep "@$snapname"); do 414 grep "^[ ]* $ds$" $NEW_HISTORY >/dev/null 2>&1 415 if [[ $? != 0 ]]; then 416 log_note "Didn't find \"ioctl snapshot\" for $ds" 417 return 1 418 fi 419 done 420 421 return 0 422} 423