1 #! /usr/bin/ksh
   2 #
   3 # CDDL HEADER START
   4 #
   5 # The contents of this file are subject to the terms of the
   6 # Common Development and Distribution License (the "License").
   7 # You may not use this file except in compliance with the License.
   8 #
   9 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  10 # or http://www.opensolaris.org/os/licensing.
  11 # See the License for the specific language governing permissions
  12 # and limitations under the License.
  13 #
  14 # When distributing Covered Code, include this CDDL HEADER in each
  15 # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  16 # If applicable, add the following below this CDDL HEADER, with the
  17 # fields enclosed by brackets "[]" replaced with your own identifying
  18 # information: Portions Copyright [yyyy] [name of copyright owner]
  19 #
  20 # CDDL HEADER END
  21 #
  22 
  23 #
  24 # Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  25 # Use is subject to license terms.
  26 #
  27 
  28 #
  29 # These cases are intended to test client recovery when server
  30 # randomly returns a BAD_SEQID error.
  31 #
  32 # Strategy
  33 #    Modify seqid on client, inspect the behavior of client
  34 #
  35 
  36 . ${STF_SUITE}/include/nfsgen.kshlib
  37 
  38 NAME=$(basename $0)
  39 
  40 [[ :$NFSGEN_DEBUG: = *:${NAME}:* || :${NFSGEN_DEBUG}: = *:all:* ]] \
  41         && set -x
  42 
  43 DIR=$(dirname $0)
  44 SEQID_DIR=${SEQID_DIR:-"$ZONE_PATH/BadSeqid"}
  45 TESTFILE="$SEQID_DIR/bad_seqid.txt"
  46 SEQID_CASES=${SEQID_CASES:-"a b c"}
  47 
  48 # used to export EXECUTION path
  49 export RECOVERY_EXECUTE_PATH=$DIR
  50 export RECOVERY_STAT_PATH=$STF_SUITE/bin/
  51 
  52 
  53 # distinguish different kinds of error
  54 # For the fatal errors, the test will be terminated and only
  55 # some assertions info will be printed, such as mdb fails.
  56 # For the failure that seqid can't be fetched, the test will
  57 # continue
  58 typeset cont_flg=0
  59 typeset prog=$STF_SUITE/bin/file_operator
  60 
  61 # fail to intialize, print some basic cases info
  62 function prt_info 
  63 {
  64         CODE=$1
  65         shift
  66         MSG="$*"
  67         for seqid_case in $SEQID_CASES; do
  68                 echo "$NAME{$seqid_case}: $MSG"
  69                 echo "\tTest $CODE"
  70         done
  71 }
  72 
  73 # check result and print out failure messages
  74 function ckres 
  75 {
  76         [[ :$NFSGEN_DEBUG: = *:${NAME}:* || :${NFSGEN_DEBUG}: = *:all:* ]] \
  77                 && set -x
  78 
  79         op=$1
  80         st=$2
  81         expt=$3
  82         if [[ -n $4 ]]; then
  83                 res=$4
  84                 # if $res is an operation output file, get its content
  85                 if [[ -f $res ]]; then
  86                         res=$(cat $res)
  87                 else
  88                         # is a string, get all components from $4 to last
  89                         shift; shift; shift
  90                         res="$@"
  91                 fi
  92         fi
  93         tmp=$(echo $expt | sed 's/|/ /g')
  94         expt=$tmp
  95         code="STF_FAIL"
  96         # if exported var for code different from STF_FAIL
  97         [[ -n $ckres_code ]] && code=$ckres_code
  98         ret=$(echo $expt | grep "$st")
  99         if (( $? != 0 )); then
 100                 echo "\tTest $code: $op returned ($st), expected ($expt)"
 101                 [[ -n $res ]] && echo "\t\tres = $res\n"
 102                 return 1
 103         else
 104                 echo "\tTest PASS"
 105                 return 0
 106         fi
 107 }
 108 
 109 
 110 ck_zone 1 " "
 111 if (( $? != 0 )); then
 112         prt_info STF_UNSUPPORTED "Not supported in non-global zone."
 113         exit $STF_UNSUPPORTED
 114 fi
 115 
 116 # the parameters to open the testfile
 117 is_cipso "vers=4" $SERVER
 118 if (( $? == $CIPSO_NFSV4 )); then
 119         #
 120         # For Trusted Extensions the user role
 121         # 'root' while in the global zone can
 122         # read files from non-global zones.
 123         # Non-root roles cannot.
 124         #
 125         PARMS="root"
 126 else
 127         PARMS="$TUSER01"
 128 fi
 129 
 130 # clean up the tmp files
 131 function internalCleanup 
 132 {
 133         [[ :$NFSGEN_DEBUG: = *:${NAME}:* || :${NFSGEN_DEBUG}: = *:all:* ]] \
 134                 && set -x
 135 
 136         typeset local_res=$1
 137         [[ -n $pid1 ]] && kill $pid1 > /dev/null 2>&1 && pid1=""
 138         [[ -n $pid2 ]] && kill $pid2 > /dev/null 2>&1 && pid2=""
 139         
 140         if (( $local_res != 0 )); then
 141                 [[ -s $STF_TMPDIR/$NAME.1.$$ ]] && echo "--$NAME.1.$$--" \
 142                         && cat $STF_TMPDIR/$NAME.1.$$
 143                 [[ -s $STF_TMPDIR/$NAME.2.$$ ]] && echo "--$NAME.2.$$--" \
 144                         && cat $STF_TMPDIR/$NAME.2.$$
 145         fi
 146         umount -f $SEQID_DIR > /dev/null 2>&1
 147         rm -rf $STF_TMPDIR/${NAME}* $SEQID_DIR
 148         exit $local_res
 149 }
 150 
 151 # Start the tests with some information
 152 echo " "
 153 echo "Testing at CLIENT=[`uname -n`] to SERVER=[$SERVER]"
 154 echo "\ton the directory=[$SEQID_DIR]"
 155 echo "Started BAD_SEQID tests at [`date`] ..."
 156 echo " "
 157 
 158 umount -f $SEQID_DIR > /dev/null 2>&1
 159 mkdir -p -m 0777 $SEQID_DIR
 160 if (( $? != 0 )); then
 161         prt_info STF_UNINITIATED "ERROR: Cannot create $SEQID_DIR."
 162         exit $STF_UNINITIATED
 163 fi
 164 mount -F nfs -o rw,vers=4 $SERVER:$SHRDIR $SEQID_DIR > /dev/null 2>&1
 165 res=$?
 166 if (( $res != 0 )); then
 167         prt_info STF_UNINITIATED "ERROR: Cannot mount $SEQID_DIR on $CLIENT."
 168         internalCleanup $STF_UNINITIATED
 169 fi
 170 
 171 echo "Test NFS4ERR_BAD_SEQID" > $TESTFILE
 172 res=$?
 173 if (( $res != 0 )); then
 174         prt_info STF_UNINITIATED "ERROR: Cannot create $TESTFILE on $CLIENT."
 175         internalCleanup $STF_UNINITIATED
 176 fi
 177 
 178 chmod 666 $TESTFILE
 179 COMMAND="::nfs4_oob"
 180 
 181 case $(isainfo -k) in
 182         sparcv9 ) offset=0x16 ;;
 183         amd64 ) offset=0x14 ;;
 184         * ) offset=0x10 ;;
 185 esac
 186 
 187 # record the tried times
 188 # increase it when can't get seqid in normal operations
 189 # reset to 0 at the beginning of each case
 190 typeset -i try_times=0
 191 typeset -i max_try=5
 192 typeset addr_base
 193 typeset seqid
 194 typeset caseid
 195 typeset pid1 pid2
 196 
 197 # set up environment for the test
 198 function refresh_mnt 
 199 {
 200         [[ :$NFSGEN_DEBUG: = *:${NAME}:* || :${NFSGEN_DEBUG}: = *:all:* ]] \
 201                 && set -x
 202 
 203         typeset local_res
 204         [[ -n $pid1 ]] && kill -9 $pid1 > /dev/null 2>&1 && pid1=""
 205         [[ -n $pid2 ]] && kill -9 $pid2 > /dev/null 2>&1 && pid2=""
 206 
 207         umount -f $SEQID_DIR > /dev/null 2>&1
 208         mount -F nfs -o rw $SERVER:$SHRDIR $SEQID_DIR > /dev/null 2>&1
 209         if (( $? != 0 )); then 
 210                 echo "\tWarning: Cannot mount $SEQID_DIR on $CLIENT." \
 211                 return $STF_FAIL 
 212         else
 213                 return $STF_PASS
 214         fi
 215 }
 216 
 217 # Get the current seqid list
 218 # If error is returned, the test will terminate, only some cases info
 219 # is printed
 220 function snap_seqid 
 221 {
 222         [[ :$NFSGEN_DEBUG: = *:${NAME}:* || :${NFSGEN_DEBUG}: = *:all:* ]] \
 223                 && set -x
 224 
 225         typeset filename=$1
 226         ret=$(echo $COMMAND | mdb -kw 2>$filename)
 227         if (( $? != 0 )); then
 228                 echo "\tWarning: Cannot get seqid on $CLIENT." 
 229                 echo $ret 
 230                 cat $filename
 231                 return $STF_FAIL
 232         fi
 233         echo "$ret" | grep -v "^$" | grep -v SeqID | sort > $filename 2>/dev/null
 234         return $STF_PASS
 235 }
 236 
 237 # Get the target seqid which will be got via compare the two seqids
 238 # before and after a specified operation. If only one seqid is got,
 239 # it will be treated as the wanted
 240 # If 1 is returned, the test can be contiured, try again to get seqid
 241 # If other error is returned, the test will terminate, only some
 242 # cases info is printed
 243 function get_seqid 
 244 {
 245         [[ :$NFSGEN_DEBUG: = *:${NAME}:* || :${NFSGEN_DEBUG}: = *:all:* ]] \
 246                 && set -x
 247 
 248         typeset -i num=0
 249         # get the seqid list after the operation
 250         snap_seqid $STF_TMPDIR/$NAME.2.$$
 251         (( $? != 0 )) && return $STF_FAIL
 252 
 253         # compare two seqid file to get the seqid
 254         comm -13 $STF_TMPDIR/$NAME.1.$$ $STF_TMPDIR/$NAME.2.$$ \
 255                 >$STF_TMPDIR/$NAME.3.$$ 2>&1
 256         (( $? != 0 )) && echo "\tWarning: Comm files error on $CLIENT." \
 257                 && cat $STF_TMPDIR/$NAME.3.$$ && return 1
 258         num=$(wc -l $STF_TMPDIR/$NAME.3.$$ | awk '{print $1}')
 259         if (( num == 1 )); then
 260                 # get a uniq seqid, treat it as the desired one
 261                 addr_base=$(cat $STF_TMPDIR/$NAME.3.$$ | awk '{print $1}')
 262                 seqid=$(cat $STF_TMPDIR/$NAME.3.$$ | awk '{print $4}')
 263                 return $STF_PASS
 264         else
 265                 num=0
 266                 while read addr cred refcnt sid justcre seqinuse; do
 267                         grep $addr $STF_TMPDIR/$NAME.1.$$ > /dev/null
 268                         ret=$?
 269                         if [[ $1 == assert_b ]]; then
 270                                 (( $ret == 0 )) && num=$((num + 1)) \
 271                                         && addr_base=$addr && seqid=$sid
 272                         else
 273                                 (( $ret != 0 )) && num=$((num + 1)) \
 274                                         && addr_base=$addr && seqid=$sid
 275                         fi
 276                 done < $STF_TMPDIR/$NAME.3.$$
 277                 if (( num != 1 )); then
 278                         # did not get A seqid, try again
 279                         # it's the only branch to increase try_times
 280                         addr_base=""
 281                         seqid=""
 282                         return $STF_FAIL
 283                 fi
 284         fi
 285 }
 286 
 287 # Modify the value of seqid
 288 # If error is returned, the test will terminate, only some cases info
 289 function write_seqid 
 290 {
 291         [[ :$NFSGEN_DEBUG: = *:${NAME}:* || :${NFSGEN_DEBUG}: = *:all:* ]] \
 292                 && set -x
 293 
 294         if [[ -z $addr_base || -z $seqid ]]; then
 295                 echo "\tWarning: addr_base or seqid is null."
 296                 return $STF_FAIL
 297         fi
 298 
 299         seqid=$(($seqid+10))
 300         echo "$addr_base+$offset/w $seqid" | mdb -kw > $STF_TMPDIR/$NAME.3.$$ 2>&1
 301         local_res=$?
 302         if (( $local_res != 0 )); then
 303                 echo "\tWarning: Cannot modify seqid on $CLIENT."
 304                 cat $STF_TMPDIR/$NAME.3.$$
 305         fi
 306         return $local_res
 307 }
 308 
 309 # test case a
 310 # ----------------------------------------------------------------------
 311 # 1) client mounts server
 312 # 2) client opens a file for READ, but doesn't close it (this sends over
 313 #    OP_OPEN and generates an open owner).
 314 # 3) use mdb to modify the newly created open owner's seqid
 315 # 4) client now opens the same  file for WRITE - this should send over
 316 #    OP_OPEN and get back BAD_SEQID
 317 function bad_seqid_a 
 318 {
 319         [[ :$NFSGEN_DEBUG: = *:${NAME}:* || :${NFSGEN_DEBUG}: = *:all:* ]] && set -x
 320 
 321         try_times=0
 322         cont_flg=1
 323         ex="send over OP_OPEN and get back NFS4ERR_BAD_SEQID"
 324         echo $caseid "Client opens a file for READ and doesn't close it.\n\
 325         Modify the newly created open owner's seqid. Opens the same\n\
 326         file for WRITE, expect: " $ex
 327 
 328         MSG="incorrect status of file close in WRITE"
 329         FILE_STATUS=0
 330 
 331         while (( try_times < max_try )); do
 332                 (( try_times+=1 ))
 333                 refresh_mnt
 334                 (( $? != 0 )) && continue
 335 
 336                 # get the seqid list before the operation
 337                 snap_seqid $STF_TMPDIR/$NAME.1.$$
 338                 (( $? != 0 )) && continue
 339 
 340                 $prog -R -c -o 0 -B "0 0 0" $TESTFILE \
 341                         > $STF_TMPDIR/$NAME.outw.$$ &
 342                 pid1=$!
 343 
 344                 wait_now 200 "grep \"I am ready\" $STF_TMPDIR/$NAME.outw.$$" \
 345                   > $STF_TMPDIR/$NAME.err.$$ 2>&1
 346                 if (( $? != 0 )); then
 347                         echo "file_opeartor failed to be ready with 200 seconds"
 348                         kill $pid1
 349                         return $STF_FAIL
 350                 fi
 351 
 352                 get_seqid
 353                 (( $? == 0 )) && write_seqid && (( $? == 0 )) && \
 354                         cont_flg=0 && break
 355                 
 356                 # kill pid1 before continue
 357                 kill $pid1
 358         done
 359 
 360         if (( $cont_flg != 0 )); then
 361                 MSG="Cannot get seqid on $CLIENT after $max_try tries"
 362                 ckres snap_seqid $cont_flg $STF_PASS $MSG
 363                 kill $pid1
 364                 return $STF_FAIL
 365         else
 366                 $prog -W -c -o 7 -B "0 0 -1" $TESTFILE
 367                 ckres NFS4ERR_BAD_SEQID $? $FILE_STATUS $MSG
 368                 if (( $? != 0 )); then
 369                         kill $pid1
 370                         echo "check close operation failed"
 371                         return $STF_FAIL
 372                 fi
 373                 kill $pid1 > /dev/null 2>&1
 374                 return $STF_PASS
 375         fi
 376 }
 377 
 378 # test case b
 379 # ----------------------------------------------------------------------
 380 # 1) client mounts server
 381 # 2) client opens a file for READ, but doesn't close it (this sends over
 382 #    OP_OPEN and generates an open owner).
 383 # 3) client opens the same file for WRITE, but doesn't close it (this
 384 #    sends over another OP_OPEN).
 385 # 4) use mdb to modify the newly created open owner's seqid
 386 # 5) client close its file descriptor for WRITE - this should send over
 387 #    OP_OPEN_DOWNGRADE and get back BAD_SEQID
 388 function bad_seqid_b 
 389 {
 390         [[ :$NFSGEN_DEBUG: = *:${NAME}:* || :${NFSGEN_DEBUG}: = *:all:* ]] && set -x
 391         
 392         try_times=0
 393         cont_flg=1
 394         ex="send over OP_OPEN_DOWNGRADE and get back NFS4ERR_BAD_SEQID"
 395         echo $caseid "Client opens a file for READ and doesn't close it.\n\
 396         Client opens the same file for WRITE and doesn't close it.\n\
 397         Modify the newly created open owner's seqid, close its file\n\
 398         descriptor for WRITE, expect: " $ex
 399 
 400         MSG="incorrect status of file close in WRITE"
 401         FILE_STATUS=5   # I/O error
 402 
 403         while (( try_times < max_try )); do
 404                 (( try_times+=1 ))
 405                 refresh_mnt
 406                 (( $? != 0 )) && continue
 407 
 408                 $prog -R -c -o 0 -B "0 0 0" $TESTFILE \
 409                         > $STF_TMPDIR/$NAME.outR.$$ 2>&1 & 
 410                 pid1=$!
 411 
 412                 wait_now 200 "grep \"I am ready\" $STF_TMPDIR/$NAME.outR.$$" \
 413                   > $STF_TMPDIR/$NAME.err.$$ 2>&1
 414                 if (( $? != 0 )); then
 415                         echo "file_opeartor failed to be ready with 200 seconds"
 416                         kill $pid1
 417                         return $STF_FAIL
 418                 fi
 419 
 420                 # get the seqid list before the operation
 421                 snap_seqid $STF_TMPDIR/$NAME.1.$$
 422                 (( $? != 0 )) && continue
 423 
 424                 $prog -W -c -o 4 -B "0 0 0" $TESTFILE \
 425                         > $STF_TMPDIR/$NAME.outW.$$ 2>&1 & 
 426                 pid2=$!
 427 
 428                 wait_now 200 "grep \"I am ready\" $STF_TMPDIR/$NAME.outW.$$" \
 429                   > $STF_TMPDIR/$NAME.err.$$ 2>&1
 430                 if (( $? != 0 )); then
 431                         echo "file_opeartor failed to be ready with 200 seconds"
 432                         kill $pid1
 433                         kill $pid2
 434                         return $STF_FAIL
 435                 fi
 436 
 437                 get_seqid assert_b
 438                 (( $? == 0 )) && write_seqid && (( $? == 0 )) && \
 439                         cont_flg=0 && break
 440         
 441                 # kill pid1 and pid2 before continue
 442                 kill $pid1
 443                 kill $pid2
 444         done
 445 
 446         if (( $cont_flg != 0 )); then
 447                 MSG="Cannot get seqid on $CLIENT after $max_try tries"
 448                 ckres snap_seqid $cont_flg $STF_PASS $MSG
 449                 return $STF_FAIL
 450         else
 451                 kill -16 $pid2 > /dev/null 2>&1
 452                 wait $pid2 > /dev/null 2>&1
 453                 ckres NFS4ERR_BAD_SEQID $? $FILE_STATUS $MSG
 454                 if (( $? != 0 )); then
 455                         kill $pid1
 456                         echo "check pid2=$pid2 failed"
 457                         return $STF_FAIL
 458                 fi
 459                 kill -16 $pid1 > /dev/null 2>&1
 460                 wait $pid1 > /dev/null 2>&1
 461                 if (( $? != 0 )); then
 462                         echo "failed to wait pid1=$pid1"
 463                         return $STF_FAIL
 464                 fi
 465         fi
 466 
 467         return $STF_PASS
 468 }
 469 
 470 # test case c
 471 # ----------------------------------------------------------------------
 472 # 1) client mounts server
 473 # 2) client opens a file for READ, but doesn't close it (this sends over
 474 #    OP_OPEN and generates an open owner).
 475 # 3) use mdb to modify the newly created open owner's seqid
 476 # 4) client closes the file - this should send over OP_CLOSE and get back
 477 #    BAD_SEQID
 478 function bad_seqid_c
 479 {
 480         [[ :$NFSGEN_DEBUG: = *:${NAME}:* || :${NFSGEN_DEBUG}: = *:all:* ]] && set -x
 481         try_times=0
 482         cont_flg=1
 483         ex="send over OP_CLOSE and get back NFS4ERR_BAD_SEQID"
 484         echo $caseid "Client opens a file for READ and doesn't close it. \
 485         Modify the newly created open owner's seqid and close \
 486         the file, expect: " $ex
 487 
 488         MSG="incorrect status of close file in READ"
 489         FILE_STATUS=5   # I/O error
 490 
 491         while (( try_times < max_try )); do
 492                 (( try_times+=1 ))
 493                 refresh_mnt
 494                 (( $? != 0 )) && continue
 495 
 496                 # get the seqid list before the operation
 497                 snap_seqid $STF_TMPDIR/$NAME.1.$$
 498                 (( $? != 0 )) && continue
 499 
 500                 $prog -R -c -o 0 -B "0 0 0" $TESTFILE > \
 501                         $STF_TMPDIR/$NAME.outR.$$ 2>&1 &
 502                 pid1=$!
 503 
 504                 wait_now 200 "grep \"I am ready\" $STF_TMPDIR/$NAME.outR.$$" \
 505                   > $STF_TMPDIR/$NAME.err.$$ 2>&1
 506                 if (( $? != 0 )); then
 507                         echo "file_opeartor failed to be ready with 200 seconds"
 508                         kill $pid1
 509                         return $STF_FAIL
 510                 fi
 511 
 512                 get_seqid 
 513                 (( $? == 0 )) && write_seqid && (( $? == 0 )) && \
 514                         cont_flg=0 && break
 515 
 516                 # kill pid1 before continue
 517                 kill $pid1
 518         done
 519 
 520         if (( $cont_flg != 0 )); then
 521                 MSG="Cannot get seqid on $CLIENT after $max_try tries"
 522                 ckres snap_seqid $cont_flg $STF_PASS $MSG
 523                 kill $pid1
 524                 return $STF_FAIL
 525         else
 526                 kill -16 $pid1 > /dev/null 2>&1
 527                 wait $pid1 > /dev/null 2>&1
 528                 ckres NFS4ERR_BAD_SEQID $? $FILE_STATUS $MSG
 529                 if (( $? != 0 )); then
 530                         echo "failed to check close operation with pid=$pid1"
 531                         return $STF_FAIL
 532                 fi
 533         fi
 534 
 535         return $STF_PASS
 536 }
 537 
 538 bad_seqid_a
 539 retcode=$?
 540 
 541 bad_seqid_b
 542 retcode=$(($retcode+$?))
 543 
 544 bad_seqid_c
 545 retcode=$(($retcode+$?))
 546 
 547 (( $retcode == $STF_PASS )) && internalCleanup $STF_PASS || internalCleanup $STF_FAIL