Whamcloud - gitweb
53a2ca00604741453ea32884a5a9cbcd23042bcd
[fs/lustre-release.git] / lustre / tests / lfsck.sh
1 #!/bin/bash
2 #
3 # test e2fsck and lfsck to detect and fix filesystem corruption
4 #
5 #set -vx
6 set -e
7
8 LUSTRE=${LUSTRE:-$(cd $(dirname $0)/..; echo $PWD)}
9 . $LUSTRE/tests/test-framework.sh
10 init_test_env $@
11 . ${CONFIG:=$LUSTRE/tests/cfg/$NAME.sh}
12
13 NUMFILES=${NUMFILES:-10}
14 NUMDIRS=${NUMDIRS:-4}
15 OSTIDX=${OSTIDX:-0} # the OST index in LOV
16 OBJGRP=${OBJGRP:-0} # the OST object group
17
18 [ -d "$SHARED_DIRECTORY" ] || \
19     { skip "SHARED_DIRECTORY should be specified with a shared directory \
20 which can be accessable on all of the nodes" && exit 0; }
21
22 which getfattr > /dev/null 2>&1 || { skip "could not find getfattr" && exit 0; }
23 which setfattr > /dev/null 2>&1 || { skip "could not find setfattr" && exit 0; }
24
25 MOUNT_2=""
26 check_and_setup_lustre
27
28 assert_DIR
29
30 SAMPLE_FILE=$TMP/$(basename $0 .sh).junk
31 dd if=/dev/urandom of=$SAMPLE_FILE bs=1M count=1
32
33 # Create some dirs and files on the filesystem.
34 create_files_sub() {
35     local test_dir=$1
36     local num_dirs=$2
37     local file_name=$3
38     local first_num=$4
39     local last_num=$5
40     local d e f
41
42     for d in $(seq -f d%g $first_num $last_num); do
43         echo "creating files in $test_dir/$d"
44         for e in $(seq -f d%g $num_dirs); do
45             mkdir -p $test_dir/$d/$e || error "mkdir $test_dir/$d/$e failed"
46             for f in $(seq -f test%g $num_dirs); do
47                 cp $file_name $test_dir/$d/$e/$f || \
48                     error "cp $file_name $test_dir/$d/$e/$f failed"
49             done
50         done
51     done
52 }
53
54 create_files() {
55     local test_dir=$1
56     local num_dirs=$2
57     local num_files=$3
58     local f
59
60     # create some files on the filesystem
61     local first_num=1
62     local last_num=$num_dirs
63     create_files_sub $test_dir $num_dirs /etc/fstab $first_num $last_num
64
65     # create files to be modified
66     for f in $(seq -f $test_dir/testfile.%g $((num_files * 3))); do
67         echo "creating $f"
68         cp $SAMPLE_FILE $f || error "cp $SAMPLE_FILE $f failed"
69     done
70
71     # create some more files
72     first_num=$((num_dirs * 2 + 1))
73     last_num=$((num_dirs * 2 + 3))
74     create_files_sub $test_dir $num_dirs /etc/hosts $first_num $last_num
75
76     # these should NOT be taken as duplicates
77     for f in $(seq -f $test_dir/d$last_num/linkfile.%g $num_files); do
78         echo "linking files in $test_dir/d$last_num"
79         cp /etc/hosts $f || error "cp /etc/hosts $f failed"
80         ln $f $f.link || error "ln $f $f.link failed"
81     done
82 }
83
84 # Get the objids for files on the OST (given the OST index and object group).
85 get_objects() {
86     local obdidx=$1
87     shift
88     local group=$1
89     shift
90     local ost_files="$@"
91     local ost_objids
92     ost_objids=$($LFS getstripe $ost_files | \
93                 awk '{if ($1 == '$obdidx' && $4 == '$group') print $2 }')
94     echo $ost_objids
95 }
96
97 # Get the OST nodet name (given the OST index).
98 get_ost_node() {
99     local obdidx=$1
100     local ost_uuid
101     local ost_node
102     local node
103
104     ost_uuid=$($LFS osts | grep "^$obdidx: " | cut -d' ' -f2 | head -n1)
105
106     for node in $(osts_nodes); do
107         do_node $node "lctl get_param -n obdfilter.*.uuid" | grep -q $ost_uuid
108         [ ${PIPESTATUS[1]} -eq 0 ] && ost_node=$node && break
109     done
110     [ -z "$ost_node" ] && \
111         echo "failed to find the OST with index $obdidx" && return 1
112     echo $ost_node
113 }
114
115 # Get the OST target device (given the OST facet name and OST index).
116 get_ost_dev() {
117     local node=$1
118     local obdidx=$2
119     local ost_name
120     local ost_dev
121
122     ost_name=$($LFS osts | grep "^$obdidx: " | cut -d' ' -f2 | \
123                 head -n1 | sed -e 's/_UUID$//')
124
125     ost_dev=$(do_node $node "lctl get_param -n obdfilter.$ost_name.mntdev")
126     [ ${PIPESTATUS[0]} -ne 0 ] && \
127         echo "failed to find the OST device with index $obdidx on $facet" && \
128         return 1
129
130     if [[ $ost_dev = *loop* ]]; then
131         ost_dev=$(do_node $node "losetup $ost_dev" | \
132                 sed -e "s/.*(//" -e "s/).*//")
133     fi
134
135     echo $ost_dev
136 }
137
138 # Get the file names to be duplicated or removed on the MDS.
139 get_files() {
140     local flavor=$1
141     local test_dir=$2
142     local num_files=$3
143     local first last
144     local test_file
145
146     case $flavor in
147     dup)
148         first=$((num_files + 1))
149         last=$((num_files * 2))
150         ;;
151     remove)
152         first=$((num_files * 2 + 1))
153         last=$((num_files * 3))
154         ;;
155     *) echo "get_files(): invalid flavor" && return 1 ;;
156     esac
157
158     local files=""
159     local f 
160     for f in $(seq -f testfile.%g $first $last); do
161         test_file=$test_dir/$f
162         files="$files $test_file"
163     done
164     files=$(echo $files | sed "s#$DIR/##g")
165     echo $files
166 }
167
168 # Remove objects associated with files.
169 remove_objects() {
170     local node=$1
171     shift
172     local ostdev=$1
173     shift
174     local group=$1
175     shift
176     local objids="$@"
177     local tmp
178     local i
179     local rc
180
181     echo "removing objects from $ostdev on $facet: $objids"
182     tmp=$(mktemp $SHARED_DIRECTORY/debugfs.XXXXXXXXXX)
183     for i in $objids; do
184         echo "rm O/$group/d$((i % 32))/$i" >> $tmp
185     done
186
187     do_node $node "$DEBUGFS -w -f $tmp $ostdev"
188     rc=${PIPESTATUS[0]}
189     rm -f $tmp
190
191     return $rc
192 }
193
194 # Remove files from MDS.
195 remove_files() {
196     do_rpc_nodes $(facet_host $1) remove_mdt_files $@
197 }
198
199 # Create EAs on files so objects are referenced from different files.
200 duplicate_files() {
201     do_rpc_nodes $(facet_host $1) duplicate_mdt_files $@
202 }
203
204 #********************************* Main Flow **********************************#
205
206 init_logging
207
208 # get the server target devices
209 get_svr_devs
210
211 if [ "$SKIP_LFSCK" = "no" ] && is_empty_fs $MOUNT; then
212     # create test directory
213     TESTDIR=$DIR/d0.$TESTSUITE
214     mkdir -p $TESTDIR || error "mkdir $TESTDIR failed"
215
216     # create some dirs and files on the filesystem
217     create_files $TESTDIR $NUMDIRS $NUMFILES
218
219     # get the objids for files in group $OBJGRP on the OST with index $OSTIDX
220     OST_REMOVE=$(get_objects $OSTIDX $OBJGRP \
221                 $(seq -f $TESTDIR/testfile.%g $NUMFILES))
222
223     # get the node name and target device for the OST with index $OSTIDX
224     OSTNODE=$(get_ost_node $OSTIDX) || error "get_ost_node by index $OSTIDX failed"
225     OSTDEV=$(get_ost_dev $OSTNODE $OSTIDX) || \
226         error "get_ost_dev $OSTNODE $OSTIDX failed"
227
228     # get the file names to be duplicated on the MDS
229     MDS_DUPE=$(get_files dup $TESTDIR $NUMFILES) || error "$MDS_DUPE"
230     # get the file names to be removed from the MDS
231     MDS_REMOVE=$(get_files remove $TESTDIR $NUMFILES) || error "$MDS_REMOVE"
232
233     stopall -f || error "cleanupall failed"
234
235     # remove objects associated with files in group $OBJGRP
236     # on the OST with index $OSTIDX
237     remove_objects $OSTNODE $OSTDEV $OBJGRP $OST_REMOVE || \
238         error "removing objects failed"
239
240     # remove files from MDS
241     remove_files $SINGLEMDS $MDTDEV $MDS_REMOVE || error "removing files failed"
242
243     # create EAs on files so objects are referenced from different files
244     duplicate_files $SINGLEMDS $MDTDEV $MDS_DUPE || \
245         error "duplicating files failed"
246     FSCK_MAX_ERR=1   # file system errors corrected
247 else    # I_MOUNTED=no
248     FSCK_MAX_ERR=4   # file system errors left uncorrected
249 fi
250
251 # Test 1a - check and repair the filesystem
252 # lfsck will return 1 if the filesystem had errors fixed
253 # run e2fsck to generate databases used for lfsck
254 generate_db
255 if [ "$SKIP_LFSCK" != "no" ]; then
256     echo "skip lfsck"
257 else
258     # remount filesystem
259     REFORMAT=""
260     check_and_setup_lustre
261
262     # run lfsck
263     rc=0
264     run_lfsck || rc=$?
265     if [ $rc -eq 0 ]; then
266         echo "clean after the first check"
267     else
268         # run e2fsck again to generate databases used for lfsck
269         generate_db
270
271         # run lfsck again
272         rc=0
273         run_lfsck || rc=$?
274         if [ $rc -eq 0 ]; then
275             echo "clean after the second check"
276         else
277             error "lfsck test 2 - finished with rc=$rc"
278         fi
279     fi
280 fi
281
282 equals_msg $(basename $0): test complete, cleaning up
283
284 LFSCK_ALWAYS=no
285 check_and_cleanup_lustre
286 [ -f "$TESTSUITELOG" ] && cat $TESTSUITELOG && \
287     grep -q FAIL $TESTSUITELOG && exit 1 || true
288
289 echo "$0: completed"