common/fuzzy: move fuzzing helper functions here
[xfstests-dev.git] / common / populate
1 ##/bin/bash
2 # Routines for populating a scratch fs, and helpers to exercise an FS
3 # once it's been fuzzed.
4 #-----------------------------------------------------------------------
5 #  Copyright (c) 2015 Oracle.  All Rights Reserved.
6 #  This program is free software; you can redistribute it and/or modify
7 #  it under the terms of the GNU General Public License as published by
8 #  the Free Software Foundation; either version 2 of the License, or
9 #  (at your option) any later version.
10 #
11 #  This program is distributed in the hope that it will be useful,
12 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
13 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 #  GNU General Public License for more details.
15 #
16 #  You should have received a copy of the GNU General Public License
17 #  along with this program; if not, write to the Free Software
18 #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
19 #  USA
20 #
21 #  Contact information: Silicon Graphics, Inc., 1500 Crittenden Lane,
22 #  Mountain View, CA 94043, USA, or: http://www.sgi.com
23 #-----------------------------------------------------------------------
24
25 _require_populate_commands() {
26         _require_xfs_io_command "falloc"
27         _require_xfs_io_command "fpunch"
28         _require_test_program "punch-alternating"
29         _require_command "$XFS_DB_PROG" "xfs_db"
30 }
31
32 _require_xfs_db_blocktrash_z_command() {
33         test "${FSTYP}" = "xfs" || _notrun "cannot run xfs_db on ${FSTYP}"
34         $XFS_DB_PROG -x -f -c 'blocktrash -z' "${TEST_DEV}" | grep -q 'nothing on stack' || _notrun "blocktrash -z not supported"
35 }
36
37 # Attempt to make files of "every" format for data, dirs, attrs etc.
38 # (with apologies to Eric Sandeen for mutating xfser.sh)
39
40 # Create a large directory
41 __populate_create_dir() {
42         name="$1"
43         nr="$2"
44         missing="$3"
45
46         mkdir -p "${name}"
47         seq 0 "${nr}" | while read d; do
48                 creat=mkdir
49                 test "$((d % 20))" -eq 0 && creat=touch
50                 $creat "${name}/$(printf "%.08d" "$d")"
51         done
52
53         test -z "${missing}" && return
54         seq 1 2 "${nr}" | while read d; do
55                 rm -rf "${name}/$(printf "%.08d" "$d")"
56         done
57 }
58
59 # Add a bunch of attrs to a file
60 __populate_create_attr() {
61         name="$1"
62         nr="$2"
63         missing="$3"
64
65         touch "${name}"
66         seq 0 "${nr}" | while read d; do
67                 setfattr -n "user.$(printf "%.08d" "$d")" -v "$(printf "%.08d" "$d")" "${name}"
68         done
69
70         test -z "${missing}" && return
71         seq 1 2 "${nr}" | while read d; do
72                 setfattr -x "user.$(printf "%.08d" "$d")" "${name}"
73         done
74 }
75
76 # Fill up some percentage of the remaining free space
77 __populate_fill_fs() {
78         dir="$1"
79         pct="$2"
80         test -z "${pct}" && pct=60
81
82         mkdir -p "${dir}/test/1"
83         cp -pRdu "${dir}"/S_IFREG* "${dir}/test/1/"
84
85         SRC_SZ="$(du -ks "${dir}/test/1" | cut -f 1)"
86         FS_SZ="$(( $(stat -f "${dir}" -c '%a * %S') / 1024 ))"
87
88         NR="$(( (FS_SZ * ${pct} / 100) / SRC_SZ ))"
89
90         echo "FILL FS"
91         echo "src_sz $SRC_SZ fs_sz $FS_SZ nr $NR"
92         seq 2 "${NR}" | while read nr; do
93                 cp -pRdu "${dir}/test/1" "${dir}/test/${nr}"
94         done
95 }
96
97 # Populate an XFS on the scratch device with (we hope) all known
98 # types of metadata block
99 _scratch_xfs_populate() {
100         fill=1
101
102         for arg in $@; do
103                 case "${arg}" in
104                 "nofill")
105                         fill=0;;
106                 esac
107         done
108
109         _scratch_mount
110         blksz="$(stat -f -c '%s' "${SCRATCH_MNT}")"
111         dblksz="$(xfs_info "${SCRATCH_MNT}" | grep naming.*bsize | sed -e 's/^.*bsize=//g' -e 's/\([0-9]*\).*$/\1/g')"
112         leaf_lblk="$((32 * 1073741824 / blksz))"
113         node_lblk="$((64 * 1073741824 / blksz))"
114
115         # Data:
116
117         # Fill up the root inode chunk
118         echo "+ fill root ino chunk"
119         seq 1 64 | while read f; do
120                 $XFS_IO_PROG -f -c "truncate 0" "${SCRATCH_MNT}/dummy${f}"
121         done
122
123         # Regular files
124         # - FMT_EXTENTS
125         echo "+ extents file"
126         $XFS_IO_PROG -f -c "pwrite -S 0x61 0 ${blksz}" "${SCRATCH_MNT}/S_IFREG.FMT_EXTENTS"
127
128         # - FMT_BTREE
129         echo "+ btree extents file"
130         nr="$((blksz * 2 / 16))"
131         $XFS_IO_PROG -f -c "pwrite -S 0x62 0 $((blksz * nr))" "${SCRATCH_MNT}/S_IFREG.FMT_BTREE"
132         ./src/punch-alternating "${SCRATCH_MNT}/S_IFREG.FMT_BTREE"
133
134         # Directories
135         # - INLINE
136         echo "+ inline dir"
137         __populate_create_dir "${SCRATCH_MNT}/S_IFDIR.FMT_INLINE" 1
138
139         # - BLOCK
140         echo "+ block dir"
141         __populate_create_dir "${SCRATCH_MNT}/S_IFDIR.FMT_BLOCK" "$((dblksz / 40))"
142
143         # - LEAF
144         echo "+ leaf dir"
145         __populate_create_dir "${SCRATCH_MNT}/S_IFDIR.FMT_LEAF" "$((dblksz / 12))"
146
147         # - NODE
148         echo "+ node dir"
149         __populate_create_dir "${SCRATCH_MNT}/S_IFDIR.FMT_NODE" "$((16 * dblksz / 40))" true
150
151         # - BTREE
152         echo "+ btree dir"
153         __populate_create_dir "${SCRATCH_MNT}/S_IFDIR.FMT_BTREE" "$((128 * dblksz / 40))" true
154
155         # Symlinks
156         # - FMT_LOCAL
157         echo "+ inline symlink"
158         ln -s target "${SCRATCH_MNT}/S_IFLNK.FMT_LOCAL"
159
160         # - FMT_EXTENTS
161         echo "+ extents symlink"
162         ln -s "$(perl -e 'print "x" x 1023;')" "${SCRATCH_MNT}/S_IFLNK.FMT_EXTENTS"
163
164         # Char & block
165         echo "+ special"
166         mknod "${SCRATCH_MNT}/S_IFCHR" c 1 1
167         mknod "${SCRATCH_MNT}/S_IFBLK" c 1 1
168
169         # Attribute formats
170         # LOCAL
171         echo "+ local attr"
172         __populate_create_attr "${SCRATCH_MNT}/ATTR.FMT_LOCAL" 1
173
174         # LEAF
175         echo "+ leaf attr"
176         __populate_create_attr "${SCRATCH_MNT}/ATTR.FMT_LEAF" "$((blksz / 40))"
177
178         # NODE
179         echo "+ node attr"
180         __populate_create_attr "${SCRATCH_MNT}/ATTR.FMT_NODE" "$((8 * blksz / 40))"
181
182         # BTREE
183         echo "+ btree attr"
184         __populate_create_attr "${SCRATCH_MNT}/ATTR.FMT_BTREE" "$((64 * blksz / 40))" true
185
186         # FMT_EXTENTS with a remote less-than-a-block value
187         echo "+ attr extents with a remote less-than-a-block value"
188         touch "${SCRATCH_MNT}/ATTR.FMT_EXTENTS_REMOTE3K"
189         $XFS_IO_PROG -f -c "pwrite -S 0x43 0 $((blksz - 300))" "${SCRATCH_MNT}/attrvalfile" > /dev/null
190         attr -q -s user.remotebtreeattrname "${SCRATCH_MNT}/ATTR.FMT_EXTENTS_REMOTE3K" < "${SCRATCH_MNT}/attrvalfile"
191
192         # FMT_EXTENTS with a remote block-size value
193         echo "+ attr extents with a remote one-block value"
194         touch "${SCRATCH_MNT}/ATTR.FMT_EXTENTS_REMOTE4K"
195         $XFS_IO_PROG -f -c "pwrite -S 0x44 0 ${blksz}" "${SCRATCH_MNT}/attrvalfile" > /dev/null
196         attr -q -s user.remotebtreeattrname "${SCRATCH_MNT}/ATTR.FMT_EXTENTS_REMOTE4K" < "${SCRATCH_MNT}/attrvalfile"
197         rm -rf "${SCRATCH_MNT}/attrvalfile"
198
199         # Make an unused inode
200         echo "+ empty file"
201         touch "${SCRATCH_MNT}/unused"
202         $XFS_IO_PROG -f -c 'fsync' "${SCRATCH_MNT}/unused"
203         rm -rf "${SCRATCH_MNT}/unused"
204
205         # Free space btree
206         echo "+ freesp btree"
207         nr="$((blksz * 2 / 8))"
208         $XFS_IO_PROG -f -c "pwrite -S 0x62 0 $((blksz * nr))" "${SCRATCH_MNT}/BNOBT"
209         ./src/punch-alternating "${SCRATCH_MNT}/BNOBT"
210
211         # Reverse-mapping btree
212         is_rmapbt="$(xfs_info "${SCRATCH_MNT}" | grep -c 'rmapbt=1')"
213         if [ $is_rmapbt -gt 0 ]; then
214                 echo "+ rmapbt btree"
215                 nr="$((blksz * 2 / 24))"
216                 $XFS_IO_PROG -f -c "pwrite -S 0x62 0 $((blksz * nr))" "${SCRATCH_MNT}/RMAPBT"
217                 ./src/punch-alternating "${SCRATCH_MNT}/RMAPBT"
218         fi
219
220         # Realtime Reverse-mapping btree
221         is_rt="$(xfs_info "${SCRATCH_MNT}" | grep -c 'rtextents=[1-9]')"
222         if [ $is_rmapbt -gt 0 ] && [ $is_rt -gt 0 ]; then
223                 echo "+ rtrmapbt btree"
224                 nr="$((blksz * 2 / 32))"
225                 $XFS_IO_PROG -f -R -c "pwrite -S 0x62 0 $((blksz * nr))" "${SCRATCH_MNT}/RTRMAPBT"
226                 ./src/punch-alternating "${SCRATCH_MNT}/RTRMAPBT"
227         fi
228
229         # Reference-count btree
230         is_reflink="$(xfs_info "${SCRATCH_MNT}" | grep -c 'reflink=1')"
231         if [ $is_reflink -gt 0 ]; then
232                 echo "+ reflink btree"
233                 nr="$((blksz * 2 / 12))"
234                 $XFS_IO_PROG -f -c "pwrite -S 0x62 0 $((blksz * nr))" "${SCRATCH_MNT}/REFCOUNTBT"
235                 cp --reflink=always "${SCRATCH_MNT}/REFCOUNTBT" "${SCRATCH_MNT}/REFCOUNTBT2"
236                 ./src/punch-alternating "${SCRATCH_MNT}/REFCOUNTBT"
237         fi
238
239         # Copy some real files (xfs tests, I guess...)
240         echo "+ real files"
241         test $fill -ne 0 && __populate_fill_fs "${SCRATCH_MNT}" 5
242
243         umount "${SCRATCH_MNT}"
244 }
245
246 # Populate an ext4 on the scratch device with (we hope) all known
247 # types of metadata block
248 _scratch_ext4_populate() {
249         fill=1
250
251         for arg in $@; do
252                 case "${arg}" in
253                 "nofill")
254                         fill=0;;
255                 esac
256         done
257
258         _scratch_mount
259         blksz="$(stat -f -c '%s' "${SCRATCH_MNT}")"
260         dblksz="${blksz}"
261         leaf_lblk="$((32 * 1073741824 / blksz))"
262         node_lblk="$((64 * 1073741824 / blksz))"
263
264         # Data:
265
266         # Regular files
267         # - FMT_INLINE
268         echo "+ inline file"
269         $XFS_IO_PROG -f -c "pwrite -S 0x61 0 1" "${SCRATCH_MNT}/S_IFREG.FMT_INLINE"
270
271         # - FMT_EXTENTS
272         echo "+ extents file"
273         $XFS_IO_PROG -f -c "pwrite -S 0x61 0 ${blksz}" "${SCRATCH_MNT}/S_IFREG.FMT_EXTENTS"
274
275         # - FMT_ETREE
276         echo "+ extent tree file"
277         nr="$((blksz * 2 / 12))"
278         $XFS_IO_PROG -f -c "pwrite -S 0x62 0 $((blksz * nr))" "${SCRATCH_MNT}/S_IFREG.FMT_ETREE"
279         ./src/punch-alternating "${SCRATCH_MNT}/S_IFREG.FMT_ETREE"
280
281         # Directories
282         # - INLINE
283         echo "+ inline dir"
284         __populate_create_dir "${SCRATCH_MNT}/S_IFDIR.FMT_INLINE" 1
285
286         # - BLOCK
287         echo "+ block dir"
288         __populate_create_dir "${SCRATCH_MNT}/S_IFDIR.FMT_BLOCK" "$((dblksz / 24))"
289
290         # - HTREE
291         echo "+ htree dir"
292         __populate_create_dir "${SCRATCH_MNT}/S_IFDIR.FMT_HTREE" "$((4 * dblksz / 24))"
293
294         # Symlinks
295         # - FMT_LOCAL
296         echo "+ inline symlink"
297         ln -s target "${SCRATCH_MNT}/S_IFLNK.FMT_LOCAL"
298
299         # - FMT_EXTENTS
300         echo "+ extents symlink"
301         ln -s "$(perl -e 'print "x" x 1023;')" "${SCRATCH_MNT}/S_IFLNK.FMT_EXTENTS"
302
303         # Char & block
304         echo "+ special"
305         mknod "${SCRATCH_MNT}/S_IFCHR" c 1 1
306         mknod "${SCRATCH_MNT}/S_IFBLK" c 1 1
307
308         # Attribute formats
309         # LOCAL
310         echo "+ local attr"
311         __populate_create_attr "${SCRATCH_MNT}/ATTR.FMT_LOCAL" 1
312
313         # BLOCK
314         echo "+ block attr"
315         __populate_create_attr "${SCRATCH_MNT}/ATTR.FMT_BLOCK" "$((blksz / 40))"
316
317         # Make an unused inode
318         echo "+ empty file"
319         touch "${SCRATCH_MNT}/unused"
320         $XFS_IO_PROG -f -c 'fsync' "${SCRATCH_MNT}/unused"
321         rm -rf "${SCRATCH_MNT}/unused"
322
323         # Copy some real files (xfs tests, I guess...)
324         echo "+ real files"
325         test $fill -ne 0 && __populate_fill_fs "${SCRATCH_MNT}" 5
326
327         umount "${SCRATCH_MNT}"
328 }
329
330 # Find the inode number of a file
331 __populate_find_inode() {
332         name="$1"
333         inode="$(stat -c '%i' "${name}")"
334         echo "${inode}"
335 }
336
337 # Check data fork format of XFS file
338 __populate_check_xfs_dformat() {
339         inode="$1"
340         format="$2"
341
342         fmt="$(_scratch_xfs_db -c "inode ${inode}" -c 'p core.format' | sed -e 's/^.*(\([a-z]*\)).*$/\1/g')"
343         test "${format}" = "${fmt}" || _fail "failed to create ino ${inode} dformat expected ${format} saw ${fmt}"
344 }
345
346 # Check attr fork format of XFS file
347 __populate_check_xfs_aformat() {
348         inode="$1"
349         format="$2"
350
351         fmt="$(_scratch_xfs_db -c "inode ${inode}" -c 'p core.aformat' | sed -e 's/^.*(\([a-z]*\)).*$/\1/g')"
352         test "${format}" = "${fmt}" || _fail "failed to create ino ${inode} aformat expected ${format} saw ${fmt}"
353 }
354
355 # Check structure of XFS directory
356 __populate_check_xfs_dir() {
357         inode="$1"
358         dtype="$2"
359
360         (test -n "${leaf_lblk}" && test -n "${node_lblk}") || _fail "must define leaf_lblk and node_lblk before calling __populate_check_xfs_dir"
361         datab=0
362         leafb=0
363         freeb=0
364         #echo "== check dir ${inode} type ${dtype}" ; _scratch_xfs_db -x -c "inode ${inode}" -c "bmap"
365         _scratch_xfs_db -x -c "inode ${inode}" -c "dblock 0" -c "stack" | grep -q 'file data block is unmapped' || datab=1
366         _scratch_xfs_db -x -c "inode ${inode}" -c "dblock ${leaf_lblk}" -c "stack" | grep -q 'file data block is unmapped' || leafb=1
367         _scratch_xfs_db -x -c "inode ${inode}" -c "dblock ${node_lblk}" -c "stack" | grep -q 'file data block is unmapped' || freeb=1
368
369         case "${dtype}" in
370         "shortform"|"inline"|"local")
371                 (test "${datab}" -eq 0 && test "${leafb}" -eq 0 && test "${freeb}" -eq 0) || _fail "failed to create ${dtype} dir ino ${inode} datab ${datab} leafb ${leafb} freeb ${freeb}"
372                 ;;
373         "block")
374                 (test "${datab}" -eq 1 && test "${leafb}" -eq 0 && test "${freeb}" -eq 0) || _fail "failed to create ${dtype} dir ino ${inode} datab ${datab} leafb ${leafb} freeb ${freeb}"
375                 ;;
376         "leaf")
377                 (test "${datab}" -eq 1 && test "${leafb}" -eq 1 && test "${freeb}" -eq 0) || _fail "failed to create ${dtype} dir ino ${inode} datab ${datab} leafb ${leafb} freeb ${freeb}"
378                 ;;
379         "node"|"btree")
380                 (test "${datab}" -eq 1 && test "${leafb}" -eq 1 && test "${freeb}" -eq 1) || _fail "failed to create ${dtype} dir ino ${inode} datab ${datab} leafb ${leafb} freeb ${freeb}"
381                 ;;
382         *)
383                 _fail "Unknown directory type ${dtype}"
384         esac
385 }
386
387 # Check structure of XFS attr
388 __populate_check_xfs_attr() {
389         inode="$1"
390         atype="$2"
391
392         datab=0
393         leafb=0
394         #echo "== check attr ${inode} type ${dtype}" ; _scratch_xfs_db -x -c "inode ${inode}" -c "bmap -a"
395         _scratch_xfs_db -x -c "inode ${inode}" -c "ablock 0" -c "stack" | grep -q 'file attr block is unmapped' || datab=1
396         _scratch_xfs_db -x -c "inode ${inode}" -c "ablock 1" -c "stack" | grep -q 'file attr block is unmapped' || leafb=1
397
398         case "${atype}" in
399         "shortform"|"inline"|"local")
400                 (test "${datab}" -eq 0 && test "${leafb}" -eq 0) || _fail "failed to create ${atype} attr ino ${inode} datab ${datab} leafb ${leafb}"
401                 ;;
402         "leaf")
403                 (test "${datab}" -eq 1 && test "${leafb}" -eq 0) || _fail "failed to create ${atype} attr ino ${inode} datab ${datab} leafb ${leafb}"
404                 ;;
405         "node"|"btree")
406                 (test "${datab}" -eq 1 && test "${leafb}" -eq 1) || _fail "failed to create ${atype} attr ino ${inode} datab ${datab} leafb ${leafb}"
407                 ;;
408         *)
409                 _fail "Unknown attribute type ${atype}"
410         esac
411 }
412
413 # Check that there's at least one per-AG btree with multiple levels
414 __populate_check_xfs_agbtree_height() {
415         bt_type="$1"
416         nr_ags=$(_scratch_xfs_db -c 'sb 0' -c 'p agcount' | awk '{print $3}')
417
418         case "${bt_type}" in
419         "bno"|"cnt"|"rmap"|"refcnt")
420                 hdr="agf"
421                 bt_prefix="${bt_type}"
422                 ;;
423         "ino")
424                 hdr="agi"
425                 bt_prefix=""
426                 ;;
427         "fino")
428                 hdr="agi"
429                 bt_prefix="free_"
430                 ;;
431         *)
432                 _fail "Don't know about AG btree ${bt_type}"
433                 ;;
434         esac
435
436         seq 0 $((nr_ags - 1)) | while read ag; do
437                 bt_level=$(_scratch_xfs_db -c "${hdr} ${ag}" -c "p ${bt_prefix}level" | awk '{print $3}')
438                 if [ "${bt_level}" -gt 1 ]; then
439                         return 100
440                 fi
441         done
442         test $? -eq 100 || _fail "Failed to create ${bt_type} of sufficient height!"
443         return 1
444 }
445
446 # Check that populate created all the types of files we wanted
447 _scratch_xfs_populate_check() {
448         _scratch_mount
449         extents_file="$(__populate_find_inode "${SCRATCH_MNT}/S_IFREG.FMT_EXTENTS")"
450         btree_file="$(__populate_find_inode "${SCRATCH_MNT}/S_IFREG.FMT_BTREE")"
451         inline_dir="$(__populate_find_inode "${SCRATCH_MNT}/S_IFDIR.FMT_INLINE")"
452         block_dir="$(__populate_find_inode "${SCRATCH_MNT}/S_IFDIR.FMT_BLOCK")"
453         leaf_dir="$(__populate_find_inode "${SCRATCH_MNT}/S_IFDIR.FMT_LEAF")"
454         node_dir="$(__populate_find_inode "${SCRATCH_MNT}/S_IFDIR.FMT_NODE")"
455         btree_dir="$(__populate_find_inode "${SCRATCH_MNT}/S_IFDIR.FMT_BTREE")"
456         local_slink="$(__populate_find_inode "${SCRATCH_MNT}/S_IFLNK.FMT_LOCAL")"
457         extents_slink="$(__populate_find_inode "${SCRATCH_MNT}/S_IFLNK.FMT_EXTENTS")"
458         bdev="$(__populate_find_inode "${SCRATCH_MNT}/S_IFBLK")"
459         cdev="$(__populate_find_inode "${SCRATCH_MNT}/S_IFCHR")"
460         local_attr="$(__populate_find_inode "${SCRATCH_MNT}/ATTR.FMT_LOCAL")"
461         leaf_attr="$(__populate_find_inode "${SCRATCH_MNT}/ATTR.FMT_LEAF")"
462         node_attr="$(__populate_find_inode "${SCRATCH_MNT}/ATTR.FMT_NODE")"
463         btree_attr="$(__populate_find_inode "${SCRATCH_MNT}/ATTR.FMT_BTREE")"
464         is_finobt=$(xfs_info "${SCRATCH_MNT}" | grep -c 'finobt=1')
465         is_rmapbt=$(xfs_info "${SCRATCH_MNT}" | grep -c 'rmapbt=1')
466         is_reflink=$(xfs_info "${SCRATCH_MNT}" | grep -c 'reflink=1')
467
468         blksz="$(stat -f -c '%s' "${SCRATCH_MNT}")"
469         dblksz="$(xfs_info "${SCRATCH_MNT}" | grep naming.*bsize | sed -e 's/^.*bsize=//g' -e 's/\([0-9]*\).*$/\1/g')"
470         leaf_lblk="$((32 * 1073741824 / blksz))"
471         node_lblk="$((64 * 1073741824 / blksz))"
472         umount "${SCRATCH_MNT}"
473
474         __populate_check_xfs_dformat "${extents_file}" "extents"
475         __populate_check_xfs_dformat "${btree_file}" "btree"
476         __populate_check_xfs_dir "${inline_dir}" "inline"
477         __populate_check_xfs_dir "${block_dir}" "block"
478         __populate_check_xfs_dir "${leaf_dir}" "leaf"
479         __populate_check_xfs_dir "${node_dir}" "node"
480         __populate_check_xfs_dir "${btree_dir}" "btree"
481         __populate_check_xfs_dformat "${btree_dir}" "btree"
482         __populate_check_xfs_dformat "${bdev}" "dev"
483         __populate_check_xfs_dformat "${cdev}" "dev"
484         __populate_check_xfs_attr "${local_attr}" "local"
485         __populate_check_xfs_attr "${leaf_attr}" "leaf"
486         __populate_check_xfs_attr "${node_attr}" "node"
487         __populate_check_xfs_attr "${btree_attr}" "btree"
488         __populate_check_xfs_aformat "${btree_attr}" "btree"
489         __populate_check_xfs_agbtree_height "bno"
490         __populate_check_xfs_agbtree_height "cnt"
491         test $is_rmapbt -ne 0 && __populate_check_xfs_agbtree_height "rmap"
492         test $is_reflink -ne 0 && __populate_check_xfs_agbtree_height "refcnt"
493 }
494
495 # Check data fork format of ext4 file
496 __populate_check_ext4_dformat() {
497         dev="${SCRATCH_DEV}"
498         inode="$1"
499         format="$2"
500
501         extents=0
502         etree=0
503         debugfs -R "stat <${inode}>" "${dev}" 2> /dev/null | grep 'ETB[0-9]' -q && etree=1
504         iflags="$(debugfs -R "stat <${inode}>" "${dev}" 2> /dev/null | grep 'Flags:' | sed -e 's/^.*Flags: \([0-9a-fx]*\).*$/\1/g')"
505         test "$(echo "${iflags}" | awk '{print and(strtonum($1), 0x80000);}')" -gt 0 && extents=1
506
507         case "${format}" in
508         "blockmap")
509                 test "${extents}" -eq 0 || _fail "failed to create ino ${inode} with blockmap"
510                 ;;
511         "extent"|"extents")
512                 test "${extents}" -eq 1 || _fail "failed to create ino ${inode} with extents"
513                 ;;
514         "etree")
515                 (test "${extents}" -eq 1 && test "${etree}" -eq 1) || _fail "failed to create ino ${inode} with extent tree"
516                 ;;
517         *)
518                 _fail "Unknown dformat ${format}"
519         esac
520 }
521
522 # Check attr fork format of ext4 file
523 __populate_check_ext4_aformat() {
524         dev="${SCRATCH_DEV}"
525         inode="$1"
526         format="$2"
527
528         ablock=1
529         debugfs -R "stat <${inode}>" "${dev}" 2> /dev/null | grep 'File ACL: 0' -q && ablock=0
530
531         case "${format}" in
532         "local"|"inline")
533                 test "${ablock}" -eq 0 || _fail "failed to create inode ${inode} with ${format} xattr"
534                 ;;
535         "block")
536                 test "${extents}" -eq 1 || _fail "failed to create inode ${inode} with ${format} xattr"
537                 ;;
538         *)
539                 _fail "Unknown aformat ${format}"
540         esac
541 }
542
543 # Check structure of ext4 dir
544 __populate_check_ext4_dir() {
545         dev="${SCRATCH_DEV}"
546         inode="$1"
547         dtype="$2"
548
549         htree=0
550         inline=0
551         iflags="$(debugfs -R "stat <${inode}>" "${dev}" 2> /dev/null | grep 'Flags:' | sed -e 's/^.*Flags: \([0-9a-fx]*\).*$/\1/g')"
552         test "$(echo "${iflags}" | awk '{print and(strtonum($1), 0x1000);}')" -gt 0 && htree=1
553         test "$(echo "${iflags}" | awk '{print and(strtonum($1), 0x10000000);}')" -gt 0 && inline=1
554
555         case "${dtype}" in
556         "inline")
557                 (test "${inline}" -eq 1 && test "${htree}" -eq 0) || _fail "failed to create ${dtype} dir ino ${inode} htree ${htree} inline ${inline}"
558                 ;;
559         "block")
560                 (test "${inline}" -eq 0 && test "${htree}" -eq 0) || _fail "failed to create ${dtype} dir ino ${inode} htree ${htree} inline ${inline}"
561                 ;;
562         "htree")
563                 (test "${inline}" -eq 0 && test "${htree}" -eq 1) || _fail "failed to create ${dtype} dir ino ${inode} htree ${htree} inline ${inline}"
564                 ;;
565         *)
566                 _fail "Unknown directory type ${dtype}"
567                 ;;
568         esac
569 }
570
571 # Check that populate created all the types of files we wanted
572 _scratch_ext4_populate_check() {
573         _scratch_mount
574         extents_file="$(__populate_find_inode "${SCRATCH_MNT}/S_IFREG.FMT_EXTENTS")"
575         etree_file="$(__populate_find_inode "${SCRATCH_MNT}/S_IFREG.FMT_ETREE")"
576         block_dir="$(__populate_find_inode "${SCRATCH_MNT}/S_IFDIR.FMT_BLOCK")"
577         htree_dir="$(__populate_find_inode "${SCRATCH_MNT}/S_IFDIR.FMT_HTREE")"
578         extents_slink="$(__populate_find_inode "${SCRATCH_MNT}/S_IFLNK.FMT_EXTENTS")"
579         local_attr="$(__populate_find_inode "${SCRATCH_MNT}/ATTR.FMT_LOCAL")"
580         block_attr="$(__populate_find_inode "${SCRATCH_MNT}/ATTR.FMT_BLOCK")"
581         umount "${SCRATCH_MNT}"
582
583         __populate_check_ext4_dformat "${extents_file}" "extents"
584         __populate_check_ext4_dformat "${etree_file}" "etree"
585         __populate_check_ext4_dir "${block_dir}" "block"
586         __populate_check_ext4_dir "${htree_dir}" "htree"
587         __populate_check_ext4_dformat "${extents_slink}" "extents"
588         __populate_check_ext4_aformat "${local_attr}" "local"
589         __populate_check_ext4_aformat "${block_attr}" "block"
590 }
591
592 # Populate a scratch FS and check the contents to make sure we got that
593 _scratch_populate() {
594         case "${FSTYP}" in
595         "xfs")
596                 _scratch_xfs_populate
597                 _scratch_xfs_populate_check
598                 ;;
599         "ext2"|"ext3"|"ext4")
600                 _scratch_ext4_populate
601                 _scratch_ext4_populate_check
602                 ;;
603         *)
604                 _fail "Don't know how to populate a ${FSTYP} filesystem."
605                 ;;
606         esac
607 }
608
609 # Fill a file system by repeatedly creating files in the given folder
610 # starting with the given file size.  Files are reduced in size when
611 # they can no longer fit until no more files can be created.
612 _fill_fs()
613 {
614         local file_size=$1
615         local dir=$2
616         local block_size=$3
617         local switch_user=$4
618         local file_count=1
619         local bytes_written=0
620         local use_falloc=1;
621
622         if [ $# -ne 4 ]; then
623                 echo "Usage: _fill_fs filesize dir blocksize switch_user"
624                 exit 1
625         fi
626
627         if [ $switch_user -eq 0 ]; then
628                 mkdir -p $dir
629         else
630                 _user_do "mkdir -p $dir"
631         fi
632         if [ ! -d $dir ]; then
633                 return 0;
634         fi
635
636         testio=`$XFS_IO_PROG -F -fc "falloc 0 $block_size" $dir/$$.xfs_io 2>&1`
637         echo $testio | grep -q "not found" && use_falloc=0
638         echo $testio | grep -q "Operation not supported" && use_falloc=0
639
640         if [ $file_size -lt $block_size ]; then
641                 $file_size = $block_size
642         fi
643
644         while [ $file_size -ge $block_size ]; do
645                 bytes_written=0
646                 if [ $switch_user -eq 0 ]; then
647                         if [ $use_falloc -eq 0 ]; then
648                                 $XFS_IO_PROG -fc "pwrite -b 8388608 0 $file_size" \
649                                         $dir/$file_count
650                         else
651                                 $XFS_IO_PROG -fc "falloc 0 $file_size" \
652                                         $dir/$file_count
653                         fi
654                 else
655                         if [ $use_falloc -eq 0 ]; then
656                                 _user_do "$XFS_IO_PROG -f -c \"pwrite -b 8388608 0 \
657                                         $file_size\" $dir/$file_count"
658                         else
659                                 _user_do "$XFS_IO_PROG -f -c \"falloc 0 \
660                                         $file_size\" $dir/$file_count"
661                         fi
662                 fi
663
664                 if [ -f $dir/$file_count ]; then
665                         bytes_written=$(stat -c '%s' $dir/$file_count)
666                 fi
667
668                 # If there was no room to make the file, then divide it in
669                 # half, and keep going
670                 if [ $bytes_written -lt $file_size ]; then
671                         file_size=$((file_size / 2))
672                 fi
673                 file_count=$((file_count + 1))
674         done
675 }