3 # Routines for fuzzing and scrubbing a filesystem.
5 #-----------------------------------------------------------------------
6 # Copyright (c) 2017 Oracle. All Rights Reserved.
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 2 of the License, or
10 # (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with this program; if not, write to the Free Software
19 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
21 #-----------------------------------------------------------------------
23 # Modify various files after a fuzzing operation
24 _scratch_fuzz_modify() {
27 test -z "${nr}" && nr=50000
28 echo "+++ touch ${nr} files"
29 blk_sz=$(stat -f -c '%s' ${SCRATCH_MNT})
30 $XFS_IO_PROG -f -c "pwrite -S 0x63 0 ${blk_sz}" "/tmp/afile" > /dev/null
32 find "${SCRATCH_MNT}/" -type f 2> /dev/null | head -n "${nr}" | while read f; do
33 setfattr -n "user.date" -v "${date}" "$f"
34 cat "/tmp/afile" >> "$f"
40 echo "+++ create files"
41 mkdir -p "${SCRATCH_MNT}/test.moo"
42 $XFS_IO_PROG -f -c 'pwrite -S 0x80 0 65536' "${SCRATCH_MNT}/test.moo/urk" > /dev/null
45 echo "+++ remove files"
46 rm -rf "${SCRATCH_MNT}/test.moo"
49 # Try to access files after fuzzing
50 _scratch_fuzz_test() {
51 echo "+++ ls -laR" >> $seqres.full
52 ls -laR "${SCRATCH_MNT}/test.1/" >/dev/null 2>&1
54 echo "+++ cat files" >> $seqres.full
55 (find "${SCRATCH_MNT}/test.1/" -type f -size -1048576k -print0 | xargs -0 cat) >/dev/null 2>&1
58 # Do we have an online scrub program?
62 test -x "$XFS_SCRUB_PROG" || _notrun "xfs_scrub not found"
65 _notrun "No online scrub program for ${FSTYP}."
70 # Scrub the scratch filesystem metadata (online)
74 $XFS_SCRUB_PROG -d -T -v "$@" $SCRATCH_MNT
77 _fail "No online scrub program for ${FSTYP}."
82 # Filter out any keys with an array index >= 10, collapse any array range
83 # ("[1-195]") to the first item, and ignore padding fields.
84 __filter_xfs_db_keys() {
85 sed -e '/\([a-z]*\)\[\([0-9][0-9]\+\)\].*/d' \
86 -e 's/\([a-zA-Z0-9_]*\)\[\([0-9]*\)-[0-9]*\]/\1[\2]/g' \
90 # Filter the xfs_db print command's field debug information
91 # into field name and type.
92 __filter_xfs_db_print_fields() {
94 if [ -z "${filter}" ] || [ "${filter}" = "nofilter" ]; then
97 grep ' = ' | while read key equals value; do
98 fuzzkey="$(echo "${key}" | __filter_xfs_db_keys)"
99 if [ -z "${fuzzkey}" ]; then
101 elif [[ "${value}" == "["* ]]; then
102 echo "${value}" | sed -e 's/^.//g' -e 's/.$//g' -e 's/,/\n/g' | while read subfield; do
103 echo "${fuzzkey}.${subfield}"
104 done | __filter_xfs_db_keys
108 done | egrep "${filter}"
111 # Navigate to some part of the filesystem and print the field info.
112 # The first argument is an egrep filter for the fields
113 # The rest of the arguments are xfs_db commands to locate the metadata.
114 _scratch_xfs_list_metadata_fields() {
117 if [ -n "${SCRATCH_XFS_LIST_METADATA_FIELDS}" ]; then
118 echo "${SCRATCH_XFS_LIST_METADATA_FIELDS}" | tr '[ ,]' '[\n\n]'
124 cmds+=("-c" "${arg}")
126 _scratch_xfs_db "${cmds[@]}" -c print | __filter_xfs_db_print_fields "${filter}"
129 # Get a metadata field
130 # The first arg is the field name
131 # The rest of the arguments are xfs_db commands to find the metadata.
132 _scratch_xfs_get_metadata_field() {
136 grep_key="$(echo "${key}" | tr '[]()' '....')"
139 cmds+=("-c" "${arg}")
141 _scratch_xfs_db "${cmds[@]}" -c "print ${key}" | grep "^${grep_key}" | \
145 # Set a metadata field
146 # The first arg is the field name
147 # The second arg is the new value
148 # The rest of the arguments are xfs_db commands to find the metadata.
149 _scratch_xfs_set_metadata_field() {
156 cmds+=("-c" "${arg}")
158 _scratch_xfs_db -x "${cmds[@]}" -c "write -d ${key} ${value}"
162 # Fuzz a metadata field
163 # The first arg is the field name
164 # The second arg is the xfs_db fuzz verb
165 # The rest of the arguments are xfs_db commands to find the metadata.
166 _scratch_xfs_fuzz_metadata_field() {
171 if [[ "${key}" == *crc ]]; then
176 oldval="$(_scratch_xfs_get_metadata_field "${key}" "$@")"
180 cmds+=("-c" "${arg}")
182 _scratch_xfs_db -x "${cmds[@]}" -c "fuzz ${fuzz_arg} ${key} ${value}"
184 newval="$(_scratch_xfs_get_metadata_field "${key}" "$@" 2> /dev/null)"
185 if [ "${oldval}" = "${newval}" ]; then
186 echo "Field ${key} already set to ${newval}, skipping test."
192 # Try to forcibly unmount the scratch fs
193 __scratch_xfs_fuzz_unmount()
195 while _scratch_unmount 2>/dev/null; do sleep 0.2; done
198 # Restore metadata to scratch device prior to field-fuzzing.
199 __scratch_xfs_fuzz_mdrestore()
201 test -e "${POPULATE_METADUMP}" || _fail "Need to set POPULATE_METADUMP"
203 __scratch_xfs_fuzz_unmount
204 xfs_mdrestore "${POPULATE_METADUMP}" "${SCRATCH_DEV}"
209 test -w /dev/ttyprintk && echo "$@" >> /dev/ttyprintk
212 # Fuzz one field of some piece of metadata.
213 # First arg is the field name
214 # Second arg is the fuzz verb (ones, zeroes, random, add, sub...)
215 # Third arg is the repair mode (online, offline, both)
216 __scratch_xfs_fuzz_field_test() {
222 # Set the new field value
223 __fuzz_notify "+ Fuzz ${field} = ${fuzzverb}"
224 echo "========================"
225 _scratch_xfs_fuzz_metadata_field "${field}" ${fuzzverb} "$@"
227 test $res -ne 0 && return
229 # Try to catch the error with scrub
230 echo "+ Try to catch the error"
233 if [ $res -eq 0 ]; then
234 # Try an online scrub unless we're fuzzing ag 0's sb,
235 # which scrub doesn't know how to fix.
236 echo "++ Online scrub"
237 if [ "$1" != "sb 0" ]; then
238 _scratch_scrub -n -a 1 -e continue 2>&1
241 (>&2 echo "scrub didn't fail with ${field} = ${fuzzverb}.")
244 # Try fixing the filesystem online?!
245 if [ "${repair}" = "online" ] || [ "${repair}" = "both" ]; then
246 __fuzz_notify "++ Try to repair filesystem online"
247 _scratch_scrub -y 2>&1
250 (>&2 echo "online repair failed ($res) with ${field} = ${fuzzverb}.")
253 __scratch_xfs_fuzz_unmount
254 elif [ "${repair}" = "online" ] || [ "${repair}" = "both" ]; then
255 (>&2 echo "mount failed ($res) with ${field} = ${fuzzverb}.")
258 # Repair the filesystem offline?
259 if [ "${repair}" = "offline" ] || [ "${repair}" = "both" ]; then
260 echo "+ Try to repair the filesystem offline"
261 _repair_scratch_fs 2>&1
264 (>&2 echo "offline repair failed ($res) with ${field} = ${fuzzverb}.")
267 # See if repair finds a clean fs
268 echo "+ Make sure error is gone (offline)"
269 _scratch_xfs_repair -n 2>&1
272 (>&2 echo "offline re-scrub ($res) with ${field} = ${fuzzverb}.")
274 # See if scrub finds a clean fs
275 echo "+ Make sure error is gone (online)"
278 if [ $res -eq 0 ]; then
279 # Try an online scrub unless we're fuzzing ag 0's sb,
280 # which scrub doesn't know how to fix.
281 echo "++ Online scrub"
282 if [ "$1" != "sb 0" ]; then
283 _scratch_scrub -n -e continue 2>&1
286 (>&2 echo "online re-scrub ($res) with ${field} = ${fuzzverb}.")
289 # Try modifying the filesystem again!
290 __fuzz_notify "++ Try to write filesystem again"
291 _scratch_fuzz_modify 100 2>&1
292 __scratch_xfs_fuzz_unmount
294 (>&2 echo "re-mount failed ($res) with ${field} = ${fuzzverb}.")
297 # See if repair finds a clean fs
298 echo "+ Re-check the filesystem (offline)"
299 _scratch_xfs_repair -n 2>&1
302 (>&2 echo "re-repair failed ($res) with ${field} = ${fuzzverb}.")
305 # Make sure we have all the pieces we need for field fuzzing
306 _require_scratch_xfs_fuzz_fields()
308 _require_scratch_nocheck
310 _require_populate_commands
311 _scratch_mkfs_xfs >/dev/null 2>&1
312 _require_xfs_db_command "fuzz"
315 # Grab the list of available fuzzing verbs
316 _scratch_xfs_list_fuzz_verbs() {
317 if [ -n "${SCRATCH_XFS_LIST_FUZZ_VERBS}" ]; then
318 echo "${SCRATCH_XFS_LIST_FUZZ_VERBS}" | tr '[ ,]' '[\n\n]'
321 _scratch_xfs_db -x -c 'sb 0' -c 'fuzz' | grep '^Fuzz commands:' | \
322 sed -e 's/[,.]//g' -e 's/Fuzz commands: //g' -e 's/ /\n/g'
325 # Fuzz some of the fields of some piece of metadata
326 # The first argument is an egrep filter for the field names
327 # The second argument is the repair mode (online, offline, both)
328 # The rest of the arguments are xfs_db commands to locate the metadata.
330 # Users can specify the fuzz verbs via SCRATCH_XFS_LIST_FUZZ_VERBS
331 # They can specify the fields via SCRATCH_XFS_LIST_METADATA_FIELDS
332 _scratch_xfs_fuzz_metadata() {
337 fields="$(_scratch_xfs_list_metadata_fields "${filter}" "$@")"
338 verbs="$(_scratch_xfs_list_fuzz_verbs)"
339 echo "Fields we propose to fuzz under: $@"
340 echo $(echo "${fields}")
341 echo "Verbs we propose to fuzz with:"
342 echo $(echo "${verbs}")
344 echo "${fields}" | while read field; do
345 echo "${verbs}" | while read fuzzverb; do
346 __scratch_xfs_fuzz_mdrestore
347 __scratch_xfs_fuzz_field_test "${field}" "${fuzzverb}" "${repair}" "$@"