diff mbox

[1/7] common: add routines to fuzz filesystems

Message ID 20150815015207.4333.10934.stgit@birch.djwong.org (mailing list archive)
State New, archived
Headers show

Commit Message

Darrick J. Wong Aug. 15, 2015, 1:52 a.m. UTC
Create common/populate with routines to support the new fuzz tests.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 common/populate |  546 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 546 insertions(+)
 create mode 100644 common/populate



--
To unsubscribe from this list: send the line "unsubscribe fstests" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/common/populate b/common/populate
new file mode 100644
index 0000000..d166c24
--- /dev/null
+++ b/common/populate
@@ -0,0 +1,546 @@ 
+##/bin/bash
+# Routines for populating a scratch fs, and helpers to exercise an FS
+# once it's been fuzzed.
+#-----------------------------------------------------------------------
+#  Copyright (c) 2015 Oracle.  All Rights Reserved.
+#  This program is free software; you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2 of the License, or
+#  (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
+#  USA
+#
+#  Contact information: Silicon Graphics, Inc., 1500 Crittenden Lane,
+#  Mountain View, CA 94043, USA, or: http://www.sgi.com
+#-----------------------------------------------------------------------
+
+_require_xfs_io_command "falloc"
+_require_xfs_io_command "fpunch"
+
+_require_xfs_db_blocktrash_z_command() {
+	test "${FSTYP}" = "xfs" || _notrun "cannot run xfs_db on ${FSTYP}"
+	$XFS_DB_PROG -x -f -c 'blocktrash -z' "${TEST_DEV}" | grep -q 'nothing on stack' || _notrun "blocktrash -z not supported"
+}
+
+# Attempt to make files of "every" format for data, dirs, attrs etc.
+# (with apologies to Eric Sandeen for mutating xfser.sh)
+
+# Create a large directory
+__populate_create_dir() {
+	name="$1"
+	nr="$2"
+	missing="$3"
+
+	mkdir -p "${name}"
+	seq 0 "${nr}" | while read d; do
+		creat=mkdir
+		test "$((d % 20))" -eq 0 && creat=touch
+		$creat "${name}/$(printf "%.08d" "$d")"
+	done
+
+	test -z "${missing}" && return
+	seq 1 2 "${nr}" | while read d; do
+		rm -rf "${name}/$(printf "%.08d" "$d")"
+	done
+}
+
+# Add a bunch of attrs to a file
+__populate_create_attr() {
+	name="$1"
+	nr="$2"
+	missing="$3"
+
+	touch "${name}"
+	seq 0 "${nr}" | while read d; do
+		setfattr -n "user.$(printf "%.08d" "$d")" -v "$(printf "%.08d" "$d")" "${name}"
+	done
+
+	test -z "${missing}" && return
+	seq 1 2 "${nr}" | while read d; do
+		setfattr -x "user.$(printf "%.08d" "$d")" "${name}"
+	done
+}
+
+# Fill up 60% of the remaining free space
+__populate_fill_fs() {
+	dir="$1"
+	pct="$2"
+	test -z "${pct}" && pct=60
+
+	SRC_SZ="$(du -ks "${SRCDIR}" | cut -f 1)"
+	FS_SZ="$(( $(stat -f "${dir}" -c '%a * %S') / 1024 ))"
+
+	NR="$(( (FS_SZ * ${pct} / 100) / SRC_SZ ))"
+	test "${NR}" -lt 1 && NR=1
+
+	seq 1 "${NR}" | while read nr; do
+		cp -pRdu "${SRCDIR}" "${dir}/test.${nr}" >> $seqres.full 2>&1
+	done
+}
+
+# Populate an XFS on the scratch device with (we hope) all known
+# types of metadata block
+_scratch_xfs_populate() {
+	_scratch_mount
+	blksz="$(stat -f -c '%s' "${SCRATCH_MNT}")"
+	dblksz="$(xfs_info "${SCRATCH_MNT}" | grep naming.*bsize | sed -e 's/^.*bsize=//g' -e 's/\([0-9]*\).*$/\1/g')"
+	leaf_lblk="$((32 * 1073741824 / blksz))"
+	node_lblk="$((64 * 1073741824 / blksz))"
+
+	# Data:
+
+	# Regular files
+	# - FMT_EXTENTS
+	echo "+ extents file"
+	$XFS_IO_PROG -f -c "pwrite -S 0x61 0 ${blksz}" "${SCRATCH_MNT}/S_IFREG.FMT_EXTENTS"
+
+	# - FMT_BTREE
+	echo "+ btree extents file"
+	nr="$((blksz * 2 / 16))"
+	$XFS_IO_PROG -f -c "pwrite -S 0x62 0 $((blksz * nr))" "${SCRATCH_MNT}/S_IFREG.FMT_BTREE"
+	for i in $(seq 1 2 ${nr}); do
+		$XFS_IO_PROG -f -c "fpunch $((i * blksz)) ${blksz}" "${SCRATCH_MNT}/S_IFREG.FMT_BTREE"
+	done
+
+	# Directories
+	# - INLINE
+	echo "+ inline dir"
+	__populate_create_dir "${SCRATCH_MNT}/S_IFDIR.FMT_INLINE" 1
+
+	# - BLOCK
+	echo "+ block dir"
+	__populate_create_dir "${SCRATCH_MNT}/S_IFDIR.FMT_BLOCK" "$((dblksz / 40))"
+
+	# - LEAF
+	echo "+ leaf dir"
+	__populate_create_dir "${SCRATCH_MNT}/S_IFDIR.FMT_LEAF" "$((dblksz / 12))"
+
+	# - NODE
+	echo "+ node dir"
+	__populate_create_dir "${SCRATCH_MNT}/S_IFDIR.FMT_NODE" "$((16 * dblksz / 40))" true
+
+	# - BTREE
+	__populate_create_dir "${SCRATCH_MNT}/S_IFDIR.FMT_BTREE" "$((128 * dblksz / 40))" true
+
+	# Symlinks
+	# - FMT_LOCAL
+	echo "+ inline symlink"
+	ln -s target "${SCRATCH_MNT}/S_IFLNK.FMT_LOCAL"
+
+	# - FMT_EXTENTS
+	echo "+ extents symlink"
+	ln -s "$(perl -e 'print "x" x 1023;')" "${SCRATCH_MNT}/S_IFLNK.FMT_EXTENTS"
+
+	# Char & block
+	echo "+ special"
+	mkdir devices
+	mknod "${SCRATCH_MNT}/S_IFCHR" c 1 1
+	mknod "${SCRATCH_MNT}/S_IFBLK" c 1 1
+
+	# Attribute formats
+	# LOCAL
+	echo "+ local attr"
+	__populate_create_attr "${SCRATCH_MNT}/ATTR.FMT_LOCAL" 1
+
+	# LEAF
+	echo "+ leaf attr"
+	__populate_create_attr "${SCRATCH_MNT}/ATTR.FMT_LEAF" "$((blksz / 40))"
+
+	# NODE
+	echo "+ node attr"
+	__populate_create_attr "${SCRATCH_MNT}/ATTR.FMT_NODE" "$((8 * blksz / 40))"
+
+	# BTREE
+	echo "+ btree attr"
+	__populate_create_attr "${SCRATCH_MNT}/ATTR.FMT_BTREE" "$((64 * blksz / 40))" true
+
+	# FMT_EXTENTS with a remote less-than-a-block value
+	echo "+ attr extents with a remote less-than-a-block value"
+	touch "${SCRATCH_MNT}/ATTR.FMT_EXTENTS_REMOTE3K"
+	$XFS_IO_PROG -f -c "pwrite -S 0x43 0 3k" "${SCRATCH_MNT}/attrvalfile" > /dev/null
+	attr -q -s user.remotebtreeattrname "${SCRATCH_MNT}/ATTR.FMT_EXTENTS_REMOTE3K" < "${SCRATCH_MNT}/attrvalfile"
+
+	# FMT_EXTENTS with a remote block-size value
+	echo "+ attr extents with a remote one-block value"
+	touch "${SCRATCH_MNT}/ATTR.FMT_EXTENTS_REMOTE4K"
+	$XFS_IO_PROG -f -c "pwrite -S 0x44 0 4k" "${SCRATCH_MNT}/attrvalfile" > /dev/null
+	attr -q -s user.remotebtreeattrname "${SCRATCH_MNT}/ATTR.FMT_EXTENTS_REMOTE4K" < "${SCRATCH_MNT}/attrvalfile"
+	rm -rf "${SCRATCH_MNT}/attrvalfile"
+
+	# Make an unused inode
+	echo "+ empty file"
+	touch "${SCRATCH_MNT}/unused"
+	$XFS_IO_PROG -f -c 'fsync' "${SCRATCH_MNT}/unused"
+	rm -rf "${SCRATCH_MNT}/unused"
+
+	# Copy some real files (xfs tests, I guess...)
+	echo "+ real files"
+	#__populate_fill_fs "${SCRATCH_MNT}" 40
+	cp -pRdu --reflink=always "${SCRATCH_MNT}/S_IFREG.FMT_BTREE" "${SCRATCH_MNT}/S_IFREG.FMT_BTREE.REFLINK" 2> /dev/null
+
+	umount "${SCRATCH_MNT}"
+}
+
+# Populate an ext4 on the scratch device with (we hope) all known
+# types of metadata block
+_scratch_ext4_populate() {
+	_scratch_mount
+	blksz="$(stat -f -c '%s' "${SCRATCH_MNT}")"
+	dblksz="${blksz}"
+	leaf_lblk="$((32 * 1073741824 / blksz))"
+	node_lblk="$((64 * 1073741824 / blksz))"
+
+	# Data:
+
+	# Regular files
+	# - FMT_INLINE
+	echo "+ inline file"
+	$XFS_IO_PROG -f -c "pwrite -S 0x61 0 1" "${SCRATCH_MNT}/S_IFREG.FMT_INLINE"
+
+	# - FMT_EXTENTS
+	echo "+ extents file"
+	$XFS_IO_PROG -f -c "pwrite -S 0x61 0 ${blksz}" "${SCRATCH_MNT}/S_IFREG.FMT_EXTENTS"
+
+	# - FMT_ETREE
+	echo "+ extent tree file"
+	nr="$((blksz * 2 / 12))"
+	$XFS_IO_PROG -f -c "pwrite -S 0x62 0 $((blksz * nr))" "${SCRATCH_MNT}/S_IFREG.FMT_ETREE"
+	for i in $(seq 1 2 ${nr}); do
+		$XFS_IO_PROG -f -c "fpunch $((i * blksz)) ${blksz}" "${SCRATCH_MNT}/S_IFREG.FMT_ETREE"
+	done
+
+	# Directories
+	# - INLINE
+	echo "+ inline dir"
+	__populate_create_dir "${SCRATCH_MNT}/S_IFDIR.FMT_INLINE" 1
+
+	# - BLOCK
+	echo "+ block dir"
+	__populate_create_dir "${SCRATCH_MNT}/S_IFDIR.FMT_BLOCK" "$((dblksz / 24))"
+
+	# - HTREE
+	echo "+ htree dir"
+	__populate_create_dir "${SCRATCH_MNT}/S_IFDIR.FMT_HTREE" "$((4 * dblksz / 24))"
+
+	# Symlinks
+	# - FMT_LOCAL
+	echo "+ inline symlink"
+	ln -s target "${SCRATCH_MNT}/S_IFLNK.FMT_LOCAL"
+
+	# - FMT_EXTENTS
+	echo "+ extents symlink"
+	ln -s "$(perl -e 'print "x" x 1023;')" "${SCRATCH_MNT}/S_IFLNK.FMT_EXTENTS"
+
+	# Char & block
+	echo "+ special"
+	mkdir devices
+	mknod "${SCRATCH_MNT}/S_IFCHR" c 1 1
+	mknod "${SCRATCH_MNT}/S_IFBLK" c 1 1
+
+	# Attribute formats
+	# LOCAL
+	echo "+ local attr"
+	__populate_create_attr "${SCRATCH_MNT}/ATTR.FMT_LOCAL" 1
+
+	# BLOCK
+	echo "+ block attr"
+	__populate_create_attr "${SCRATCH_MNT}/ATTR.FMT_BLOCK" "$((blksz / 40))"
+
+	# Make an unused inode
+	echo "+ empty file"
+	touch "${SCRATCH_MNT}/unused"
+	$XFS_IO_PROG -f -c 'fsync' "${SCRATCH_MNT}/unused"
+	rm -rf "${SCRATCH_MNT}/unused"
+
+	# Copy some real files (xfs tests, I guess...)
+	echo "+ real files"
+	__populate_fill_fs "${SCRATCH_MNT}"
+	cp -pRdu --reflink=always "${SCRATCH_MNT}/S_IFREG.FMT_ETREE" "${SCRATCH_MNT}/S_IREG.FMT_ETREE.REFLINK" 2> /dev/null
+
+	umount "${SCRATCH_MNT}"
+}
+
+# Find the inode number of a file
+__populate_find_inode() {
+	name="$1"
+	inode="$(stat -c '%i' "${name}")"
+	echo "${inode}"
+}
+
+# Check data fork format of XFS file
+__populate_check_xfs_dformat() {
+	dev="$1"
+	inode="$2"
+	format="$3"
+
+	fmt="$($XFS_DB_PROG -c "inode ${inode}" -c 'p core.format' "${dev}" | sed -e 's/^.*(\([a-z]*\)).*$/\1/g')"
+	test "${format}" = "${fmt}" || _fail "failed to create ino ${inode} dformat expected ${format} saw ${fmt}"
+}
+
+# Check attr fork format of XFS file
+__populate_check_xfs_aformat() {
+	dev="$1"
+	inode="$2"
+	format="$3"
+
+	fmt="$($XFS_DB_PROG -c "inode ${inode}" -c 'p core.aformat' "${dev}" | sed -e 's/^.*(\([a-z]*\)).*$/\1/g')"
+	test "${format}" = "${fmt}" || _fail "failed to create ino ${inode} aformat expected ${format} saw ${fmt}"
+}
+
+# Check structure of XFS directory
+__populate_check_xfs_dir() {
+	dev="$1"
+	inode="$2"
+	dtype="$3"
+
+	(test -n "${leaf_lblk}" && test -n "${node_lblk}") || _fail "must define leaf_lblk and node_lblk before calling __populate_check_xfs_dir"
+	datab=0
+	leafb=0
+	freeb=0
+	#echo "== check dir ${inode} type ${dtype}" ; $XFS_DB_PROG -x -c "inode ${inode}" -c "bmap" "${SCRATCH_DEV}"
+	$XFS_DB_PROG -x -c "inode ${inode}" -c "dblock 0" -c "stack" "${SCRATCH_DEV}" | grep -q 'file data block is unmapped' || datab=1
+	$XFS_DB_PROG -x -c "inode ${inode}" -c "dblock ${leaf_lblk}" -c "stack" "${SCRATCH_DEV}" | grep -q 'file data block is unmapped' || leafb=1
+	$XFS_DB_PROG -x -c "inode ${inode}" -c "dblock ${node_lblk}" -c "stack" "${SCRATCH_DEV}" | grep -q 'file data block is unmapped' || freeb=1
+
+	case "${dtype}" in
+	"shortform"|"inline"|"local")
+		(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}"
+		;;
+	"block")
+		(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}"
+		;;
+	"leaf")
+		(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}"
+		;;
+	"node"|"btree")
+		(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}"
+		;;
+	*)
+		_fail "Unknown directory type ${dtype}"
+	esac
+}
+
+# Check structure of XFS attr
+__populate_check_xfs_attr() {
+	dev="$1"
+	inode="$2"
+	atype="$3"
+
+	datab=0
+	leafb=0
+	#echo "== check attr ${inode} type ${dtype}" ; $XFS_DB_PROG -x -c "inode ${inode}" -c "bmap -a" "${SCRATCH_DEV}"
+	$XFS_DB_PROG -x -c "inode ${inode}" -c "ablock 0" -c "stack" "${SCRATCH_DEV}" | grep -q 'file attr block is unmapped' || datab=1
+	$XFS_DB_PROG -x -c "inode ${inode}" -c "ablock 1" -c "stack" "${SCRATCH_DEV}" | grep -q 'file attr block is unmapped' || leafb=1
+
+	case "${atype}" in
+	"shortform"|"inline"|"local")
+		(test "${datab}" -eq 0 && test "${leafb}" -eq 0) || _fail "failed to create ${atype} attr ino ${inode} datab ${datab} leafb ${leafb}"
+		;;
+	"leaf")
+		(test "${datab}" -eq 1 && test "${leafb}" -eq 0) || _fail "failed to create ${atype} attr ino ${inode} datab ${datab} leafb ${leafb}"
+		;;
+	"node"|"btree")
+		(test "${datab}" -eq 1 && test "${leafb}" -eq 1) || _fail "failed to create ${atype} attr ino ${inode} datab ${datab} leafb ${leafb}"
+		;;
+	*)
+		_fail "Unknown attribute type ${atype}"
+	esac
+}
+
+# Check that populate created all the types of files we wanted
+_scratch_xfs_populate_check() {
+	_scratch_mount
+	extents_file="$(__populate_find_inode "${SCRATCH_MNT}/S_IFREG.FMT_EXTENTS")"
+	btree_file="$(__populate_find_inode "${SCRATCH_MNT}/S_IFREG.FMT_BTREE")"
+	inline_dir="$(__populate_find_inode "${SCRATCH_MNT}/S_IFDIR.FMT_INLINE")"
+	block_dir="$(__populate_find_inode "${SCRATCH_MNT}/S_IFDIR.FMT_BLOCK")"
+	leaf_dir="$(__populate_find_inode "${SCRATCH_MNT}/S_IFDIR.FMT_LEAF")"
+	node_dir="$(__populate_find_inode "${SCRATCH_MNT}/S_IFDIR.FMT_NODE")"
+	btree_dir="$(__populate_find_inode "${SCRATCH_MNT}/S_IFDIR.FMT_BTREE")"
+	local_slink="$(__populate_find_inode "${SCRATCH_MNT}/S_IFLNK.FMT_LOCAL")"
+	extents_slink="$(__populate_find_inode "${SCRATCH_MNT}/S_IFLNK.FMT_EXTENTS")"
+	bdev="$(__populate_find_inode "${SCRATCH_MNT}/S_IFBLK")"
+	cdev="$(__populate_find_inode "${SCRATCH_MNT}/S_IFCHR")"
+	local_attr="$(__populate_find_inode "${SCRATCH_MNT}/ATTR.FMT_LOCAL")"
+	leaf_attr="$(__populate_find_inode "${SCRATCH_MNT}/ATTR.FMT_LEAF")"
+	node_attr="$(__populate_find_inode "${SCRATCH_MNT}/ATTR.FMT_NODE")"
+	btree_attr="$(__populate_find_inode "${SCRATCH_MNT}/ATTR.FMT_BTREE")"
+
+	blksz="$(stat -f -c '%s' "${SCRATCH_MNT}")"
+	dblksz="$(xfs_info "${SCRATCH_MNT}" | grep naming.*bsize | sed -e 's/^.*bsize=//g' -e 's/\([0-9]*\).*$/\1/g')"
+	leaf_lblk="$((32 * 1073741824 / blksz))"
+	node_lblk="$((64 * 1073741824 / blksz))"
+	umount "${SCRATCH_MNT}"
+
+	__populate_check_xfs_dformat "${SCRATCH_DEV}" "${extents_file}" "extents"
+	__populate_check_xfs_dformat "${SCRATCH_DEV}" "${btree_file}" "btree"
+	__populate_check_xfs_dir "${SCRATCH_DEV}" "${inline_dir}" "inline"
+	__populate_check_xfs_dir "${SCRATCH_DEV}" "${block_dir}" "block"
+	__populate_check_xfs_dir "${SCRATCH_DEV}" "${leaf_dir}" "leaf"
+	__populate_check_xfs_dir "${SCRATCH_DEV}" "${node_dir}" "node"
+	__populate_check_xfs_dir "${SCRATCH_DEV}" "${btree_dir}" "btree"
+	__populate_check_xfs_dformat "${SCRATCH_DEV}" "${btree_dir}" "btree"
+	__populate_check_xfs_dformat "${SCRATCH_DEV}" "${bdev}" "dev"
+	__populate_check_xfs_dformat "${SCRATCH_DEV}" "${cdev}" "dev"
+	__populate_check_xfs_attr "${SCRATCH_DEV}" "${local_attr}" "local"
+	__populate_check_xfs_attr "${SCRATCH_DEV}" "${leaf_attr}" "leaf"
+	__populate_check_xfs_attr "${SCRATCH_DEV}" "${node_attr}" "node"
+	__populate_check_xfs_attr "${SCRATCH_DEV}" "${btree_attr}" "btree"
+	__populate_check_xfs_aformat "${SCRATCH_DEV}" "${btree_attr}" "btree"
+}
+
+# Check data fork format of ext4 file
+__populate_check_ext4_dformat() {
+	dev="$1"
+	inode="$2"
+	format="$3"
+
+	extents=0
+	etree=0
+	debugfs -R "stat <${inode}>" "${dev}" 2> /dev/null | grep 'ETB[0-9]' -q && etree=1
+	iflags="$(debugfs -R "stat <${inode}>" "${dev}" 2> /dev/null | grep 'Flags:' | sed -e 's/^.*Flags: \([0-9a-fx]*\).*$/\1/g')"
+	test "$(echo "${iflags}" | awk '{print and(strtonum($1), 0x80000);}')" -gt 0 && extents=1
+
+	case "${format}" in
+	"blockmap")
+		test "${extents}" -eq 0 || _fail "failed to create ino ${inode} with blockmap"
+		;;
+	"extent"|"extents")
+		test "${extents}" -eq 1 || _fail "failed to create ino ${inode} with extents"
+		;;
+	"etree")
+		(test "${extents}" -eq 1 && test "${etree}" -eq 1) || _fail "failed to create ino ${inode} with extent tree"
+		;;
+	*)
+		_fail "Unknown dformat ${format}"
+	esac
+}
+
+# Check attr fork format of ext4 file
+__populate_check_ext4_aformat() {
+	dev="$1"
+	inode="$2"
+	format="$3"
+
+	ablock=1
+	debugfs -R "stat <${inode}>" "${dev}" 2> /dev/null | grep 'File ACL: 0' -q && ablock=0
+
+	case "${format}" in
+	"local"|"inline")
+		test "${ablock}" -eq 0 || _fail "failed to create inode ${inode} with ${format} xattr"
+		;;
+	"block")
+		test "${extents}" -eq 1 || _fail "failed to create inode ${inode} with ${format} xattr"
+		;;
+	*)
+		_fail "Unknown aformat ${format}"
+	esac
+}
+
+# Check structure of ext4 dir
+__populate_check_ext4_dir() {
+	dev="$1"
+	inode="$2"
+	dtype="$3"
+
+	htree=0
+	inline=0
+	iflags="$(debugfs -R "stat <${inode}>" "${dev}" 2> /dev/null | grep 'Flags:' | sed -e 's/^.*Flags: \([0-9a-fx]*\).*$/\1/g')"
+	test "$(echo "${iflags}" | awk '{print and(strtonum($1), 0x1000);}')" -gt 0 && htree=1
+	test "$(echo "${iflags}" | awk '{print and(strtonum($1), 0x10000000);}')" -gt 0 && inline=1
+
+	case "${dtype}" in
+	"inline")
+		(test "${inline}" -eq 1 && test "${htree}" -eq 0) || _fail "failed to create ${dtype} dir ino ${inode} htree ${htree} inline ${inline}"
+		;;
+	"block")
+		(test "${inline}" -eq 0 && test "${htree}" -eq 0) || _fail "failed to create ${dtype} dir ino ${inode} htree ${htree} inline ${inline}"
+		;;
+	"htree")
+		(test "${inline}" -eq 0 && test "${htree}" -eq 1) || _fail "failed to create ${dtype} dir ino ${inode} htree ${htree} inline ${inline}"
+		;;
+	*)
+		_fail "Unknown directory type ${dtype}"
+		;;
+	esac
+}
+
+# Check that populate created all the types of files we wanted
+_scratch_ext4_populate_check() {
+	_scratch_mount
+	extents_file="$(__populate_find_inode "${SCRATCH_MNT}/S_IFREG.FMT_EXTENTS")"
+	etree_file="$(__populate_find_inode "${SCRATCH_MNT}/S_IFREG.FMT_ETREE")"
+	block_dir="$(__populate_find_inode "${SCRATCH_MNT}/S_IFDIR.FMT_BLOCK")"
+	htree_dir="$(__populate_find_inode "${SCRATCH_MNT}/S_IFDIR.FMT_HTREE")"
+	extents_slink="$(__populate_find_inode "${SCRATCH_MNT}/S_IFLNK.FMT_EXTENTS")"
+	local_attr="$(__populate_find_inode "${SCRATCH_MNT}/ATTR.FMT_LOCAL")"
+	block_attr="$(__populate_find_inode "${SCRATCH_MNT}/ATTR.FMT_BLOCK")"
+	umount "${SCRATCH_MNT}"
+
+	__populate_check_ext4_dformat "${SCRATCH_DEV}" "${extents_file}" "extents"
+	__populate_check_ext4_dformat "${SCRATCH_DEV}" "${etree_file}" "etree"
+	__populate_check_ext4_dir "${SCRATCH_DEV}" "${block_dir}" "block"
+	__populate_check_ext4_dir "${SCRATCH_DEV}" "${htree_dir}" "htree"
+	__populate_check_ext4_dformat "${SCRATCH_DEV}" "${extents_slink}" "extents"
+	__populate_check_ext4_aformat "${SCRATCH_DEV}" "${local_attr}" "local"
+	__populate_check_ext4_aformat "${SCRATCH_DEV}" "${block_attr}" "block"
+}
+
+# Populate a scratch FS and check the contents to make sure we got that
+_scratch_populate() {
+	case "${FSTYP}" in
+	"xfs")
+		_scratch_xfs_populate
+		_scratch_xfs_populate_check
+		;;
+	"ext4")
+		_scratch_ext4_populate
+		_scratch_ext4_populate_check
+		;;
+	*)
+		_fail "Don't know how to populate a ${FSTYP} filesystem."
+		;;
+	esac
+}
+
+# Modify various files after a fuzzing operation
+_scratch_fuzz_modify() {
+	nr="$1"
+
+	test -z "${nr}" && nr=50000
+	echo "+++ touch ${nr} files"
+	$XFS_IO_PROG -f -c "pwrite -S 0x63 0 ${BLK_SZ}" "/tmp/afile" > /dev/null
+	date="$(date)"
+	find "${SCRATCH_MNT}/" -type f 2> /dev/null | head -n "${nr}" | while read f; do
+		setfattr -n "user.date" -v "${date}" "$f"
+		cat "/tmp/afile" >> "$f"
+		mv "$f" "$f.longer"
+	done
+	rm -rf "/tmp/afile"
+
+	echo "+++ create files"
+	cp -pRdu "${SRCDIR}" "${SCRATCH_MNT}/test.moo"
+	sync
+
+	echo "+++ remove files"
+	rm -rf "${SCRATCH_MNT}/test.moo"
+	rm -rf "${SCRATCH_MNT}/test.1"
+}
+
+# Try to access files after fuzzing
+_scratch_fuzz_test() {
+	echo "+++ ls -laR" >> $seqres.full
+	ls -laR "${SCRATCH_MNT}/test.1/" >/dev/null 2>&1
+
+	echo "+++ cat files" >> $seqres.full
+	(find "${SCRATCH_MNT}/test.1/" -type f -size -1048576k -print0 | xargs -0 cat) >/dev/null 2>&1
+}
+