##/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.
-#-----------------------------------------------------------------------
-# 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
-#-----------------------------------------------------------------------
+
. ./common/quota
_require_populate_commands() {
# Attempt to make files of "every" format for data, dirs, attrs etc.
# (with apologies to Eric Sandeen for mutating xfser.sh)
+# Create a file of a given size.
+__populate_create_file() {
+ local sz="$1"
+ local fname="$2"
+
+ $XFS_IO_PROG -f -c "pwrite -S 0x62 -W -b 1m 0 $sz" "${fname}"
+}
+
+# Punch out every other hole in this file, if it exists.
+#
+# The goal here is to force the creation of a large number of metadata records
+# by creating a lot of tiny extent mappings in a file. Callers should ensure
+# that fragmenting the file actually causes record creation. Call this
+# function /after/ creating all other metadata structures.
+__populate_fragment_file() {
+ local fname="$1"
+
+ test -f "${fname}" && $here/src/punch-alternating "${fname}"
+}
+
# Create a large directory
__populate_create_dir() {
name="$1"
fi
# Turn on all the quotas
- if xfs_info "${TEST_DIR}" | grep -q 'crc=1'; then
+ if $XFS_INFO_PROG "${TEST_DIR}" | grep -q 'crc=1'; then
# v5 filesystems can have group & project quotas
quota="usrquota,grpquota,prjquota"
else
_populate_xfs_qmount_option
_scratch_mount
+
+ # We cannot directly force the filesystem to create the metadata
+ # structures we want; we can only achieve this indirectly by carefully
+ # crafting files and a directory tree. Therefore, we must have exact
+ # control over the layout and device selection of all files created.
+ # Clear the rtinherit flag on the root directory so that files are
+ # always created on the data volume regardless of MKFS_OPTIONS. We can
+ # set the realtime flag when needed.
+ $XFS_IO_PROG -c 'chattr -t' $SCRATCH_MNT
+
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')"
+ 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))"
# Regular files
# - FMT_EXTENTS
echo "+ extents file"
- $XFS_IO_PROG -f -c "pwrite -S 0x61 0 ${blksz}" "${SCRATCH_MNT}/S_IFREG.FMT_EXTENTS"
+ __populate_create_file $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"
+ __populate_create_file $((blksz * nr)) "${SCRATCH_MNT}/S_IFREG.FMT_BTREE"
# Directories
# - INLINE
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
# Char & block
echo "+ special"
mknod "${SCRATCH_MNT}/S_IFCHR" c 1 1
- mknod "${SCRATCH_MNT}/S_IFBLK" c 1 1
+ mknod "${SCRATCH_MNT}/S_IFBLK" b 1 1
# special file with an xattr
setfacl -P -m u:nobody:r ${SCRATCH_MNT}/S_IFCHR
# 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"
+ __populate_create_file $((blksz * nr)) "${SCRATCH_MNT}/BNOBT"
+
+ # Inode btree
+ echo "+ inobt btree"
+ local ino_per_rec=64
+ local rec_per_btblock=16
+ local nr="$(( 2 * (blksz / rec_per_btblock) * ino_per_rec ))"
+ local dir="${SCRATCH_MNT}/INOBT"
+ mkdir -p "${dir}"
+ seq 0 "${nr}" | while read f; do
+ touch "${dir}/${f}"
+ done
+
+ seq 0 2 "${nr}" | while read f; do
+ rm -f "${dir}/${f}"
+ done
# Reverse-mapping btree
- is_rmapbt="$(xfs_info "${SCRATCH_MNT}" | grep -c 'rmapbt=1')"
+ 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"
+ __populate_create_file $((blksz * nr)) "${SCRATCH_MNT}/RMAPBT"
fi
# Realtime Reverse-mapping btree
- is_rt="$(xfs_info "${SCRATCH_MNT}" | grep -c 'rtextents=[1-9]')"
+ 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"
+ $XFS_IO_PROG -R -f -c 'truncate 0' "${SCRATCH_MNT}/RTRMAPBT"
+ __populate_create_file $((blksz * nr)) "${SCRATCH_MNT}/RTRMAPBT"
fi
# Reference-count btree
- is_reflink="$(xfs_info "${SCRATCH_MNT}" | grep -c 'reflink=1')"
+ 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"
+ __populate_create_file $((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
+ # Make sure we get all the fragmentation we asked for
+ __populate_fragment_file "${SCRATCH_MNT}/S_IFREG.FMT_BTREE"
+ __populate_fragment_file "${SCRATCH_MNT}/BNOBT"
+ __populate_fragment_file "${SCRATCH_MNT}/RMAPBT"
+ __populate_fragment_file "${SCRATCH_MNT}/RTRMAPBT"
+ __populate_fragment_file "${SCRATCH_MNT}/REFCOUNTBT"
+
umount "${SCRATCH_MNT}"
}
# Regular files
# - FMT_INLINE
echo "+ inline file"
- $XFS_IO_PROG -f -c "pwrite -S 0x61 0 1" "${SCRATCH_MNT}/S_IFREG.FMT_INLINE"
+ __populate_create_file 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"
+ __populate_create_file $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"
+ __populate_create_file $((blksz * nr)) "${SCRATCH_MNT}/S_IFREG.FMT_ETREE"
# Directories
# - INLINE
# Char & block
echo "+ special"
mknod "${SCRATCH_MNT}/S_IFCHR" c 1 1
- mknod "${SCRATCH_MNT}/S_IFBLK" c 1 1
+ mknod "${SCRATCH_MNT}/S_IFBLK" b 1 1
# special file with an xattr
setfacl -P -m u:nobody:r ${SCRATCH_MNT}/S_IFCHR
echo "+ real files"
test $fill -ne 0 && __populate_fill_fs "${SCRATCH_MNT}" 5
+ # Make sure we get all the fragmentation we asked for
+ __populate_fragment_file "${SCRATCH_MNT}/S_IFREG.FMT_ETREE"
+
umount "${SCRATCH_MNT}"
}
"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}"
;;
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")"
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')
+ 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 "${SCRATCH_MNT}" | grep naming.*bsize | sed -e 's/^.*bsize=//g' -e 's/\([0-9]*\).*$/\1/g')"
+ 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_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_aformat "${btree_attr}" "btree"
__populate_check_xfs_agbtree_height "bno"
__populate_check_xfs_agbtree_height "cnt"
+ __populate_check_xfs_agbtree_height "ino"
+ test $is_finobt -ne 0 && __populate_check_xfs_agbtree_height "fino"
test $is_rmapbt -ne 0 && __populate_check_xfs_agbtree_height "rmap"
test $is_reflink -ne 0 && __populate_check_xfs_agbtree_height "refcnt"
}
fi
if [ -f $dir/$file_count ]; then
- bytes_written=$(stat -c '%s' $dir/$file_count)
+ bytes_written=$(_get_filesize $dir/$file_count)
fi
# If there was no room to make the file, then divide it in
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"
+# Compute the fs geometry description of a populated filesystem
+_scratch_populate_cache_tag() {
+ local extra_descr=""
+ local size="$(blockdev --getsz "${SCRATCH_DEV}")"
- # 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}"
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
+ echo "FSTYP ${FSTYP} MKFS_OPTIONS ${MKFS_OPTIONS} SIZE ${size} ${extra_descr} ARGS $@"
+}
+
+# Restore a cached populated fs from a metadata dump
+_scratch_populate_restore_cached() {
+ local metadump="$1"
+
+ # If we're configured for compressed dumps and there isn't already an
+ # uncompressed dump, see if we can use DUMP_COMPRESSOR to decompress
+ # something.
+ if [ -n "$DUMP_COMPRESSOR" ]; then
+ for compr in "$metadump".*; do
+ [ -e "$compr" ] && $DUMP_COMPRESSOR -d -f -k "$compr" && break
+ done
fi
+ test -r "$metadump" || return 1
+
+ case "${FSTYP}" in
+ "xfs")
+ xfs_mdrestore "${metadump}" "${SCRATCH_DEV}" && return 0
+ ;;
+ "ext2"|"ext3"|"ext4")
+ # ext4 cannot e2image external logs, so we cannot restore
+ test -n "${SCRATCH_LOGDEV}" && return 1
+ e2image -r "${metadump}" "${SCRATCH_DEV}" && return 0
+ ;;
+ esac
+ return 1
+}
+
+# Populate a scratch FS from scratch or from a cached image.
+_scratch_populate_cached() {
+ local meta_descr="$(_scratch_populate_cache_tag "$@")"
+ local meta_tag="$(echo "${meta_descr}" | md5sum - | cut -d ' ' -f 1)"
+ local metadump_stem="${TEST_DIR}/__populate.${FSTYP}.${meta_tag}"
+
+ # These variables are shared outside this function
+ POPULATE_METADUMP="${metadump_stem}.metadump"
+ POPULATE_METADUMP_DESCR="${metadump_stem}.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.
+ cmp -s "${POPULATE_METADUMP_DESCR}" <(echo "${meta_descr}") || \
+ rm -rf "${POPULATE_METADUMP}"
+
+ # Try to restore from the metadump
+ _scratch_populate_restore_cached "${POPULATE_METADUMP}" && \
+ return
+
# Oh well, just create one from scratch
_scratch_mkfs
echo "${meta_descr}" > "${POPULATE_METADUMP_DESCR}"
"xfs")
_scratch_xfs_populate $@
_scratch_xfs_populate_check
- _scratch_metadump "${POPULATE_METADUMP}" -a -o
+ _scratch_xfs_metadump "${POPULATE_METADUMP}"
+
+ local logdev=
+ [ "$USE_EXTERNAL" = yes -a ! -z "$SCRATCH_LOGDEV" ] && \
+ logdev=$SCRATCH_LOGDEV
+
+ _xfs_metadump "$POPULATE_METADUMP" "$SCRATCH_DEV" "$logdev" \
+ compress
;;
"ext2"|"ext3"|"ext4")
_scratch_ext4_populate $@
_scratch_ext4_populate_check
- e2image -Q "${SCRATCH_DEV}" "${POPULATE_METADUMP}"
+ _ext4_metadump "${SCRATCH_DEV}" "${POPULATE_METADUMP}" compress
;;
*)
_fail "Don't know how to populate a ${FSTYP} filesystem."