Files
apfstests/common/populate
T
Darrick J. Wong 3ed4ce655d populate: fix leafn creation test for v4 filesystems
The leafn creation test doesn't work on some v4 filesystems
because the field names change.  Rearrange the code somewhat so that it
works properly.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Signed-off-by: Eryu Guan <guaneryu@gmail.com>
2018-11-11 22:05:12 +08:00

810 lines
25 KiB
Plaintext

##/bin/bash
# SPDX-License-Identifier: GPL-2.0+
# Copyright (c) 2015 Oracle. All Rights Reserved.
#
# Routines for populating a scratch fs, and helpers to exercise an FS
# once it's been fuzzed.
. ./common/quota
_require_populate_commands() {
_require_xfs_io_command "falloc"
_require_xfs_io_command "fpunch"
_require_test_program "punch-alternating"
_require_command "$XFS_DB_PROG" "xfs_db"
}
_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 some percentage of the remaining free space
__populate_fill_fs() {
dir="$1"
pct="$2"
test -z "${pct}" && pct=60
mkdir -p "${dir}/test/1"
cp -pRdu "${dir}"/S_IFREG* "${dir}/test/1/"
SRC_SZ="$(du -ks "${dir}/test/1" | cut -f 1)"
FS_SZ="$(( $(stat -f "${dir}" -c '%a * %S') / 1024 ))"
NR="$(( (FS_SZ * ${pct} / 100) / SRC_SZ ))"
echo "FILL FS"
echo "src_sz $SRC_SZ fs_sz $FS_SZ nr $NR"
seq 2 "${NR}" | while read nr; do
cp -pRdu "${dir}/test/1" "${dir}/test/${nr}"
done
}
# For XFS, force on all the quota options if quota is enabled
# and the user didn't feed us noquota.
_populate_xfs_qmount_option()
{
# User explicitly told us not to quota
if echo "${MOUNT_OPTIONS}" | grep -q 'noquota'; then
return
fi
# Don't bother if we can't turn on quotas
if [ ! -f /proc/fs/xfs/xqmstat ]; then
# No quota support
return
elif [ "${USE_EXTERNAL}" = "yes" ] && [ ! -z "${SCRATCH_RTDEV}" ]; then
# Quotas not supported on rt filesystems
return
elif [ -z "${XFS_QUOTA_PROG}" ]; then
# xfs quota tools not installed
return
fi
# Turn on all the quotas
if $XFS_INFO_PROG "${TEST_DIR}" | grep -q 'crc=1'; then
# v5 filesystems can have group & project quotas
quota="usrquota,grpquota,prjquota"
else
# v4 filesystems cannot mix group & project quotas
quota="usrquota,grpquota"
fi
# Inject our quota mount options
if echo "${MOUNT_OPTIONS}" | grep -q "${quota}"; then
return
elif echo "${MOUNT_OPTIONS}" | egrep -q '(quota|noenforce)'; then
_qmount_option "${quota}"
else
export MOUNT_OPTIONS="$MOUNT_OPTIONS -o ${quota}"
echo "MOUNT_OPTIONS = $MOUNT_OPTIONS" >>$seqres.full
fi
}
# Populate an XFS on the scratch device with (we hope) all known
# types of metadata block
_scratch_xfs_populate() {
fill=1
for arg in $@; do
case "${arg}" in
"nofill")
fill=0;;
esac
done
_populate_xfs_qmount_option
_scratch_mount
blksz="$(stat -f -c '%s' "${SCRATCH_MNT}")"
dblksz="$($XFS_INFO_PROG "${SCRATCH_MNT}" | grep naming.*bsize | sed -e 's/^.*bsize=//g' -e 's/\([0-9]*\).*$/\1/g')"
crc="$($XFS_INFO_PROG "${SCRATCH_MNT}" | grep crc= | sed -e 's/^.*crc=//g' -e 's/\([0-9]*\).*$/\1/g')"
if [ $crc -eq 1 ]; then
leaf_hdr_size=64
else
leaf_hdr_size=16
fi
leaf_lblk="$((32 * 1073741824 / blksz))"
node_lblk="$((64 * 1073741824 / blksz))"
# Data:
# Fill up the root inode chunk
echo "+ fill root ino chunk"
seq 1 64 | while read f; do
$XFS_IO_PROG -f -c "truncate 0" "${SCRATCH_MNT}/dummy${f}"
done
# 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"
./src/punch-alternating "${SCRATCH_MNT}/S_IFREG.FMT_BTREE"
# 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))"
# - LEAFN
echo "+ leafn dir"
__populate_create_dir "${SCRATCH_MNT}/S_IFDIR.FMT_LEAFN" "$(( ((dblksz - leaf_hdr_size) / 8) - 3 ))"
# - NODE
echo "+ node dir"
__populate_create_dir "${SCRATCH_MNT}/S_IFDIR.FMT_NODE" "$((16 * dblksz / 40))" true
# - BTREE
echo "+ btree dir"
__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"
mknod "${SCRATCH_MNT}/S_IFCHR" c 1 1
mknod "${SCRATCH_MNT}/S_IFBLK" c 1 1
# special file with an xattr
setfacl -P -m u:nobody:r ${SCRATCH_MNT}/S_IFCHR
# 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
# trusted namespace
touch ${SCRATCH_MNT}/ATTR.TRUSTED
setfattr -n trusted.moo -v urk ${SCRATCH_MNT}/ATTR.TRUSTED
# security namespace
touch ${SCRATCH_MNT}/ATTR.SECURITY
setfattr -n security.foo -v bar ${SCRATCH_MNT}/ATTR.SECURITY
# system namespace
touch ${SCRATCH_MNT}/ATTR.SYSTEM
setfacl -m u:root:r ${SCRATCH_MNT}/ATTR.SYSTEM
# 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 $((blksz - 300))" "${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 ${blksz}" "${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"
# Free space btree
echo "+ freesp btree"
nr="$((blksz * 2 / 8))"
$XFS_IO_PROG -f -c "pwrite -S 0x62 0 $((blksz * nr))" "${SCRATCH_MNT}/BNOBT"
./src/punch-alternating "${SCRATCH_MNT}/BNOBT"
# Reverse-mapping btree
is_rmapbt="$($XFS_INFO_PROG "${SCRATCH_MNT}" | grep -c 'rmapbt=1')"
if [ $is_rmapbt -gt 0 ]; then
echo "+ rmapbt btree"
nr="$((blksz * 2 / 24))"
$XFS_IO_PROG -f -c "pwrite -S 0x62 0 $((blksz * nr))" "${SCRATCH_MNT}/RMAPBT"
./src/punch-alternating "${SCRATCH_MNT}/RMAPBT"
fi
# Realtime Reverse-mapping btree
is_rt="$($XFS_INFO_PROG "${SCRATCH_MNT}" | grep -c 'rtextents=[1-9]')"
if [ $is_rmapbt -gt 0 ] && [ $is_rt -gt 0 ]; then
echo "+ rtrmapbt btree"
nr="$((blksz * 2 / 32))"
$XFS_IO_PROG -f -R -c "pwrite -S 0x62 0 $((blksz * nr))" "${SCRATCH_MNT}/RTRMAPBT"
./src/punch-alternating "${SCRATCH_MNT}/RTRMAPBT"
fi
# Reference-count btree
is_reflink="$($XFS_INFO_PROG "${SCRATCH_MNT}" | grep -c 'reflink=1')"
if [ $is_reflink -gt 0 ]; then
echo "+ reflink btree"
nr="$((blksz * 2 / 12))"
$XFS_IO_PROG -f -c "pwrite -S 0x62 0 $((blksz * nr))" "${SCRATCH_MNT}/REFCOUNTBT"
cp --reflink=always "${SCRATCH_MNT}/REFCOUNTBT" "${SCRATCH_MNT}/REFCOUNTBT2"
./src/punch-alternating "${SCRATCH_MNT}/REFCOUNTBT"
fi
# Copy some real files (xfs tests, I guess...)
echo "+ real files"
test $fill -ne 0 && __populate_fill_fs "${SCRATCH_MNT}" 5
umount "${SCRATCH_MNT}"
}
# Populate an ext4 on the scratch device with (we hope) all known
# types of metadata block
_scratch_ext4_populate() {
fill=1
for arg in $@; do
case "${arg}" in
"nofill")
fill=0;;
esac
done
_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"
./src/punch-alternating "${SCRATCH_MNT}/S_IFREG.FMT_ETREE"
# 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 / 32))"
# - 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"
mknod "${SCRATCH_MNT}/S_IFCHR" c 1 1
mknod "${SCRATCH_MNT}/S_IFBLK" c 1 1
# special file with an xattr
setfacl -P -m u:nobody:r ${SCRATCH_MNT}/S_IFCHR
# Attribute formats
# LOCAL
echo "+ local attr"
__populate_create_attr "${SCRATCH_MNT}/ATTR.FMT_LOCAL" 0
# BLOCK
echo "+ block attr"
__populate_create_attr "${SCRATCH_MNT}/ATTR.FMT_BLOCK" "$((blksz / 40))"
# trusted namespace
touch ${SCRATCH_MNT}/ATTR.TRUSTED
setfattr -n trusted.moo -v urk ${SCRATCH_MNT}/ATTR.TRUSTED
# security namespace
touch ${SCRATCH_MNT}/ATTR.SECURITY
setfattr -n security.foo -v bar ${SCRATCH_MNT}/ATTR.SECURITY
# system namespace
touch ${SCRATCH_MNT}/ATTR.SYSTEM
setfacl -m u:root:r ${SCRATCH_MNT}/ATTR.SYSTEM
# 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"
test $fill -ne 0 && __populate_fill_fs "${SCRATCH_MNT}" 5
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() {
inode="$1"
format="$2"
fmt="$(_scratch_xfs_db -c "inode ${inode}" -c 'p core.format' | 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() {
inode="$1"
format="$2"
fmt="$(_scratch_xfs_db -c "inode ${inode}" -c 'p core.aformat' | 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() {
inode="$1"
dtype="$2"
(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}" ; _scratch_xfs_db -x -c "inode ${inode}" -c "bmap"
_scratch_xfs_db -x -c "inode ${inode}" -c "dblock 0" -c "stack" | grep -q 'file data block is unmapped' || datab=1
_scratch_xfs_db -x -c "inode ${inode}" -c "dblock ${leaf_lblk}" -c "stack" | grep -q 'file data block is unmapped' || leafb=1
_scratch_xfs_db -x -c "inode ${inode}" -c "dblock ${node_lblk}" -c "stack" | 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}"
;;
"leafn")
_scratch_xfs_db -x -c "inode ${inode}" -c "dblock ${leaf_lblk}" -c "p lhdr.info.hdr.magic" | grep -q '0x3dff' && return
_scratch_xfs_db -x -c "inode ${inode}" -c "dblock ${leaf_lblk}" -c "p lhdr.info.magic" | grep -q '0xd2ff' && return
_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() {
inode="$1"
atype="$2"
datab=0
leafb=0
#echo "== check attr ${inode} type ${dtype}" ; _scratch_xfs_db -x -c "inode ${inode}" -c "bmap -a"
_scratch_xfs_db -x -c "inode ${inode}" -c "ablock 0" -c "stack" | grep -q 'file attr block is unmapped' || datab=1
_scratch_xfs_db -x -c "inode ${inode}" -c "ablock 1" -c "stack" | 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 there's at least one per-AG btree with multiple levels
__populate_check_xfs_agbtree_height() {
bt_type="$1"
nr_ags=$(_scratch_xfs_db -c 'sb 0' -c 'p agcount' | awk '{print $3}')
case "${bt_type}" in
"bno"|"cnt"|"rmap"|"refcnt")
hdr="agf"
bt_prefix="${bt_type}"
;;
"ino")
hdr="agi"
bt_prefix=""
;;
"fino")
hdr="agi"
bt_prefix="free_"
;;
*)
_fail "Don't know about AG btree ${bt_type}"
;;
esac
seq 0 $((nr_ags - 1)) | while read ag; do
bt_level=$(_scratch_xfs_db -c "${hdr} ${ag}" -c "p ${bt_prefix}level" | awk '{print $3}')
if [ "${bt_level}" -gt 1 ]; then
return 100
fi
done
test $? -eq 100 || _fail "Failed to create ${bt_type} of sufficient height!"
return 1
}
# 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")"
leafn_dir="$(__populate_find_inode "${SCRATCH_MNT}/S_IFDIR.FMT_LEAFN")"
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")"
is_finobt=$($XFS_INFO_PROG "${SCRATCH_MNT}" | grep -c 'finobt=1')
is_rmapbt=$($XFS_INFO_PROG "${SCRATCH_MNT}" | grep -c 'rmapbt=1')
is_reflink=$($XFS_INFO_PROG "${SCRATCH_MNT}" | grep -c 'reflink=1')
blksz="$(stat -f -c '%s' "${SCRATCH_MNT}")"
dblksz="$($XFS_INFO_PROG "${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 "${extents_file}" "extents"
__populate_check_xfs_dformat "${btree_file}" "btree"
__populate_check_xfs_dir "${inline_dir}" "inline"
__populate_check_xfs_dir "${block_dir}" "block"
__populate_check_xfs_dir "${leaf_dir}" "leaf"
__populate_check_xfs_dir "${leafn_dir}" "leafn"
__populate_check_xfs_dir "${node_dir}" "node"
__populate_check_xfs_dir "${btree_dir}" "btree"
__populate_check_xfs_dformat "${btree_dir}" "btree"
__populate_check_xfs_dformat "${bdev}" "dev"
__populate_check_xfs_dformat "${cdev}" "dev"
__populate_check_xfs_attr "${local_attr}" "local"
__populate_check_xfs_attr "${leaf_attr}" "leaf"
__populate_check_xfs_attr "${node_attr}" "node"
__populate_check_xfs_attr "${btree_attr}" "btree"
__populate_check_xfs_aformat "${btree_attr}" "btree"
__populate_check_xfs_agbtree_height "bno"
__populate_check_xfs_agbtree_height "cnt"
test $is_rmapbt -ne 0 && __populate_check_xfs_agbtree_height "rmap"
test $is_reflink -ne 0 && __populate_check_xfs_agbtree_height "refcnt"
}
# Check data fork format of ext4 file
__populate_check_ext4_dformat() {
dev="${SCRATCH_DEV}"
inode="$1"
format="$2"
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="${SCRATCH_DEV}"
inode="$1"
format="$2"
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="${SCRATCH_DEV}"
inode="$1"
dtype="$2"
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 "${extents_file}" "extents"
__populate_check_ext4_dformat "${etree_file}" "etree"
__populate_check_ext4_dir "${block_dir}" "block"
__populate_check_ext4_dir "${htree_dir}" "htree"
__populate_check_ext4_dformat "${extents_slink}" "extents"
__populate_check_ext4_aformat "${local_attr}" "local"
__populate_check_ext4_aformat "${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
;;
"ext2"|"ext3"|"ext4")
_scratch_ext4_populate
_scratch_ext4_populate_check
;;
*)
_fail "Don't know how to populate a ${FSTYP} filesystem."
;;
esac
}
# Fill a file system by repeatedly creating files in the given folder
# starting with the given file size. Files are reduced in size when
# they can no longer fit until no more files can be created.
_fill_fs()
{
local file_size=$1
local dir=$2
local block_size=$3
local switch_user=$4
local file_count=1
local bytes_written=0
local use_falloc=1;
if [ $# -ne 4 ]; then
echo "Usage: _fill_fs filesize dir blocksize switch_user"
exit 1
fi
if [ $switch_user -eq 0 ]; then
mkdir -p $dir
else
_user_do "mkdir -p $dir"
fi
if [ ! -d $dir ]; then
return 0;
fi
testio=`$XFS_IO_PROG -F -fc "falloc 0 $block_size" $dir/$$.xfs_io 2>&1`
echo $testio | grep -q "not found" && use_falloc=0
echo $testio | grep -q "Operation not supported" && use_falloc=0
if [ $file_size -lt $block_size ]; then
$file_size = $block_size
fi
while [ $file_size -ge $block_size ]; do
bytes_written=0
if [ $switch_user -eq 0 ]; then
if [ $use_falloc -eq 0 ]; then
$XFS_IO_PROG -fc "pwrite -b 8388608 0 $file_size" \
$dir/$file_count
else
$XFS_IO_PROG -fc "falloc 0 $file_size" \
$dir/$file_count
fi
else
if [ $use_falloc -eq 0 ]; then
_user_do "$XFS_IO_PROG -f -c \"pwrite -b 8388608 0 \
$file_size\" $dir/$file_count"
else
_user_do "$XFS_IO_PROG -f -c \"falloc 0 \
$file_size\" $dir/$file_count"
fi
fi
if [ -f $dir/$file_count ]; then
bytes_written=$(stat -c '%s' $dir/$file_count)
fi
# If there was no room to make the file, then divide it in
# half, and keep going
if [ $bytes_written -lt $file_size ]; then
file_size=$((file_size / 2))
fi
file_count=$((file_count + 1))
done
}
# Populate a scratch FS from scratch or from a cached image.
_scratch_populate_cached() {
POPULATE_METADUMP="${TEST_DIR}/__populate.${FSTYP}"
POPULATE_METADUMP_DESCR="${TEST_DIR}/__populate.${FSTYP}.txt"
# Don't keep metadata images cached for more 48 hours...
rm -rf "$(find "${POPULATE_METADUMP}" -mtime +2 2>/dev/null)"
# Throw away cached image if it doesn't match our spec.
case "${FSTYP}" in
"ext4")
extra_descr="LOGDEV ${SCRATCH_LOGDEV} USE_EXTERNAL ${USE_EXTERNAL}"
# ext4 cannot e2image external logs, so we cannot restore
test -n "${SCRATCH_LOGDEV}" && rm -f "${POPULATE_METADUMP}"
;;
"xfs")
extra_descr="LOGDEV ${SCRATCH_LOGDEV} USE_EXTERNAL ${USE_EXTERNAL} RTDEV ${SCRATCH_RTDEV}"
_populate_xfs_qmount_option
if echo "${MOUNT_OPTIONS}" | grep -q 'usrquota'; then
extra_descr="${extra_descr} QUOTAS"
fi
;;
*)
extra_descr="";;
esac
meta_descr="FSTYP ${FSTYP} MKFS_OPTIONS ${MKFS_OPTIONS} SIZE $(blockdev --getsz "${SCRATCH_DEV}") ${extra_descr} ARGS $@"
cmp -s "${POPULATE_METADUMP_DESCR}" <(echo "${meta_descr}") || rm -rf "${POPULATE_METADUMP}"
# Do we have a cached image?
if [ -r "${POPULATE_METADUMP}" ]; then
case "${FSTYP}" in
"xfs")
xfs_mdrestore "${POPULATE_METADUMP}" "${SCRATCH_DEV}" && return
;;
"ext2"|"ext3"|"ext4")
e2image -r "${POPULATE_METADUMP}" "${SCRATCH_DEV}" && return
;;
esac
fi
# Oh well, just create one from scratch
_scratch_mkfs
echo "${meta_descr}" > "${POPULATE_METADUMP_DESCR}"
case "${FSTYP}" in
"xfs")
_scratch_xfs_populate $@
_scratch_xfs_populate_check
_scratch_metadump "${POPULATE_METADUMP}" -a -o
;;
"ext2"|"ext3"|"ext4")
_scratch_ext4_populate $@
_scratch_ext4_populate_check
e2image -Q "${SCRATCH_DEV}" "${POPULATE_METADUMP}"
;;
*)
_fail "Don't know how to populate a ${FSTYP} filesystem."
;;
esac
}