btrfs-progs: tests: more variable quoting in common scripts
[platform/upstream/btrfs-progs.git] / tests / common
1 #!/bin/bash
2 #
3 # Common routines for all tests
4 #
5
6 # assert that argument is not empty and is an existing path (file or directory)
7 _assert_path()
8 {
9         local path
10
11         path="$1"
12         if [ -z "$path" ]; then
13                 echo "ASSERTION FAIL: $path is not valid"
14                 exit 1
15         fi
16
17         if [ -f "$path" -o -d "$path" -o -b "$path" ]; then
18                 return 0
19         fi
20         echo "ASSERTION FAIL: $path is not valid"
21         exit 1
22 }
23
24 _fail()
25 {
26         echo "$*" | tee -a "$RESULTS"
27         exit 1
28 }
29
30 # log a message to the results file
31 _log()
32 {
33         echo "$*" | tee -a "$RESULTS"
34 }
35
36 _not_run()
37 {
38         echo "    [NOTRUN] $*"
39         exit 0
40 }
41
42 run_check()
43 {
44         echo "############### $@" >> "$RESULTS" 2>&1
45         if [[ $TEST_LOG =~ tty ]]; then echo "CMD: $@" > /dev/tty; fi
46         if [ "$1" = 'root_helper' ]; then
47                 "$@" >> "$RESULTS" 2>&1 || _fail "failed: $@"
48         else
49                 $INSTRUMENT "$@" >> "$RESULTS" 2>&1 || _fail "failed: $@"
50         fi
51 }
52
53 # same as run_check but the stderr+stdout output is duplicated on stdout and
54 # can be processed further
55 run_check_stdout()
56 {
57         echo "############### $@" >> "$RESULTS" 2>&1
58         if [[ $TEST_LOG =~ tty ]]; then echo "CMD(stdout): $@" > /dev/tty; fi
59         if [ "$1" = 'root_helper' ]; then
60                 "$@" 2>&1 | tee -a "$RESULTS" || _fail "failed: $@"
61         else
62                 $INSTRUMENT "$@" 2>&1 | tee -a "$RESULTS" || _fail "failed: $@"
63         fi
64 }
65
66 # same as run_check but does not fail the test if it's handled gracefully by
67 # the tool, unexpected failure like segfault or abor will exit forcibly
68 # output is logged
69 run_mayfail()
70 {
71         local ret
72
73         echo "############### $@" >> "$RESULTS" 2>&1
74         if [[ $TEST_LOG =~ tty ]]; then echo "CMD(mayfail): $@" > /dev/tty; fi
75         if [ "$1" = 'root_helper' ]; then
76                 "$@" >> "$RESULTS" 2>&1
77         else
78                 $INSTRUMENT "$@" >> "$RESULTS" 2>&1
79         fi
80         ret=$?
81         if [ $ret != 0 ]; then
82                 echo "failed (ignored, ret=$ret): $@" >> "$RESULTS"
83                 if [ $ret == 139 ]; then
84                         _fail "mayfail: returned code 139 (SEGFAULT), not ignored"
85                 elif [ $ret == 134 ]; then
86                         _fail "mayfail: returned code 134 (SIGABRT), not ignored"
87                 fi
88                 return $ret
89         fi
90 }
91
92 # first argument is error message to print if it fails, otherwise
93 # same as run_check but expects the command to fail, output is logged
94 run_mustfail()
95 {
96         local msg
97
98         msg="$1"
99         shift
100
101         echo "############### $@" >> "$RESULTS" 2>&1
102         if [[ $TEST_LOG =~ tty ]]; then echo "CMD(mustfail): $@" > /dev/tty; fi
103         if [ "$1" = 'root_helper' ]; then
104                 "$@" >> "$RESULTS" 2>&1
105         else
106                 $INSTRUMENT "$@" >> "$RESULTS" 2>&1
107         fi
108         if [ $? != 0 ]; then
109                 echo "failed (expected): $@" >> "$RESULTS"
110                 return 0
111         else
112                 echo "succeeded (unexpected!): $@" >> "$RESULTS"
113                 _fail "unexpected success: $msg"
114                 return 1
115         fi
116 }
117
118 check_prereq()
119 {
120         if ! [ -f "$TOP/$1" ]; then
121                 _fail "Failed prerequisites: $1";
122         fi
123 }
124
125 check_global_prereq()
126 {
127         which $1 &> /dev/null
128         if [ $? -ne 0 ]; then
129                 _fail "Failed system wide prerequisities: $1";
130         fi
131 }
132
133 check_image()
134 {
135         local image
136
137         image=$1
138         echo "testing image $(basename $image)" >> "$RESULTS"
139         "$TOP/btrfs" check "$image" >> "$RESULTS" 2>&1
140         [ $? -eq 0 ] && _fail "btrfs check should have detected corruption"
141
142         run_check "$TOP/btrfs" check --repair "$image"
143         run_check "$TOP/btrfs" check "$image"
144 }
145
146 # Extract a usable image from packed formats
147 # - raw btrfs filesystem images, suffix .raw
148 # - dtto compressed by XZ, suffix .raw.xz
149 # - meta-dump images with suffix .img
150 # - dtto compressed by XZ, suffix .img.xz
151 # - compressed send stream, .stream.xz
152 extract_image()
153 {
154         local image
155         local cleanme
156
157         image="$1"
158         case "$image" in
159         *.img)
160                 rm -f "$image.restored"
161                 : ;;
162         *.img.xz)
163                 xz --decompress --keep "$image" || \
164                         _fail "failed to decompress image $image" >&2
165                 image=${image%%.xz}
166                 rm -f "$image.restored"
167                 cleanme=$image
168                 ;;
169         *.raw)
170                 cp --sparse=auto "$image" "$image.restored"
171                 ;;
172         *.raw.xz)
173                 xz --decompress --keep "$image" || \
174                         _fail "failed to decompress image $image" >&2
175                 image=${image%%.xz}
176                 mv "$image" "$image.restored"
177                 ;;
178         *.stream.xz)
179                 xz --decompress --keep "$image" || \
180                         _fail "failed to decompress file $image" >&2
181                 image=${image%%.xz}
182                 mv "$image" "$image.restored"
183                 ;;
184         esac
185
186         if ! [ -f "$image.restored" ]; then
187                 echo "restoring image $(basename $image)" >> "$RESULTS"
188                 "$TOP/btrfs-image" -r "$image" "$image.restored" \
189                         &>> "$RESULTS" \
190                         || _fail "failed to restore image $image" >&2
191         fi
192
193         [ -f "$cleanme" ] && rm -f "$cleanme"
194
195         echo "$image.restored"
196 }
197
198 # Process all image dumps in a given directory
199 check_all_images()
200 {
201         local dir
202         local extracted
203
204         dir="$1"
205         if [ -z "$dir" ]; then
206                 dir=.
207         fi
208         _assert_path "$dir"
209         for image in $(find "$dir" \( -iname '*.img' -o \
210                                 -iname '*.img.xz' -o    \
211                                 -iname '*.raw' -o       \
212                                 -iname '*.raw.xz' \) | sort)
213         do
214                 extracted=$(extract_image "$image")
215                 check_image "$extracted"
216                 rm -f "$extracted"
217         done
218 }
219
220 # some tests need to mount the recovered image and do verifications call
221 # 'setup_root_helper' and then check for have_root_helper == 1 if the test
222 # needs to fail otherwise; using sudo by default for now
223 SUDO_HELPER=
224 NEED_SUDO_VALIDATE=unknown
225 export SUDO_HELPER
226 export NEED_SUDO_VALIDATE
227 root_helper()
228 {
229         if [ $UID -eq 0 ]; then
230                 "$@"
231         else
232                 if [ "$NEED_SUDO_VALIDATE" = 'yes' ]; then
233                         sudo -v -n &>/dev/null || \
234                                 _not_run "Need to validate sudo credentials"
235                         sudo -n "$@"
236                 elif [ "$NEED_SUDO_VALIDATE" = 'no' ]; then
237                         sudo -n /bin/true &> /dev/null || \
238                                 _not_run "Need to validate sudo user settings"
239                         sudo -n "$@"
240                 else
241                         # should not happen
242                         _not_run "Need to validate root privileges"
243                 fi
244         fi
245 }
246
247 setup_root_helper()
248 {
249         if [ $UID -eq 0 -o -n "$SUDO_HELPER" ]; then
250                 return
251         fi
252
253         # Test for old sudo or special settings, which make sudo -v fail even
254         # if user setting is NOPASSWD
255         sudo -n /bin/true &>/dev/null && NEED_SUDO_VALIDATE=no
256
257         # Newer sudo or default sudo setting
258         sudo -v -n &>/dev/null && NEED_SUDO_VALIDATE=yes
259
260         if [ "$NEED_SUDO_VALIDATE" = 'unknown' ]; then
261                 _not_run "Need to validate root privileges"
262         fi
263         SUDO_HELPER=root_helper
264 }
265
266 prepare_test_dev()
267 {
268         # num[K/M/G/T...]
269         local size="$1"
270
271         [[ "$TEST_DEV" ]] && return
272         [[ "$size" ]] || size='2G'
273
274         echo "\$TEST_DEV not given, use $TOP/test/test.img as fallback" >> \
275                 "$RESULTS"
276         TEST_DEV="$TOP/tests/test.img"
277
278         truncate -s "$size" "$TEST_DEV" || _not_run "create file for loop device failed"
279 }
280
281 run_check_mount_test_dev()
282 {
283         setup_root_helper
284
285         local loop_opt
286         if [[ -b "$TEST_DEV" ]]; then
287                 loop_opt=""
288         elif [[ -f "$TEST_DEV" ]]; then
289                 loop_opt="-o loop"
290         else
291                 _fail "Invalid \$TEST_DEV: $TEST_DEV"
292         fi
293
294         [[ -d "$TEST_MNT" ]] || {
295                 _fail "Invalid \$TEST_MNT: $TEST_MNT"
296         }
297
298         run_check $SUDO_HELPER mount $loop_opt "$@" "$TEST_DEV" "$TEST_MNT"
299 }
300
301 run_check_umount_test_dev()
302 {
303         setup_root_helper
304         run_check $SUDO_HELPER umount "$@" "$TEST_DEV"
305 }
306
307 check_kernel_support()
308 {
309         if ! grep -iq 'btrfs' /proc/filesystems; then
310                 echo "WARNING: btrfs filesystem not listed in /proc/filesystems, some tests might fail"
311                 return 1
312         fi
313         return 0
314 }
315
316 # how many files to create.
317 DATASET_SIZE=50
318
319 generate_dataset() {
320
321         dataset_type="$1"
322         dirpath=$TEST_MNT/$dataset_type
323         run_check $SUDO_HELPER mkdir -p "$dirpath"
324
325         case "$dataset_type" in
326                 small)
327                         for num in $(seq 1 "$DATASET_SIZE"); do
328                                 run_check $SUDO_HELPER dd if=/dev/urandom of="$dirpath/$dataset_type.$num" bs=10K \
329                                 count=1 >/dev/null 2>&1
330                         done
331                         ;;
332
333                 hardlink)
334                         for num in $(seq 1 "$DATASET_SIZE"); do
335                                 run_check $SUDO_HELPER touch "$dirpath/$dataset_type.$num"
336                                 run_check $SUDO_HELPER ln "$dirpath/$dataset_type.$num" "$dirpath/hlink.$num"
337                         done
338                         ;;
339
340                 fast_symlink)
341                         for num in $(seq 1 "$DATASET_SIZE"); do
342                                 run_check $SUDO_HELPER touch "$dirpath/$dataset_type.$num"
343                                 run_check cd "$dirpath" && \
344                                         $SUDO_HELPER ln -s "$dataset_type.$num" "$dirpath/slink.$num" && \
345                                         cd /
346                         done
347                         ;;
348
349                 brokenlink)
350                         for num in $(seq 1 "$DATASET_SIZE"); do
351                                 run_check $SUDO_HELPER ln -s "$dirpath/$dataset_type.$num" "$dirpath/blink.$num"
352                         done
353                         ;;
354
355                 perm)
356                         for modes in 777 775 755 750 700 666 664 644 640 600 444 440 400 000            \
357                                 1777 1775 1755 1750 1700 1666 1664 1644 1640 1600 1444 1440 1400 1000   \
358                                 2777 2775 2755 2750 2700 2666 2664 2644 2640 2600 2444 2440 2400 2000   \
359                                 4777 4775 4755 4750 4700 4666 4664 4644 4640 4600 4444 4440 4400 4000; do
360                                 run_check $SUDO_HELPER touch "$dirpath/$dataset_type.$modes"
361                                 run_check $SUDO_HELPER chmod "$modes" "$dirpath/$dataset_type.$modes"
362                         done
363                         ;;
364
365                 sparse)
366                         for num in $(seq 1 "$DATASET_SIZE"); do
367                                 run_check $SUDO_HELPER dd if=/dev/urandom of="$dirpath/$dataset_type.$num" bs=10K \
368                                 count=1 >/dev/null 2>&1
369                                 run_check $SUDO_HELPER truncate -s 500K "$dirpath/$dataset_type.$num"
370                                 run_check $SUDO_HELPER dd if=/dev/urandom of="$dirpath/$dataset_type.$num" bs=10K \
371                                 oflag=append conv=notrunc count=1 >/dev/null 2>&1
372                                 run_check $SUDO_HELPER truncate -s 800K "$dirpath/$dataset_type.$num"
373                         done
374                         ;;
375
376                 acls)
377                         for num in $(seq 1 "$DATASET_SIZE"); do
378                                 run_check $SUDO_HELPER touch "$dirpath/$dataset_type.$num"
379                                 run_check $SUDO_HELPER setfacl -m "u:root:x" "$dirpath/$dataset_type.$num"
380                                 run_check $SUDO_HELPER setfattr -n user.foo -v "bar$num" "$dirpath/$dataset_type.$num"
381                         done
382                         ;;
383
384                 fifo)
385                         for num in $(seq 1 "$DATASET_SIZE"); do
386                                 run_check $SUDO_HELPER mkfifo "$dirpath/$dataset_type.$num"
387                         done
388                         ;;
389
390                 slow_symlink)
391                         long_filename=`date +%s | sha256sum | cut -f1 -d'-'`
392                         run_check $SUDO_HELPER touch "$dirpath/$long_filename"
393                         for num in $(seq 1 "$DATASET_SIZE"); do
394                                 run_check $SUDO_HELPER ln -s "$dirpath/$long_filename" "$dirpath/slow_slink.$num"
395                         done
396                         ;;
397         esac
398 }
399
400 init_env()
401 {
402         TEST_MNT="${TEST_MNT:-$TOP/tests/mnt}"
403         export TEST_MNT
404         mkdir -p "$TEST_MNT" || { echo "Failed mkdir -p $TEST_MNT"; exit 1; }
405
406 }
407 init_env