generic: test for file loss after mix of rename, fsync and inode eviction
[xfstests-dev.git] / tests / generic / 001
1 #! /bin/bash
2 # SPDX-License-Identifier: GPL-2.0
3 # Copyright (c) 2000-2001 Silicon Graphics, Inc.  All Rights Reserved.
4 #
5 # FS QA Test No. 001
6 #
7 # Random file copier to produce chains of identical files so the head
8 # and the tail can be diff'd at the end of each iteration.
9 #
10 # Exercises creat, write and unlink for a variety of directory sizes, and
11 # checks for data corruption.
12 #
13 # run [config]
14 #
15 # config has one line per file with filename and byte size, else use
16 # the default one below.
17 #
18 . ./common/preamble
19 _begin_fstest rw dir udf auto quick
20
21 # Import common functions.
22 . ./common/filter
23
24 status=1
25 done_cleanup=false
26 _register_cleanup "_cleanup; rm -f $tmp.*"
27
28 # real QA test starts here
29 _supported_fs generic
30 _require_test
31
32 verbose=true
33 verify=$here/verify_fill
34
35 if [ $# -eq 0 ]
36 then
37     # use the default config
38     #
39     cat <<End-of-File >$tmp.config
40 # pathname      size in bytes
41 #
42 small           10
43 big             102400
44 sub/small       10
45 sub/big         102400
46 #
47 sub/a           1
48 sub/b           2
49 sub/c           4
50 sub/d           8
51 sub/e           16
52 sub/f           32
53 sub/g           64
54 sub/h           128
55 sub/i           256
56 sub/j           512
57 sub/k           1024
58 sub/l           2048
59 sub/m           4096
60 sub/n           8192
61 #
62 sub/a00         100
63 sub/b00         200
64 sub/c00         400
65 sub/d00         800
66 sub/e00         1600
67 sub/f00         3200
68 sub/g00         6400
69 sub/h00         12800
70 sub/i00         25600
71 sub/j00         51200
72 sub/k00         102400
73 sub/l00         204800
74 sub/m00         409600
75 sub/n00         819200
76 #
77 sub/a000        1000
78 sub/e000        16000
79 sub/h000        128000
80 sub/k000        1024000
81 End-of-File
82 elif [ $# -eq 1 ]
83 then
84     if [ -f $1 ]
85     then
86         cp $1 $tmp.config
87     else
88         echo "Error: cannot open config \"$1\""
89         exit 1
90     fi
91 else
92     echo "Usage: run [config]"
93     exit 1
94 fi
95
96 ncopy=200               # number of file copies in the chain step
97 udf_fsize=20240         # number of sectors for UDF
98
99 _setup()
100 {
101     if mkdir -p $TEST_DIR/$$
102     then
103         :
104     else
105         echo "Error: cannot mkdir \"$TEST_DIR/$$\""
106         exit 1
107     fi
108     cd $TEST_DIR/$$
109
110     $verbose && echo -n "setup "
111     sed -e '/^#/d' $tmp.config \
112     | while read file nbytes
113     do
114         dir=`dirname $file`
115         if [ "$dir" != "." ]
116         then
117             if [ ! -d $dir ]
118             then
119                 if mkdir $dir
120                 then
121                     :
122                 else
123                     $verbose && echo
124                     echo "Error: cannot mkdir \"$dir\""
125                     exit 1
126                 fi
127             fi
128         fi
129         rm -f $file
130         if $here/src/fill $file $file $nbytes
131         then
132             :
133         else
134             $verbose && echo
135             echo "Error: cannot create \"$file\""
136             exit 1
137         fi
138         $verbose && echo -n "."
139     done
140     $verbose && echo
141 }
142
143 _mark_iteration()
144 {
145     $verbose && echo -n "mark_iteration "
146     sed -e '/^#/d' $tmp.config \
147     | while read file nbytes
148     do
149         if [ ! -f $file ]
150         then
151             $verbose && echo
152             echo "Error: $file vanished!"
153             touch $tmp.bad
154             continue
155         fi
156         sed -e "s/ [0-9][0-9]* / $1 /" <$file >$file.tmp
157         mv $file.tmp $file
158         $verbose && echo -n "."
159     done
160     $verbose && echo
161 }
162
163 # for each file, make a number of copies forming a chain like foo.0,
164 # foo.1, foo.2, ... foo.N
165 #
166 # files are chosen at random, so the lengths of the chains are different
167 #
168 # then rename foo.N to foo.last and remove all of the other files in
169 # the chain
170 #
171 _chain()
172 {
173     $AWK_PROG -v full_file=$seqres.full -v verify=$verify <$tmp.config '
174 BEGIN   { nfile = 0 }
175 /^#/    { next }
176         { file[nfile] = $1
177           size[nfile] = $2
178           link[nfile] = 0
179           nfile++
180           total_size += $2
181         }
182 END     { srand('$iter')
183           for (i=0; i < '$ncopy'; i++) {
184             # choose a file at random, and add one copy to that chain
185             j = -1
186             while (j < 0 || j >= nfile)
187                 j = int(rand() * nfile)
188             if (link[j] == 0) {
189                 # previous should already exist and next one should not exist
190                 printf "if [ ! -f %s ]; then echo \"%s missing!\"; exit; fi\n",file[j],file[j]
191                 printf "if [ -f %s.0 ]; then echo \"%s.0 already present!\"; exit; fi\n",file[j],file[j]
192                 printf "cp %s %s.0 || exit 1\n",file[j],file[j]
193                 printf "ls -i %s.0\n", file[j] >full_file;
194                 total_size += size[j]
195                 printf "# total size = %d\n", total_size 
196             }
197             else {
198                 # previous should already exist and next one should not exist
199                 printf "if [ ! -f %s.%d ]; then echo \"%s.%d missing!\"; exit; fi\n",file[j],link[j]-1,file[j],link[j]-1
200                 printf "if [ -f %s.%d ]; then echo \"%s.%d already present!\"; exit; fi\n",file[j],link[j],file[j],link[j]
201                 printf "cp %s.%d %s.%d || exit 1\n",file[j],link[j]-1,file[j],link[j]
202                 printf "ls -i %s.%d\n", file[j], link[j] >full_file;
203                 total_size += size[j]
204                 printf "# total size = %d\n", total_size 
205             }
206             link[j]++
207           }
208           # close all the chains, 
209           # if have at least one copy then move the last copy to "file[j].last"
210           # and remove all of the other files except the head of the chain
211           for (j=0; j<nfile; j++) {
212             if (link[j] > 0) {
213                 printf "mv %s.%d %s.last\n",file[j],link[j]-1,file[j]
214                 printf "ls -i %s.last\n", file[j] >full_file;
215             }
216             for (i=0; i<link[j]-1; i++) {
217                 printf "rm -f %s.%d\n",file[j],i
218             }
219           }
220         }' \
221         | tee -a $seqres.full | sh
222 }
223
224 _check()
225 {
226     rm -f $tmp.bad
227     $verbose && echo -n "check "
228     sed -e '/^#/d' $tmp.config \
229     | while read file nbytes
230     do
231         # the file is never removed so it should exist
232         if [ ! -f $file ]
233         then
234             $verbose && echo
235             echo "Error: $file vanished!"
236             touch $tmp.bad
237             continue
238         fi
239         # checks that the file and its last copy are the same
240         if [ -f $file.last ]
241         then
242             if cmp $file $file.last >/dev/null 2>&1
243             then
244                 $verbose && echo -n "."
245             else
246                 $verbose && echo
247                 echo "Error: corruption for $file ..."
248                 diff -c $file $file.last
249                 touch $tmp.bad
250             fi
251         else
252             $verbose && echo -n "."
253         fi
254     done
255     $verbose && echo
256 }
257
258 # Override the default cleanup function.
259 _cleanup()
260 {
261     # cleanup
262     #
263     if $done_cleanup
264     then
265         :
266     elif [ $status -eq 0 ]
267     then
268         $verbose && echo "cleanup"
269         cd /
270         rm -rf $TEST_DIR/$$
271         done_cleanup=true
272     fi
273 }
274
275 status=0
276 _cleanup
277 status=1
278 done_cleanup=false
279
280 _setup
281
282 # do the test
283 #
284 for iter in 1 2 3 4 5
285 do
286     echo -n "iter $iter chain ... "
287     echo "iter $iter" >> $seqres.full
288     _chain
289     _check
290     if [ -f $tmp.bad ]
291     then
292         echo "Fatal error: test abandoned without changes"
293         exit 1
294     fi
295 done
296
297 status=0
298 exit