mirror of
https://github.com/linux-apfs/apfstests.git
synced 2026-05-01 15:01:44 -07:00
898e0cf2a4
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com> Reviewed-by: Eryu Guan <eguan@redhat.com> Signed-off-by: Eryu Guan <eguan@redhat.com>
750 lines
23 KiB
Plaintext
750 lines
23 KiB
Plaintext
##/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_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
|
|
}
|
|
|
|
# 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
|
|
|
|
_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:
|
|
|
|
# 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))"
|
|
|
|
# - 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 "${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 "${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 "${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 / 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"
|
|
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
|
|
|
|
# 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}"
|
|
;;
|
|
"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")"
|
|
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 "${SCRATCH_MNT}" | grep -c 'finobt=1')
|
|
is_rmapbt=$(xfs_info "${SCRATCH_MNT}" | grep -c 'rmapbt=1')
|
|
is_reflink=$(xfs_info "${SCRATCH_MNT}" | grep -c 'reflink=1')
|
|
|
|
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 "${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 "${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.
|
|
meta_descr="FSTYP ${FSTYP} MKFS_OPTIONS ${MKFS_OPTIONS} SIZE $(blockdev --getsz "${SCRATCH_DEV}") 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
|
|
}
|