xfs: test that the needsrepair feature works as advertised
[xfstests-dev.git] / crash / xfscrash
1 #!/bin/sh
2 #
3 # Copyright (c) 2000-2001 Silicon Graphics, Inc.  All Rights Reserved.
4 #
5 # xfscrash - control the XFS crash tests
6 #
7
8   #######################
9 ### configuration stuff ########################################################
10   #######################
11
12 # remount, repair or corrupt
13 MODE=remount
14 # where to find xfscrash
15 XFSCRASH=/xfscrash
16 # put log files here
17 LOG=$XFSCRASH
18 # put output to these places
19 OUTPUT="$LOG/xfscrash.log /dev/tty1 /dev/console"
20 # awk...
21 AWK_PROG=gawk
22 # clear FS if >= this percent full at start of run. 100 is a good
23 # number - only used on corrupt test so far
24 FULL_LIMIT=80
25   
26 case `hostname -s`
27 in
28     leesa)
29         # mount test partition here
30         TEST_MNT=/mnt/arch0
31         # build test partition here
32         TEST_DEV=/dev/hda6
33         # backup test partition to here (or empty)
34         BACKUP_DEV=/dev/hda8
35         # backup block size for dd
36         BACKUP_BS=1024k
37         # base stress time
38         STRESS_TIME=60
39         # stress random time
40         STRESS_RANDOM=60
41         ;;
42     lumpy)
43         # mount test partition here
44         TEST_MNT=/mnt/scratch_0
45         # build test partition here
46         TEST_DEV=/dev/sdc5
47         # backup test partition to here (or empty)
48         BACKUP_DEV=  ;#/dev/sdc6
49         # backup block size for dd
50         BACKUP_BS=10240k
51         # base stress time
52         STRESS_TIME=360
53         # stress random time
54         STRESS_RANDOM=360
55         ;;
56     *)
57         echo "!!! no configuration data for host `hostname -s`"
58         exit 1
59         ;;
60 esac
61
62 # avoid stress
63
64 AVOID="-f resvsp=0 -f unresvsp=0"
65
66 # DIY stress command
67 STRESS="/usr/local/bin/fsstress -d $TEST_MNT/stress -n 10000000 -p 1 $AVOID"
68 #STRESS="/usr/local/bin/randholes -l 10000000 -c 100000 -b 512 $TEST_MNT/stress/holes"
69
70 # stress command for the corrupt test
71 CORRUPT_STRESS="/usr/local/bin/fsstress -d $TEST_MNT/stress -n 10000 -p 1 $AVOID"
72
73 ###########################################################################
74
75 reboot=-1
76
77 _log()
78 {
79     tee -a $OUTPUT > /dev/null
80 }
81
82 _echo()
83 {
84     echo "$*" | _log
85 }
86
87 _mount()
88 {
89     _echo "   *** Mounting $TEST_DEV on $TEST_MNT"
90     if ! mount -t xfs $TEST_DEV $TEST_MNT
91     then
92         _echo "   !!! unable to mount"
93         exit 1
94     fi
95 }
96
97 _unmount()
98 {
99     _echo "   *** Unmounting $TEST_DEV"
100     if ! umount $TEST_DEV &> /dev/null
101     then
102         _echo "   !!! unable to unmount"
103         exit 1
104     fi
105 }
106
107 _check()
108 {
109     expect=$1
110     fail=0
111     
112     if [ $expect -eq 0 ]
113     then
114         _echo "   *** Checking FS (expecting clean fs)"
115     else
116         _echo "   *** Checking FS (expecting dirty fs)"
117     fi
118     
119     
120     if [ $expect -eq 0 ]
121     then
122         _echo "      *** xfs_check ($LOG/check_clean.out)"   
123         _xfs_check $TEST_DEV &> $LOG/check_clean.out || fail=1
124         [ -s /tmp/xfs_check_clean.out ] && fail=1
125     else
126         _echo "      *** xfs_check ($LOG/check_dirty.out)"   
127         _xfs_check $TEST_DEV &> $LOG/check_dirty.out || fail=1
128     fi
129     
130     if [ $fail -eq 0 -a $expect -eq 0 ]
131     then
132         _echo "      *** xfs_repair -n ($LOG/repair_clean.out)"   
133         xfs_repair -n $TEST_DEV &> $LOG/repair_clean.out || fail=1
134     fi
135     
136     if [ $fail -eq 0 ]
137     then
138         _echo "         *** FS checks ok"
139     else
140         if [ $expect -eq 0 ]
141         then
142             _echo "         !!! FS check failed - inconsistent FS"
143             _echo "         !!! (see $LOG/*.out for details)"
144             exit 1
145         else
146             _echo "         *** inconsistent fs (as expected)"
147         fi
148     fi
149 }
150
151 _check_core()
152 {        
153     if [ -e core ]
154     then
155         _echo "   !!! core file found!"
156         exit 1
157     fi
158 }
159
160 _repair()
161 {
162     rm -f core
163     _echo "   *** repair"
164     _echo "      *** repair pass 1 (RO)"
165     xfs_repair -n $TEST_DEV &> $LOG/repair_1.out \
166         && _echo "         !!! no errors found (eh?)" \
167         || _echo "         *** errors found (expected)"
168         
169     _check_core
170         
171     _echo "      *** repair pass 2 (RW)"
172     
173     if xfs_repair $TEST_DEV &> $LOG/repair_2.out
174     then
175         _echo "         *** FS checks ok (now)"
176     else
177         _echo "         !!! xfs_repair returned error code"
178         _echo "         !!! (see $LOG/repair_*.out for details)"
179         exit 1
180     fi
181         
182     _check_core
183         
184     _echo "      *** repair pass 3 (RO)"
185     if xfs_repair -n $TEST_DEV &> $LOG/repair_3.out
186     then
187         _echo "         *** FS checks ok"
188     else
189         _echo "         !!! errors found after repair (unexpected)"
190         _echo "         !!! (see $LOG/repair_*.out for details)"
191         exit 1
192     fi
193
194     _check_core
195 }
196
197 _cleanup()
198 {
199     rm -f $XFSCRASH/counter $XFSCRASH/start $XFSCRASH/stop $XFSCRASH/active 
200     
201     if [ $reboot != -1 ]
202     then
203         kill $reboot
204     fi
205     
206 }
207
208 _random()
209 {
210     od -tu -N 4 /dev/random | gawk -v v=$1 'NR==1 { print $2 % v }'
211 }
212
213 _backup()
214 {
215     if [ $count -ne 1 -a "$BACKUP_DEV" != "" ]
216     then
217         _echo "   *** Backing up $TEST_DEV to $BACKUP_DEV"
218         if ! dd if=$TEST_DEV of=$BACKUP_DEV bs=$BACKUP_BS &> $LOG/dd.out
219         then
220             _echo "   !!! unable to backup fs"
221             _echo "   !!! (see $LOG/dd.out)"
222             exit 1
223         fi
224     else
225         _echo "   *** skipping back up step"
226     fi
227 }
228
229 _logprint()
230 {
231     _echo "   *** dumping log to $LOG/logprint.out"
232     rm -f core
233     xfs_logprint $TEST_DEV &> $LOG/logprint.out
234     if [ -e core ]
235     then
236         _echo "      !!! xfs_logprint dumped core"
237         echo "" >> $LOG/logprint.out
238         echo "*** CORE DUMPED ***" >> $LOG/logprint.out
239         echo "" >> $LOG/logprint.out
240     fi
241     
242     _echo "   *** dumping log (-t -i) to $LOG/logprint_inode.out"
243     
244     rm -f core
245     xfs_logprint -t -i $TEST_DEV &> $LOG/logprint_inode.out
246     if [ -e core ]
247     then
248         _echo "      !!! xfs_logprint dumped core"
249         echo "" >> $LOG/logprint_inode.out
250         echo "*** CORE DUMPED ***" >> $LOG/logprint_inode.out
251         echo "" >> $LOG/logprint_inode.out
252     fi
253     
254     _echo "   *** dumping log (-t -b) to $LOG/logprint_buf.out"
255     
256     rm -f core
257     xfs_logprint -t -b $TEST_DEV &> $LOG/logprint_buf.out
258     if [ -e core ]
259     then
260         _echo "      !!! xfs_logprint dumped core"
261         echo "" >> $LOG/logprint_buf.out
262         echo "*** CORE DUMPED ***" >> $LOG/logprint_buf.out
263         echo "" >> $LOG/logprint_buf.out
264     fi
265 }
266 #
267 # _df_device : get an IRIX style df line for a given device 
268 #
269 #       - returns "" if not mounted
270 #       - returns fs type in field two (ala IRIX)
271 #       - joins line together if split by fancy df formatting
272 #       - strips header etc
273 #
274
275 _df_device()
276 {
277     if [ $# -ne 1 ]
278     then
279         echo "Usage: _df_device device" >&2
280         exit 1
281     fi
282     
283     df -T 2> /dev/null | $AWK_PROG -v what=$1 '
284         match($1,what) && NF==1 { 
285             v=$1
286             getline
287             print v, $0
288             exit
289         }
290         match($1,what) {
291             print
292             exit
293         }
294     '
295 }
296
297 #
298 # _df_dir : get an IRIX style df line for device where a directory resides
299 #
300 #       - returns fs type in field two (ala IRIX)
301 #       - joins line together if split by fancy df formatting
302 #       - strips header etc
303 #
304
305 _df_dir()
306 {
307     if [ $# -ne 1 ]
308     then
309         echo "Usage: _df_dir device" >&2
310         exit 1
311     fi
312     
313     df -T $1 2> /dev/null | $AWK_PROG -v what=$1 '
314         NR == 2 && NF==1 { 
315             v=$1
316             getline 
317             print v, $0;
318             exit 0
319         }
320         NR == 2 {
321             print;
322             exit 0
323         }
324         {}
325     '
326     # otherwise, nada
327 }
328
329 # return percentage used disk space for mounted device
330
331 _used()
332 {
333     if [ $# -ne 1 ]
334     then
335         echo "Usage: _used device" >&2
336         exit 1
337     fi
338     
339     _df_device $1 | $AWK_PROG '{ sub("%", "") ; print $6 }'
340 }
341
342 _check_free()
343 {
344      used=`_used $TEST_DEV`
345
346      if [ $used -ge $FULL_LIMIT ]    
347      then
348          _echo "      *** $used % used on $TEST_DEV - deleting files"
349          rm -rf $TEST_MNT/stress
350      fi
351 }       
352
353 # loop, stressing, unounting and checking
354 # no (expected) rebooting...
355 _corrupt()
356 {
357     count=0
358     
359     # don't want to restart if we reboot...
360     _cleanup
361     
362     while true
363     do
364
365         if [ -e $XFSCRASH/stop ]
366         then
367             _echo "### XFS Crash stopped "
368             exit 0
369         fi
370
371         _echo "*** run $count"
372         let "count = count + 1"
373         
374         _check 0
375         _mount
376         
377         _check_free
378         
379         $CORRUPT_STRESS | _log
380         
381         _unmount        
382     done
383 }
384
385 ###########################################################################
386     
387 _echo ""
388 _echo ""
389 echo "XFSCRASH [output to $OUTPUT]"
390 _echo ""
391
392 if [ "$1" = "start" ]
393 then
394     touch $XFSCRASH/start
395 fi
396
397 if [ "$1" = "stop" ]
398 then
399     touch $XFSCRASH/stop
400 fi
401
402
403 trap "_cleanup; exit \$status" 0 1 2 3 15
404
405
406 if [ -e $XFSCRASH/stop ]
407 then
408     _echo "### XFS Crash stopped "
409     exit 0
410 fi
411
412 if [ -e $XFSCRASH/start ]
413 then
414     _echo "### XFS Crash started "
415     _cleanup
416     rm -f $LOG/*.out $LOG/*.log core
417     
418     touch $XFSCRASH/active
419
420     _echo "   *** Building fresh XFS FS"
421     umount $TEST_DEV &> /dev/null
422     if ! mkfs -t xfs -f $TEST_DEV &> $LOG/mkfs.out
423     then
424         _echo "   !!! unable to mkfs"
425         _echo "   !!! (see $LOG/mkfs.out)"
426         exit 1
427     fi
428 fi
429
430 if [ ! -e $XFSCRASH/active ]
431 then
432     _echo "### XFS Crash inactive "
433     exit 0
434 fi
435
436
437 if [ -r $XFSCRASH/counter ]
438 then
439     count=`cat $XFSCRASH/counter`
440 else
441     count=0
442 fi
443 _echo "### Crash test run $count (mode=$MODE, log=$LOG/{*.out,*.log})"
444
445 let "count = count +1"
446 echo $count > $XFSCRASH/counter
447
448 # real test starts here
449
450 _echo "   *** Checking for R/O root"
451 if ! mount | grep "on / type" | grep -q "(ro)"
452 then
453     _echo "   !!! root not mounted readonly"
454     exit 1
455 fi
456
457 _echo "   *** Loading XFS modules"
458 if ! modprobe xfs
459 then
460     _echo "   !!! unable to modprobe xfs"
461     exit 1
462 fi
463
464 _echo "   *** Unmounting $TEST_DEV"
465 umount $TEST_DEV &> /dev/null
466
467 _logprint
468 if [ $MODE != "corrupt" ]
469 then
470     _backup
471 fi
472
473 case $MODE
474 in
475     remount)
476         _check 1 # expect errors
477         _mount
478         _unmount
479         ;;
480     repair)
481         _repair
482         ;;
483     corrupt)
484         _corrupt
485         exit 0
486         ;;
487     *)
488         _echo "xfscrash: MODE must be remount or repair"
489         exit 1
490         ;;
491 esac
492
493 _check 0 # don't expect errors
494 _mount
495
496 _echo "   *** Cleaning XFS FS"
497 if ! rm -rf $TEST_MNT/stress $TEST_MNT/lost+found &> $LOG/clean.out
498 then
499     _echo "   !!! unable to clean XFS FS"
500     _echo "   !!! (see $LOG/clean.out)"
501     exit 1
502 fi
503
504 _echo "   *** Making stress directory"
505 if ! mkdir $TEST_MNT/stress
506 then
507     _echo "   !!! unable to mkdir stress"
508     exit 1
509 fi
510
511 let "bang = STRESS_TIME + `_random $STRESS_RANDOM`"
512
513 _echo "   *** Preparing random reboot (in $bang seconds)"
514 (
515     sleep $bang
516     _echo "      *** BANG ****"
517     reboot -fn
518 ) &
519 reboot=$!
520
521 _echo "   *** Causing stress & waiting for the inevitable"
522 $STRESS | _log
523
524 exit 0