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