Whamcloud - gitweb
tests: create random filesystem, corrupt, e2fsck
[tools/e2fsprogs.git] / tests / f_random_corruption / script
1 # This is to make sure that if this test fails other tests can still be run
2 # instead of doing an exit. We break before the end of the loop.
3 export LOOP_COUNT=${LOOP_COUNT:-1}
4 export COUNT=1
5 test_orig=$test_name
6
7 [ "$F_RANDOM_CORRUPTION" = "skip" ] &&
8         echo "$test_name: $test_description: skipped" && exit 0
9
10 log()
11 {
12         [ "$VERBOSE" ] && echo "$*"
13         echo "$*" >> $OUT
14 }
15
16 error()
17 {
18         log "$*"
19         echo "$*" >> $FAILED
20 }
21
22 get_random_location()
23 {
24         total=$1
25
26         tmp=$(((RANDOM * 32768) % total))
27
28         # Try and have more corruption in metadata at the start of the
29         # filesystem.
30         if ((tmp % 3 == 0 || tmp % 5 == 0 || tmp % 7 == 0)); then
31                 tmp=$((tmp % 32768))
32         fi
33
34         echo $tmp
35 }
36
37 make_fs_dirty()
38 {
39         from=$(get_random_location $NUM_BLKS)
40
41         # Number of blocks to write garbage into should be within fs and should
42         # not be too many.
43         num_blks_to_dirty=$((RANDOM % $1))
44
45         # write garbage into the selected blocks
46         [ ! -c /dev/urandom ] && return
47         log "writing ${num_blks_to_dirty}kB random garbage at offset ${from}kB"
48         dd if=/dev/urandom of=$TMPFILE bs=1kB seek=$from conv=notrunc \
49                 count=$num_blks_to_dirty >> $OUT 2>&1
50 }
51
52 unset_vars()
53 {
54         unset DATE ARCHIVE FS_TYPE SIZE BLK_SIZE MKFS_OPTS MOUNT_OPTS
55         unset E2FSCK FIRST_FSCK_OPTS SECOND_FSCK_OPTS OUT FAILED OKFILE
56 }
57
58 cleanup()
59 {
60         local MSG="$*"
61
62         umount -f $MNTPT > /dev/null 2>&1 | tee -a $OUT
63         [ -d $MNTPT ] && rmdir $MNTPT
64         [ -z "$MSG" -a -s "$FAILED" ] && MSG=$(cat $FAILED)
65         if [ "$MSG" ]; then
66                 cp $OUT $OUT.$DATE
67                 error "$MSG"
68                 echo ""
69                 echo "error: *** This appears to be a bug in e2fsprogs ***"
70                 echo "Please contact linux-ext4@vger.kernel.org for further"
71                 echo "assistance.  Include $OUT.$DATE, and save $ARCHIVE"
72                 echo "locally for reference."
73         else
74                 rm -f $TMPFILE $ARCHIVE
75         fi
76         unset_vars
77
78         [ -n "$MSG" ] && break
79 }
80
81 while [ $COUNT -le $LOOP_COUNT ]; do
82 # choose block and inode sizes randomly
83 BLK_SIZES=(1024 2048 4096)
84 INODE_SIZES=(128 256 512 1024)
85
86 SEED=$(head -1 /dev/urandom | od -N 1 | awk '{ print $2 }')
87 RANDOM=$SEED
88
89 [ -f "$TMPFILE" ] && rm -f $TMPFILE
90 TMPFILE=$test_name.tmp
91 DATE=$(date '+%Y%m%d%H%M%S')
92 ARCHIVE=$TMPFILE.$DATE
93 SIZE=${SIZE:-$((256000 + RANDOM * RANDOM / 1024)) } # in kB
94 BLK_SIZE=${BLK_SIZES[((RANDOM % ${#BLK_SIZES[*]}))]}
95 INODE_SIZE=${INODE_SIZES[((RANDOM % ${#INODE_SIZES[*]}))]}
96 DEF_FEATURES="sparse_super,filetype,dir_index"
97 FEATURES=${FEATURES:-$DEF_FEATURES}
98 #FSTYPE=$FSTYPE # set below, or from environment
99
100 # Do you want to try and mount the filesystem?
101 MOUNT_AFTER_CORRUPTION=${MOUNT_AFTER_CORRUPTION:-"no"}
102 # Do you want to remove the files from the mounted filesystem?
103 # Ideally use it only in test environment.
104 REMOVE_FILES=${REMOVE_FILES:-"no"}
105
106 # In KB
107 CORRUPTION_SIZE=${CORRUPTION_SIZE:-64}
108 CORRUPTION_ITERATIONS=${CORRUPTION_ITERATIONS:-5}
109 # END OF ENVIRONMENT SPECIFIED CONFIGURATION
110
111 MOUNT_OPTS="-o loop"
112 MNTPT=$(mktemp -d)
113 OUT=$test_name.log
114 FAILED=$test_name.failed
115 OKFILE=$test_name.ok
116
117 MKFS=../misc/mke2fs
118 E2FSCK=../e2fsck/e2fsck
119 FIRST_FSCK_OPTS="-fyv"
120 SECOND_FSCK_OPTS="-fyv"
121
122 # Lets check if the image can fit in the current filesystem.
123 NUM_BLKS=$(((SIZE * 1024) / BLK_SIZE))
124 BASE_DIR=$(dirname $TMPFILE)
125 BASE_AVAIL_BLOCKS=$(df -P -k $BASE_DIR  | awk '/%/ { print $4 }')
126
127 if [ $BASE_AVAIL_BLOCKS -lt $((NUM_BLKS * (BLK_SIZE / 1024))) ]; then
128         NUM_BLKS=$((BASE_AVAIL_BLOCKS - RANDOM))
129         SIZE=$((NUM_BLKS * BLK_SIZE))
130         log "$BASE_DIR too small, shrunk image size to $SIZE"
131 fi
132
133 # Lets have a journal more times than not.
134 HAVE_JOURNAL=$((RANDOM % 12))
135 if [ -z "$FS_TYPE" -a $HAVE_JOURNAL -lt 10 ]; then
136         modprobe ext4 2> /dev/null
137         if [ $HAVE_JOURNAL -lt 9 ] && grep -q ext4 /proc/filesystems; then
138                 [ $HAVE_JOURNAL -eq 0 ] && FEATURES="$FEATURES,64bit"
139                 FS_TYPE="ext4"
140                 HAVE_JOURNAL="-j"
141         else
142                 modprobe ext3 2> /dev/null
143                 if grep -q ext3 /proc/filesystems; then
144                         FS_TYPE="ext3"
145                         HAVE_JOURNAL="-j"
146                 fi
147         fi
148 fi
149 if [ -z "$FS_TYPE" ]; then
150         if grep -q ext2 /proc/filesystems; then
151                 FS_TYPE="ext2"
152                 HAVE_JOURNAL=""
153         else
154                 log "no supported filesystem types for mounting, assume ext4"
155                 FS_TYPE="ext4"
156                 HAVE_JOURNAL="-j"
157         fi
158 else
159         log "using environment-supplied FS_TYPE=$FS_TYPE"
160 fi
161
162 # meta_bg and resize_inode features should not be enabled simultaneously
163 if [ $((RANDOM % 12)) -eq 0 ]; then
164         FEATURES="$FEATURES,meta_bg,^resize_inode"
165 else
166         FEATURES="$FEATURES,resize_inode"
167 fi
168
169 if [ "$FS_TYPE" = "ext4" ]; then
170         if [ $((RANDOM % 8)) -eq 0 ]; then
171                 FEATURES="$FEATURES,mmp"
172         fi
173         if [ $((RANDOM % 6)) -eq 0 ]; then
174                 HAVE_JOURNAL=""
175                 FEATURES="$FEATURES,^has_journal"
176         fi
177         if [ $((RANDOM % 12)) -eq 0 ]; then
178                 FIRST_FSCK_OPTS="$FIRST_FSCK_OPTS -Eexpand_extra_isize"
179         fi
180 fi
181
182 MKFS_OPTS="$HAVE_JOURNAL -t $FS_TYPE -b $BLK_SIZE -I $INODE_SIZE -O $FEATURES"
183
184 # Truncate the output log file
185 > $OUT
186 > $TMPFILE
187 rm -f $FAILED $OKFILE
188
189 log "Format the filesystem image with SIZE=$SIZE..."
190 trap cleanup EXIT INT
191
192 # Write some garbage blocks into the filesystem to make sure e2fsck has to do
193 # a more difficult job than checking blocks of zeroes.
194 log "Copy some random data into filesystem image...."
195 make_fs_dirty 32768
196 log "$MKFS $MKFS_OPTS -F $TMPFILE $NUM_BLKS"
197 $MKFS $MKFS_OPTS -F $TMPFILE $NUM_BLKS >> $OUT 2>&1
198 if [ $? -ne 0 ]; then
199         zero_size=$(grep "Device size reported to be zero" $OUT)
200         short_write=$(grep "Attempt to write .* resulted in short write" $OUT)
201
202         if [ -n "$zero_size" -o -n "$short_write" ]; then
203                 echo "mkfs failed due to device size of 0 or a short write."
204                 echo "This is harmless and need not be reported."
205         else
206                 cleanup "mkfs failed - internal error. Aborting test..."
207         fi
208 fi
209 $DUMPE2FS -h $TMPFILE >> $OUT 2>&1
210
211 if [ $(id -u) = 0 -a -n "$FS_TYPE" ]; then
212         mkdir -p $MNTPT
213         if [ $? -ne 0 ]; then
214                 log "Failed to create or find mountpoint...."
215         else
216                 mount -t $FS_TYPE $MOUNT_OPTS $TMPFILE $MNTPT 2>&1 | tee -a $OUT
217                 if [ ${PIPESTATUS[0]} -ne 0 ]; then
218                         log "Error mounting file system - skipped"
219                 else
220                         df -h $MNTPT >> $OUT
221                         df -i $MNTPT >> $OUT
222                         log "Copying data into the test filesystem..."
223
224                         cp -r ../{.git,lib,misc,e2fsck,po} $MNTPT/ >> $OUT 2>&1
225                         sync
226                         umount -f $MNTPT > /dev/null 2>&1 | tee -a $OUT
227                 fi
228         fi
229 else
230         log "skipping mount test for non-root user"
231 fi
232
233 log "Corrupt the image by moving around blocks of data..."
234 log
235 for (( i = 0; i < $CORRUPTION_ITERATIONS; i++ )); do
236         from=$(get_random_location $NUM_BLKS)
237         to=$(get_random_location $NUM_BLKS)
238
239         log "Moving ${CORRUPTION_SIZE}kB from block ${from}kB to ${to}kB"
240         dd if=$TMPFILE of=$TMPFILE bs=1k count=$CORRUPTION_SIZE conv=notrunc \
241                 skip=$from seek=$to >> $OUT 2>&1
242
243         # more corruption by overwriting blocks from within the filesystem.
244         make_fs_dirty $CORRUPTION_SIZE
245 done
246
247 # Copy the image for reproducing the bug.
248 cp --sparse=always $TMPFILE $ARCHIVE >> $OUT 2>&1
249
250 log "First pass of fsck..."
251 $E2FSCK $FIRST_FSCK_OPTS $TMPFILE >> $OUT 2>&1
252 RET=$?
253
254 # Run e2fsck for the second time and check if the problem gets solved.
255 # After we can report error with pass1.
256 [ $((RET & 1)) == 0 ] || log "The first fsck corrected errors"
257 [ $((RET & 2)) == 0 ] || error "The first fsck wants a reboot"
258 [ $((RET & 4)) == 0 ] || error "The first fsck left uncorrected errors"
259 [ $((RET & 8)) == 0 ] || error "The first fsck reports an operational error"
260 [ $((RET & 16)) == 0 ] || error "The first fsck reports there was a usage error"
261 [ $((RET & 32)) == 0 ] || error "The first fsck reports it was cancelled"
262 [ $((RET & 128)) == 0 ] || error "The first fsck reports a library error"
263
264 log "---------------------------------------------------------"
265
266 log "Second pass of fsck..."
267 $E2FSCK $SECOND_FSCK_OPTS $TMPFILE >> $OUT 2>&1
268 RET=$?
269 [ $((RET & 1)) == 0 ] || cleanup "The second fsck corrected errors!"
270 [ $((RET & 2)) == 0 ] || cleanup "The second fsck wants a reboot"
271 [ $((RET & 4)) == 0 ] || cleanup "The second fsck left uncorrected errors"
272 [ $((RET & 8)) == 0 ] || cleanup "The second fsck reports an operational error"
273 [ $((RET & 16)) == 0 ] || cleanup "The second fsck reports a usage error"
274 [ $((RET & 32)) == 0 ] || cleanup "The second fsck reports it was cancelled"
275 [ $((RET & 128)) == 0 ] || cleanup "The second fsck reports a library error"
276
277 [ -f $FAILED ] && cleanup "Test Failed"
278
279 if [ "$MOUNT_AFTER_CORRUPTION" = "yes" ]; then
280         mount -t $FS_TYPE $MOUNT_OPTS $TMPFILE $MNTPT 2>&1 | tee -a $OUT
281         [ ${PIPESTATUS[0]} -ne 0 ] && log "error mounting file system - skipped"
282
283         [ "$REMOVE_FILES" = "yes" ] && rm -rf $MNTPT/* >> $OUT
284         umount -f $MNTPT > /dev/null 2>&1 | tee -a $OUT
285 fi
286
287 # Report success
288 touch $OKFILE
289 echo "$test_name: $test_description: ok"
290
291 cleanup
292
293 COUNT=$((COUNT + 1))
294 test_name=${test_orig}_$COUNT
295 done # while [ $COUNT -le $LOOP_COUNT ]; do