/src/deduperace
/src/detached_mounts_propagation
/src/devzero
+/src/dio-buf-fault
/src/dio-interleaved
/src/dio-invalidate-cache
/src/dirhash_collide
/src/dirstress
/src/e4compact
/src/ext4_resize
+/src/fake-dump-rootino
/src/fault
/src/feature
/src/fiemap-tester
/src/punch-alternating
/src/pwrite_mmap_blocked
/src/randholes
+/src/readdir-while-renames
/src/rename
/src/renameat2
/src/resvtest
+/src/rewinddir-test
/src/runas
/src/seek_copy_test
/src/seek_sanity_test
/src/t_holes
/src/t_immutable
/src/t_mmap_collision
+/src/t_mmap_cow_memory_failure
/src/t_mmap_cow_race
/src/t_mmap_dio
/src/t_mmap_fallocate
/src/t_readdir_1
/src/t_readdir_2
/src/t_readdir_3
+/src/t_reflink_read_race
/src/t_rename_overwrite
+/src/t_snapshot_deleted_subvolume
/src/t_stripealign
/src/t_truncate_cmtime
/src/t_truncate_self
/src/unwritten_sync
/src/uring_read_fault
/src/usemem
+/src/uuid_ioctl
/src/writemod
/src/writev_on_pagefault
/src/xfsctl
+/src/xfsfind
/src/aio-dio-regress/aio-dio-append-write-fallocate-race
/src/aio-dio-regress/aio-dio-append-write-read-race
/src/aio-dio-regress/aio-dio-cow-race
/src/vfs/mount-idmapped
/src/log-writes/replay-log
/src/perf/*.pyc
+/src/fiemap-fault
# Symlinked files
/tests/generic/035.out
/tests/xfs/033.out
/tests/xfs/071.out
/tests/xfs/096.out
+/tests/xfs/216.out
# cscope files
cscope.*
--- /dev/null
+List of reviewers, co-maintainers and how to submit fstests changes
+====================================================
+
+Please try to follow the guidelines below. This will make things
+easier on the maintainers. Not all of these guidelines matter for every
+trivial patch so apply some common sense.
+
+Tips for patch submitters
+-------------------------
+
+1. Always *test* your changes, however small, on at least 4 or
+ 5 people, preferably many more.
+
+2. Make sure your changes compile correctly in multiple
+ configurations. In particular check that changes don't break
+ fstests basic running.
+
+3. When you are happy with a change make it generally available for
+ testing and await feedback.
+
+4. Make a patch available to fstests@ list directly, that's the only
+ one mailing list which maintain the whole fstests project.
+
+ PLEASE CC: the relevant reviewers, co-maintainers and mailing lists
+ that are generated by ``tools/get_maintainer.pl.``
+
+ PLEASE try to include any credit lines you want added with the
+ patch. It avoids people being missed off by mistake and makes
+ it easier to know who wants adding and who doesn't.
+
+ PLEASE document known bugs. If it doesn't work for everything
+ or does something very odd once a month document it.
+
+5. Make sure you have the right to send any changes you make. If you
+ do changes at work you may find your employer owns the patch
+ not you.
+
+6. Happy hacking.
+
+Descriptions of section entries and preferred order
+---------------------------------------------------
+
+ M: *Mail* patches to: FullName <address@domain>
+ These people might be a co-maintainer (with Supported status) or
+ maintainer (with Maintained status).
+ R: Designated *Reviewer*: FullName <address@domain>
+ These reviewers should be CCed on patches.
+ L: Besides fstests@ list itself, this *Mailing list* is relevant to
+ this area, should be CCed.
+ S: *Status*, one of the following (note: all things are maintained by
+ fstests@vger.kernel.org):
+ Supported: Someone is actually paid to look after this.
+ Maintained: Someone actually looks after it, has the privilege to
+ merge & push.
+ Odd Fixes: It has a maintainer but they don't have time to do
+ much other than throw the odd patch in. See below..
+ Orphan: No current maintainer [but maybe you could take the
+ role as you write your new code].
+ Obsolete: Old code. Something tagged obsolete generally means
+ it has been replaced by a better system and you
+ should be using that.
+ W: *Web-page* with status/info
+ Q: *Patchwork* web based patch tracking system site
+ B: URI for where to file *bugs*. A web-page with detailed bug
+ filing info, a direct bug tracker link, or a mailto: URI.
+ C: URI for *chat* protocol, server and channel where developers
+ usually hang out, for example irc://server/channel.
+ P: Subsystem Profile document for more details submitting
+ patches to the given subsystem. This is either an in-tree file,
+ or a URI.
+ T: *SCM* tree type and location.
+ Type is one of: git, hg, quilt, stgit, topgit
+ F: *Files* and directories wildcard patterns.
+ A trailing slash includes all files and subdirectory files.
+ F: tests/xfs/ all files in and below tests/xfs
+ F: tests/generic/* all files in tests/generic, but not below
+ F: */ext4/* all files in "any top level directory"/ext4
+ One pattern per line. Multiple F: lines acceptable.
+ X: *Excluded* files and directories that are NOT maintained, same
+ rules as F:. Files exclusions are tested before file matches.
+ Can be useful for excluding a specific subdirectory, for instance:
+ F: src/
+ X: src/vfs
+ matches all files in and below net excluding net/ipv6/
+ N: Files and directories *Regex* patterns.
+ N: [^a-z]tegra all files whose path contains tegra
+ (not including files like integrator)
+ One pattern per line. Multiple N: lines acceptable.
+ tools/get_maintainer.pl has different behavior for files that
+ match F: pattern and matches of N: patterns. By default,
+ get_maintainer will not look at git log history when an F: pattern
+ match occurs. When an N: match occurs, git log history is used
+ to also notify the people that have git commit signatures.
+ K: *Content regex* (perl extended) pattern match in a patch or file.
+ For instance:
+ K: of_get_profile
+ matches patches or files that contain "of_get_profile"
+ K: \b(printk|pr_(info|err))\b
+ matches patches or files that contain one or more of the words
+ printk, pr_info or pr_err
+ One regex pattern per line. Multiple K: lines acceptable.
+
+Maintainers List
+----------------
+
+.. note:: The whole fstests are maintained by fstests@vger.kernel.org, so you
+ should send patch to fstests@ at least. Other relevant mailing list
+ or reviewer or co-maintainer can be in cc list.
+
+BTRFS
+M: Anand Jain <anand.jain@oracle.com>
+R: Filipe Manana <fdmanana@suse.com>
+L: linux-btrfs@vger.kernel.org
+S: Supported
+F: tests/btrfs/
+F: common/btrfs
+
+CEPH
+L: ceph-devel@vger.kernel.org
+S: Supported
+F: tests/ceph/
+F: common/ceph
+
+CIFS
+L: linux-cifs@vger.kernel.org
+S: Supported
+F: tests/cifs
+
+EXT4
+L: linux-ext4@vger.kernel.org
+S: Supported
+F: tests/ext4/
+F: common/ext4
+
+F2FS
+L: linux-f2fs-devel@lists.sourceforge.net
+S: Supported
+F: tests/f2fs/
+F: common/f2fs
+
+FSVERITY
+R: Eric Biggers <ebiggers@google.com>
+L: fsverity@lists.linux.dev
+S: Supported
+F: common/verity
+
+FSCRYPT
+R: Eric Biggers <ebiggers@google.com>
+L: linux-fscrypt@vger.kernel.org
+S: Supported
+F: common/encrypt
+
+NFS
+L: linux-nfs@vger.kernel.org
+S: Supported
+F: tests/nfs/
+F: common/nfs
+
+OCFS2
+L: ocfs2-devel@oss.oracle.com
+S: Supported
+F: tests/ocfs2/
+
+OVERLAYFS
+R: Amir Goldstein <amir73il@gmail.com>
+L: linux-unionfs@vger.kernel.org
+S: Supported
+F: tests/overlay
+F: common/overlay
+
+UDF
+R: Jan Kara <jack@suse.com>
+S: Supported
+F: tests/udf/
+
+VFS
+R: Christian Brauner <brauner@kernel.org>
+L: linux-fsdevel@vger.kernel.org
+S: Supported
+F: src/vfs/
+
+XFS
+R: Darrick J. Wong <djwong@kernel.org>
+L: linux-xfs@vger.kernel.org
+S: Supported
+F: common/dump
+F: common/fuzzy
+F: common/inject
+F: common/populate
+F: common/repair
+F: common/xfs
+F: tests/xfs/
+
+ALL
+M: Zorro Lang <zlang@kernel.org>
+L: fstests@vger.kernel.org
+S: Maintained
+T: git git://git.kernel.org/pub/scm/fs/xfs/xfstests-dev.git
+F: *
+F: */
$ sudo apt-get install acl attr automake bc dbench dump e2fsprogs fio gawk \
gcc git indent libacl1-dev libaio-dev libcap-dev libgdbm-dev libtool \
libtool-bin liburing-dev libuuid1 lvm2 make psmisc python3 quota sed \
- uuid-dev uuid-runtime xfsprogs linux-headers-$(uname -r) sqlite3
+ uuid-dev uuid-runtime xfsprogs linux-headers-$(uname -r) sqlite3 \
+ libgdbm-compat-dev
2. Install packages for the filesystem(s) being tested:
$ sudo apt-get install exfatprogs f2fs-tools ocfs2-tools udftools xfsdump \
xfslibs-dev
- For OverlayFS install:
- - see https://github.com/hisilicon/overlayfs-progs
-
Fedora
------
$ sudo yum install btrfs-progs exfatprogs f2fs-tools ocfs2-tools xfsdump \
xfsprogs-devel
- For OverlayFS build and install:
- - see https://github.com/hisilicon/overlayfs-progs
-
RHEL or CentOS
--------------
For ocfs2 build and install:
- see https://github.com/markfasheh/ocfs2-tools
- For OverlayFS build and install:
- - see https://github.com/hisilicon/overlayfs-progs
+SUSE Linux Enterprise or openSUSE
+---------------------------------
+
+1. Install all necessary packages from standard repositories:
+
+ $ sudo zypper install acct automake bc dbench duperemove dump fio gcc git \
+ indent libacl-devel libaio-devel libattr-devel libcap libcap-devel \
+ libtool liburing-devel libuuid-devel lvm2 make quota sqlite3 xfsprogs
+
+2. Install packages for the filesystem(s) being tested:
+
+ For btrfs install:
+ $ sudo zypper install btrfsprogs libbtrfs-devel
+
+ For XFS install:
+ $ sudo zypper install xfsdump xfsprogs-devel
Build and install test, libs and utils
--------------------------------------
https://www.lscdweb.com/registered/udf_verifier.html, then copy the udf_test
binary to xfstests/src/.
+8. (optional) To do io_uring related testing, please make sure below 3 things:
+ 1) kernel is built with CONFIG_IO_URING=y
+ 2) sysctl -w kernel.io_uring_disabled=0 (or set it to 2 to disable io_uring
+ testing dynamically if kernel supports)
+ 3) install liburing development package contains liburing.h before building
+ fstests
For example, to run the tests with loopback partitions:
to check the filesystem. As of August 2021, xfs_repair finds all
filesystem corruptions found by xfs_check, and more, which means that
xfs_check is no longer run by default.
+ - Set TEST_XFS_SCRUB_REBUILD=1 to have _check_xfs_filesystem run xfs_scrub in
+ "force_repair" mode to rebuild the filesystem; and xfs_repair -n to check
+ the results of the rebuilding.
- xfs_scrub, if present, will always check the test and scratch
filesystems if they are still online at the end of the test. It is no
longer necessary to set TEST_XFS_SCRUB.
- Set FSSTRESS_AVOID and/or FSX_AVOID, which contain options added to
the end of fsstresss and fsx invocations, respectively, in case you wish
to exclude certain operational modes from these tests.
+ - core dumps:
+ - Set COREDUMP_COMPRESSOR to a compression program to compress crash dumps.
+ This program must accept '-f' and the name of a file to compress. In
+ other words, it must emulate gzip.
Kernel/Modules related configuration:
- Set TEST_FS_MODULE_RELOAD=1 to unload the module and reload it between
to "forever" and we'll wait forever until the module is gone.
- Set KCONFIG_PATH to specify your preferred location of kernel config
file. The config is used by tests to check if kernel feature is enabled.
+ - Set REPORT_GCOV to a directory path to make lcov and genhtml generate
+ html reports from any gcov code coverage data collected by the kernel.
+ If REPORT_GCOV is set to 1, the report will be written to $REPORT_DIR/gcov/.
+
+Test control:
+ - Set LOAD_FACTOR to a nonzero positive integer to increase the amount of
+ load applied to the system during a test by the specified multiple.
+ - Set TIME_FACTOR to a nonzero positive integer to increase the amount of
+ time that a test runs by the specified multiple.
+ - For tests that are a member of the "soak" group, setting SOAK_DURATION
+ allows the test runner to specify exactly how long the test should continue
+ running. This setting overrides TIME_FACTOR. Floating point numbers are
+ allowed, and the unit suffixes m(inutes), h(ours), d(ays), and w(eeks) are
+ supported.
Misc:
- If you wish to disable UDF verification test set the environment variable
this option is supported for all filesystems currently only -overlay is
expected to run without issues. For other filesystems additional patches
and fixes to the test suite might be needed.
+ - Set REPORT_VARS_FILE to a file containing colon-separated name-value pairs
+ that will be recorded in the test section report. Names must be unique.
+ Whitespace surrounding the colon will be removed.
+ - set CANON_DEVS=yes to canonicalize device symlinks. This will let you
+ for example use something like TEST_DEV/dev/disk/by-id/nvme-* so the
+ device remains persistent between reboots. This is disabled by default.
______________________
USING THE FSQA SUITE
- If you want to run all tests regardless of what group they are in
(including dangerous tests), use the "all" group: ./check -g all
- To randomize test order: ./check -r [test(s)]
- - You can explicitly specify NFS/CIFS/OVERLAY, otherwise
+ - You can explicitly specify NFS/AFS/CIFS/OVERLAY, otherwise
the filesystem type will be autodetected from $TEST_DEV:
- for running nfs tests: ./check -nfs [test(s)]
+ - for running afs tests: ./check -afs [test(s)]
- for running cifs/smb3 tests: ./check -cifs [test(s)]
- for overlay tests: ./check -overlay [test(s)]
The TEST and SCRATCH partitions should be pre-formatted
6. Test group membership: Each test can be associated with any number
of groups for convenient selection of subsets of tests. Group names
- can be any sequence of non-whitespace characters. Test authors
- associate a test with groups by passing the names of those groups as
- arguments to the _begin_fstest function. For example, the code:
+ must be human readable using only characters in the set [:alnum:_-].
+
+ Test authors associate a test with groups by passing the names of those
+ groups as arguments to the _begin_fstest function. While _begin_fstests
+ is a shell function that must be called at the start of a test to
+ initialise the test environment correctly, the the build infrastructure
+ also scans the test files for _begin_fstests invocations. It does this
+ to compile the group lists that are used to determine which tests to run
+ when `check` is executed. In other words, test files files must call
+ _begin_fstest with their intended groups or they will not be run.
+
+ However, because the build infrastructure also uses _begin_fstests as
+ a defined keyword, addition restrictions are placed on how it must be
+ formatted:
+
+ (a) It must be a single line with no multi-line continuations.
- _begin_fstest auto quick subvol snapshot
+ (b) group names should be separated by spaces and not other whitespace
+
+ (c) A '#' placed anywhere in the list, even in the middle of a group
+ name, will cause everything from the # to the end of the line to be
+ ignored.
+
+ For example, the code:
+
+ _begin_fstest auto quick subvol snapshot # metadata
associates the current test with the "auto", "quick", "subvol", and
- "snapshot" groups. It is not necessary to specify the "all" group
- in the list because that group is computed at run time.
+ "snapshot" groups. Because "metadata" is after the "#" comment
+ delimiter, it is ignored by the build infrastructure and so it will not
+ be associated with that group.
+
+ It is not necessary to specify the "all" group in the list because that
+ group is always computed at run time from the group lists.
- The build process scans test files for _begin_fstest invocations and
- compiles the group list from that information. In other words, test
- files must call _begin_fstest or they will not be run.
Verified output:
TEST_DEV=192.168.1.1:testvol
SCRATCH_MNT=/mnt/gluster/scratch
SCRATCH_DEV=192.168.1.1:scratchvol
+
+[afs]
+FSTYP=afs
+TEST_DEV=%example.com:xfstest.test
+TEST_DIR=/mnt/xfstest.test
+SCRATCH_DEV=%example.com:xfstest.scratch
+SCRATCH_MNT=/mnt/xfstest.scratch
--- /dev/null
+Here are instructions for testing fuse using the passthrough_ll example
+filesystem provided in the libfuse source tree:
+
+git clone git://github.com/libfuse/libfuse.git
+cd libfuse
+meson build
+cd build
+ninja
+cat << EOF | sudo tee /sbin/mount.fuse.passthrough_ll
+#!/bin/bash
+ulimit -n 1048576
+exec $(pwd)/example/passthrough_ll -ofsname="\$@"
+EOF
+sudo chmod +x /sbin/mount.fuse.passthrough_ll
+mkdir -p /mnt/test /mnt/scratch /home/test/test /home/test/scratch
+
+Use the following local.config file:
+
+export TEST_DEV=non1
+export TEST_DIR=/mnt/test
+export SCRATCH_DEV=non2
+export SCRATCH_MNT=/mnt/scratch
+export FSTYP=fuse
+export FUSE_SUBTYP=.passthrough_ll
+export MOUNT_OPTIONS="-osource=/home/test/scratch,allow_other,default_permissions"
+export TEST_FS_MOUNT_OPTS="-osource=/home/test/test,allow_other,default_permissions"
-
To run xfstest on overlayfs, configure the variables of TEST and SCRATCH
partitions to be used as the "base fs" and run './check -overlay'.
Run './check -overlay -g overlay/union' to execute all the unionmount testsuite
test cases.
+
+
+Overlayfs Tools
+===============
+
+A few tests require additional tools. For fsck.overlay [optional],
+build and install:
+ https://github.com/kmxz/overlayfs-tools
--- /dev/null
+Tests with consistent results are provided in the selftest folder.
+Since many people develop testing infrastructure around xfstests,
+these tests are helpful to confirm the testing setup is working as
+expected.
+
+The provided tests include:
+selftest/001 - pass
+selftest/002 - fail from output mismatch
+selftest/003 - fail via _fail
+selftest/004 - skip
+selftest/005 - crash
+selftest/006 - hang
+
+Two groups are used for these tests: selftest and dangerous_selftest.
+selftest/00[1-4] are in the selftest group and selftest/00[5-6] are
+in the dangerous_selftest group.
+
+The selftest will only be run if explicitly speficied. To run the
+selftest, you can specify individual tests, e.g.
+
+# ./check selftest/001
+
+or use the groups under selftest/, e.g.
+
+# ./check -g selftest/selftest
+# ./check -g selftest/dangerous_selftest
+
+Note, you cannot use the group names without including the folder name
+(ie. "-g selftest").
AC_SUBST(have_fiemap)
])
-AC_DEFUN([AC_PACKAGE_WANT_LINUX_PRCTL_H],
- [ AC_CHECK_HEADERS([sys/prctl.h], [ have_prctl=true ], [ have_prctl=false ])
- AC_SUBST(have_prctl)
- ])
-
AC_DEFUN([AC_PACKAGE_WANT_LINUX_FS_H],
[ AC_CHECK_HEADER([linux/fs.h])
])
%description
The XFS regression test suite. Also includes some support for
-acl, attr, udf, and nfs testing. Contains around 200 specific tests
+acl, attr, udf, nfs and afs testing. Contains around 200 specific tests
for userspace & kernelspace.
%prep
status=0
needwrap=true
needsum=true
-n_try=0
-try=""
-n_bad=0
+try=()
sum_bad=0
-bad=""
-n_notrun=0
-notrun=""
+bad=()
+notrun=()
interrupt=true
diff="diff -u"
showme=false
DUMP_OUTPUT=false
iterations=1
istop=false
+loop_on_fail=0
+exclude_tests=()
# This is a global variable used to pass test failure text to reporting gunk
_err_msg=""
# start the initialisation work now
iam=check
+# mkfs.xfs uses the presence of both of these variables to enable formerly
+# supported tiny filesystem configurations that fstests use for fuzz testing
+# in a controlled environment
export MSGVERB="text:action"
export QA_CHECK_FS=${QA_CHECK_FS:=true}
# by default don't output timestamps
timestamp=${TIMESTAMP:=false}
-rm -f $tmp.list $tmp.tmp $tmp.grep $here/$iam.out $tmp.xlist $tmp.report.*
+rm -f $tmp.list $tmp.tmp $tmp.grep $here/$iam.out $tmp.report.* $tmp.arglist
SRC_GROUPS="generic shared"
export SRC_DIR="tests"
check options
-nfs test NFS
+ -afs test AFS
-glusterfs test GlusterFS
-cifs test CIFS
-9p test 9p
+ -fuse test fuse
-virtiofs test virtiofs
-overlay test overlay
-pvfs2 test PVFS2
-I <n> iterate the test list <n> times, but stops iterating further in case of any test failure
-d dump test output to stdout
-b brief test summary
- -R fmt[,fmt] generate report in formats specified. Supported format: [xunit]
+ -R fmt[,fmt] generate report in formats specified. Supported formats: xunit, xunit-quiet
--large-fs optimise scratch device for large filesystems
-s section run only specified section from config file
-S section exclude the specified section from the config file
+ -L <n> loop tests <n> times following a failure, measuring aggregate pass/fail metrics
testlist options
-g group[,group...] include tests from these groups
# the function from that list.
trim_test_list()
{
- test_list="$*"
+ local test_list="$*"
rm -f $tmp.grep
- numsed=0
+ local numsed=0
for t in $test_list
do
if [ $numsed -gt 100 ]; then
rm -f $tmp.grep
}
-
-_wallclock()
-{
- date "+%s"
-}
-
_timestamp()
{
- now=`date "+%T"`
+ local now=`date "+%T"`
echo -n " [$now]"
}
case "$1" in
-\? | -h | --help) usage ;;
- -nfs) FSTYP=nfs ;;
- -glusterfs) FSTYP=glusterfs ;;
- -cifs) FSTYP=cifs ;;
- -9p) FSTYP=9p ;;
- -virtiofs) FSTYP=virtiofs ;;
- -overlay) FSTYP=overlay; export OVERLAY=true ;;
- -pvfs2) FSTYP=pvfs2 ;;
- -tmpfs) FSTYP=tmpfs ;;
- -ubifs) FSTYP=ubifs ;;
+ -nfs|-afs|-glusterfs|-cifs|-9p|-fuse|-virtiofs|-pvfs2|-tmpfs|-ubifs)
+ FSTYP="${1:1}"
+ ;;
+ -overlay)
+ [ "$FSTYP" == overlay ] || export OVL_BASE_FSTYP="$FSTYP"
+ FSTYP=overlay
+ export OVERLAY=true
+ ;;
-g) group=$2 ; shift ;
GROUP_LIST="$GROUP_LIST ${group//,/ }"
;;
-e)
xfile=$2; shift ;
- echo "$xfile" | tr ', ' '\n\n' >> $tmp.xlist
+ readarray -t -O "${#exclude_tests[@]}" exclude_tests < \
+ <(echo "$xfile" | tr ', ' '\n\n')
;;
-E) xfile=$2; shift ;
if [ -f $xfile ]; then
- sed "s/#.*$//" "$xfile" >> $tmp.xlist
- fi
+ readarray -t -O ${#exclude_tests[@]} exclude_tests < \
+ <(sed "s/#.*$//" $xfile)
+ fi
;;
-s) RUN_SECTION="$RUN_SECTION $2"; shift ;;
-S) EXCLUDE_SECTION="$EXCLUDE_SECTION $2"; shift ;;
;;
--large-fs) export LARGE_SCRATCH_DEV=yes ;;
--extra-space=*) export SCRATCH_DEV_EMPTY_SPACE=${r#*=} ;;
+ -L) [[ $2 =~ ^[0-9]+$ ]] || usage
+ loop_on_fail=$2; shift
+ ;;
-*) usage ;;
*) # not an argument, we've got tests now.
exit 1
fi
+# If the test config specified a soak test duration, see if there are any
+# unit suffixes that need converting to an integer seconds count.
+if [ -n "$SOAK_DURATION" ]; then
+ SOAK_DURATION="$(echo "$SOAK_DURATION" | \
+ sed -e 's/^\([.0-9]*\)\([a-z]\)*/\1 \2/g' | \
+ $AWK_PROG -f $here/src/soak_duration.awk)"
+ if [ $? -ne 0 ]; then
+ status=1
+ exit 1
+ fi
+fi
+
if [ -n "$subdir_xfile" ]; then
for d in $SRC_GROUPS $FSTYP; do
[ -f $SRC_DIR/$d/$subdir_xfile ] || continue
for f in `sed "s/#.*$//" $SRC_DIR/$d/$subdir_xfile`; do
- echo $d/$f >> $tmp.xlist
+ exclude_tests+=($d/$f)
done
done
fi
*) # Expand test pattern (e.g. xfs/???, *fs/001)
list=$(cd $SRC_DIR; echo $1)
for t in $list; do
- test_dir=`dirname $t`
- test_dir=${test_dir#$SRC_DIR/*}
- test_name=`basename $t`
+ t=${t#$SRC_DIR/}
+ test_dir=${t%%/*}
+ test_name=${t##*/}
group_file=$SRC_DIR/$test_dir/group.list
- if egrep -q "^$test_name" $group_file; then
+ if grep -Eq "^$test_name" $group_file; then
# in group file ... OK
echo $SRC_DIR/$test_dir/$test_name \
>>$tmp.arglist
_wipe_counters()
{
- n_try="0"
- n_bad="0"
- n_notrun="0"
- unset try notrun bad
+ try=()
+ notrun=()
+ bad=()
}
_global_log() {
fi
}
+if [ -n "$REPORT_GCOV" ]; then
+ . ./common/gcov
+ _gcov_check_report_gcov
+fi
+
_wrapup()
{
seq="check"
check="$RESULT_BASE/check"
+ $interrupt && sect_stop=`_wallclock`
- if $showme; then
- if $needwrap; then
- if $do_report; then
- _make_section_report
- fi
- needwrap=false
+ if $showme && $needwrap; then
+ if $do_report; then
+ # $showme = all selected tests are notrun (no tries)
+ _make_section_report "$section" "${#notrun[*]}" "0" \
+ "${#notrun[*]}" \
+ "$((sect_stop - sect_start))"
fi
+ needwrap=false
elif $needwrap; then
if [ -f $check.time -a -f $tmp.time ]; then
cat $check.time $tmp.time \
echo "SECTION -- $section" >>$tmp.summary
echo "=========================" >>$tmp.summary
- if [ ! -z "$n_try" -a $n_try != 0 ]; then
+ if ((${#try[*]} > 0)); then
if [ $brief_test_summary == "false" ]; then
- echo "Ran:$try"
- echo "Ran:$try" >>$tmp.summary
+ echo "Ran: ${try[*]}"
+ echo "Ran: ${try[*]}" >>$tmp.summary
fi
- _global_log "Ran:$try"
+ _global_log "Ran: ${try[*]}"
fi
$interrupt && echo "Interrupted!" | tee -a $check.log
${REPORT_DIR}/check.log
fi
- if [ ! -z "$notrun" ]; then
+ if ((${#notrun[*]} > 0)); then
if [ $brief_test_summary == "false" ]; then
- echo "Not run:$notrun"
- echo "Not run:$notrun" >>$tmp.summary
+ echo "Not run: ${notrun[*]}"
+ echo "Not run: ${notrun[*]}" >>$tmp.summary
fi
- _global_log "Not run:$notrun"
+ _global_log "Not run: ${notrun[*]}"
fi
- if [ ! -z "$n_bad" -a $n_bad != 0 ]; then
- echo "Failures:$bad"
- echo "Failed $n_bad of $n_try tests"
- _global_log "Failures:$bad"
- _global_log "Failed $n_bad of $n_try tests"
- echo "Failures:$bad" >>$tmp.summary
- echo "Failed $n_bad of $n_try tests" >>$tmp.summary
+ if ((${#bad[*]} > 0)); then
+ echo "Failures: ${bad[*]}"
+ echo "Failed ${#bad[*]} of ${#try[*]} tests"
+ _global_log "Failures: ${bad[*]}"
+ _global_log "Failed ${#bad[*]} of ${#try[*]} tests"
+ echo "Failures: ${bad[*]}" >>$tmp.summary
+ echo "Failed ${#bad[*]} of ${#try[*]} tests" >>$tmp.summary
else
- echo "Passed all $n_try tests"
- _global_log "Passed all $n_try tests"
- echo "Passed all $n_try tests" >>$tmp.summary
+ echo "Passed all ${#try[*]} tests"
+ _global_log "Passed all ${#try[*]} tests"
+ echo "Passed all ${#try[*]} tests" >>$tmp.summary
fi
echo "" >>$tmp.summary
if $do_report; then
- _make_section_report
+ _make_section_report "$section" "${#try[*]}" \
+ "${#bad[*]}" "${#notrun[*]}" \
+ "$((sect_stop - sect_start))"
+ fi
+
+ # Generate code coverage report
+ if [ -n "$REPORT_GCOV" ]; then
+ # don't trigger multiple times if caller hits ^C
+ local gcov_report_dir="$REPORT_GCOV"
+ test "$gcov_report_dir" = "1" && \
+ gcov_report_dir="$REPORT_DIR/gcov"
+ unset REPORT_GCOV
+
+ _gcov_generate_report "$gcov_report_dir"
fi
+
needwrap=false
fi
- sum_bad=`expr $sum_bad + $n_bad`
+ sum_bad=`expr $sum_bad + ${#bad[*]}`
_wipe_counters
rm -f /tmp/*.rawout /tmp/*.out /tmp/*.err /tmp/*.time
if ! $OPTIONS_HAVE_SECTIONS; then
local ret=0
if [ -f ${RESULT_DIR}/require_test ]; then
- _check_test_fs || ret=1
+ if ! _check_test_fs ; then
+ ret=1
+ echo "Trying to repair broken TEST_DEV file system"
+ _repair_test_fs
+ _test_mount
+ fi
rm -f ${RESULT_DIR}/require_test*
else
_test_unmount 2> /dev/null
_expunge_test()
{
local TEST_ID="$1"
- if [ -s $tmp.xlist ]; then
- if grep -q $TEST_ID $tmp.xlist; then
+
+ for f in "${exclude_tests[@]}"; do
+ # $f may contain traling spaces and comments
+ local id_regex="^${TEST_ID}\b"
+ if [[ "$f" =~ ${id_regex} ]]; then
echo " [expunged]"
- return 1
+ return 0
+ fi
+ done
+ return 1
+}
+
+# retain files which would be overwritten in subsequent reruns of the same test
+_stash_fail_loop_files() {
+ local seq_prefix="${REPORT_DIR}/${1}"
+ local cp_suffix="$2"
+
+ for i in ".full" ".dmesg" ".out.bad" ".notrun" ".core" ".hints"; do
+ rm -f "${seq_prefix}${i}${cp_suffix}"
+ if [ -f "${seq_prefix}${i}" ]; then
+ cp "${seq_prefix}${i}" "${seq_prefix}${i}${cp_suffix}"
+ fi
+ done
+}
+
+# Retain in @bad / @notrun the result of the just-run @test_seq. @try array
+# entries are added prior to execution.
+_stash_test_status() {
+ local test_seq="$1"
+ local test_status="$2"
+
+ if $do_report && [[ $test_status != "expunge" ]]; then
+ _make_testcase_report "$section" "$test_seq" \
+ "$test_status" "$((stop - start))"
+ fi
+
+ if ((${#loop_status[*]} > 0)); then
+ # continuing or completing rerun-on-failure loop
+ _stash_fail_loop_files "$test_seq" ".rerun${#loop_status[*]}"
+ loop_status+=("$test_status")
+ if ((${#loop_status[*]} > loop_on_fail)); then
+ printf "%s aggregate results across %d runs: " \
+ "$test_seq" "${#loop_status[*]}"
+ awk "BEGIN {
+ n=split(\"${loop_status[*]}\", arr);"'
+ for (i = 1; i <= n; i++)
+ stats[arr[i]]++;
+ for (x in stats)
+ printf("%s=%d (%.1f%%)",
+ (i-- > n ? x : ", " x),
+ stats[x], 100 * stats[x] / n);
+ }'
+ echo
+ loop_status=()
fi
+ return # only stash @bad result for initial failure in loop
fi
- return 0
+
+ case "$test_status" in
+ fail)
+ if ((loop_on_fail > 0)); then
+ # initial failure, start rerun-on-failure loop
+ _stash_fail_loop_files "$test_seq" ".rerun0"
+ loop_status+=("$test_status")
+ fi
+ bad+=("$test_seq")
+ ;;
+ list|notrun)
+ notrun+=("$test_seq")
+ ;;
+ pass|expunge)
+ ;;
+ *)
+ echo "Unexpected test $test_seq status: $test_status"
+ ;;
+ esac
}
# Can we run systemd scopes?
_detect_kmemleak
_prepare_test_list
+fstests_start_time="$(date +"%F %T")"
if $OPTIONS_HAVE_SECTIONS; then
trap "_summary; exit \$status" 0 1 2 3 15
function run_section()
{
- local section=$1
+ local section=$1 skip
OLD_FSTYP=$FSTYP
OLD_TEST_FS_MOUNT_OPTS=$TEST_FS_MOUNT_OPTS
- get_next_config $section
# Do we need to run only some sections ?
if [ ! -z "$RUN_SECTION" ]; then
fi
fi
+ get_next_config $section
+ _canonicalize_devices
+
mkdir -p $RESULT_BASE
if [ ! -d $RESULT_BASE ]; then
echo "failed to create results directory $RESULT_BASE"
echo "MOUNT_OPTIONS -- `_scratch_mount_options`"
fi
echo
+ test -n "$REPORT_GCOV" && _gcov_reset
needwrap=true
if [ ! -z "$SCRATCH_DEV" ]; then
seqres="$check"
_check_test_fs
- err=false
- first_test=true
- prev_seq=""
- for seq in $list ; do
- # Run report for previous test!
- if $err ; then
- bad="$bad $seqnum"
- n_bad=`expr $n_bad + 1`
- tc_status="fail"
- fi
- if $do_report && ! $first_test ; then
- if [ $tc_status != "expunge" ] ; then
- _make_testcase_report "$prev_seq" "$tc_status"
- fi
- fi
- first_test=false
+ loop_status=() # track rerun-on-failure state
+ local tc_status ix
+ local -a _list=( $list )
+ for ((ix = 0; ix < ${#_list[*]}; !${#loop_status[*]} && ix++)); do
+ seq="${_list[$ix]}"
- err=false
- prev_seq="$seq"
if [ ! -f $seq ]; then
# Try to get full name in case the user supplied only
# seq id and the test has a name. A bit of hassle to
# the filename for the test and the name output are different.
# we don't include the tests/ directory in the name output.
- export seqnum=`echo $seq | sed -e "s;$SRC_DIR/;;"`
-
- # Similarly, the result directory needs to replace the tests/
- # part of the test location.
- group=`dirname $seq`
+ export seqnum=${seq#$SRC_DIR/}
+ group=${seqnum%%/*}
if $OPTIONS_HAVE_SECTIONS; then
- export RESULT_DIR=`echo $group | sed -e "s;$SRC_DIR;${RESULT_BASE}/$section;"`
REPORT_DIR="$RESULT_BASE/$section"
else
- export RESULT_DIR=`echo $group | sed -e "s;$SRC_DIR;$RESULT_BASE;"`
REPORT_DIR="$RESULT_BASE"
fi
+ export RESULT_DIR="$REPORT_DIR/$group"
seqres="$REPORT_DIR/$seqnum"
- mkdir -p $RESULT_DIR
- rm -f ${RESULT_DIR}/require_scratch*
- rm -f ${RESULT_DIR}/require_test*
+ # Generate the entire section report with whatever test results
+ # we have so far. Leave the $sect_time parameter empty so that
+ # it's a little more obvious that this test run is incomplete.
+ if $do_report; then
+ _make_section_report "$section" "${#try[*]}" \
+ "${#bad[*]}" "${#notrun[*]}" \
+ "" &> /dev/null
+ fi
+
echo -n "$seqnum"
if $showme; then
- _expunge_test $seqnum
- if [ $? -eq 1 ]; then
- tc_status="expunge"
- continue
+ if _expunge_test $seqnum; then
+ tc_status="expunge"
+ else
+ echo
+ start=0
+ stop=0
+ tc_status="list"
fi
- echo
- start=0
- stop=0
- tc_status="list"
- n_notrun=`expr $n_notrun + 1`
+ _stash_test_status "$seqnum" "$tc_status"
continue
fi
tc_status="pass"
if [ ! -f $seq ]; then
echo " - no such test?"
+ _stash_test_status "$seqnum" "$tc_status"
continue
fi
# really going to try and run this one
+ mkdir -p $RESULT_DIR
+ rm -f ${RESULT_DIR}/require_scratch*
+ rm -f ${RESULT_DIR}/require_test*
rm -f $seqres.out.bad $seqres.hints
# check if we really should run it
- _expunge_test $seqnum
- if [ $? -eq 1 ]; then
+ if _expunge_test $seqnum; then
tc_status="expunge"
+ _stash_test_status "$seqnum" "$tc_status"
continue
fi
# record that we really tried to run this test.
- try="$try $seqnum"
- n_try=`expr $n_try + 1`
-
- # slashes now in names, sed barfs on them so use grep
- lasttime=`grep -w ^$seqnum $check.time | awk '// {print $2}'`
- if [ "X$lasttime" != X ]; then
- echo -n " ${lasttime}s ... "
- else
- echo -n " " # prettier output with timestamps.
+ if ((!${#loop_status[*]})); then
+ try+=("$seqnum")
fi
+
+ awk 'BEGIN {lasttime=" "} \
+ $1 == "'$seqnum'" {lasttime=" " $2 "s ... "; exit} \
+ END {printf "%s", lasttime}' "$check.time"
rm -f core $seqres.notrun
start=`_wallclock`
- $timestamp && echo -n " ["`date "+%T"`"]"
+ $timestamp && _timestamp
[ ! -x $seq ] && chmod u+x $seq # ensure we can run it
$LOGGER_PROG "run xfstest $seqnum"
if [ -w /dev/kmsg ]; then
echo "run fstests $seqnum at $date_time" > /dev/kmsg
# _check_dmesg depends on this log in dmesg
touch ${RESULT_DIR}/check_dmesg
+ rm -f ${RESULT_DIR}/dmesg_filter
fi
_try_wipe_scratch_devs > /dev/null 2>&1
# to be reported for each test
(echo 1 > $DEBUGFS_MNT/clear_warn_once) > /dev/null 2>&1
+ test_start_time="$(date +"%F %T")"
if [ "$DUMP_OUTPUT" = true ]; then
_run_seq 2>&1 | tee $tmp.out
# Because $? would get tee's return code
sts=$?
fi
- if [ -f core ]; then
- _dump_err_cont "[dumped core]"
- mv core $RESULT_BASE/$seqnum.core
- err=true
- fi
+ # If someone sets kernel.core_pattern or kernel.core_uses_pid,
+ # coredumps generated by fstests might have a longer name than
+ # just "core". Use globbing to find the most common patterns,
+ # assuming there are no other coredump capture packages set up.
+ local cores=0
+ for i in core core.*; do
+ test -f "$i" || continue
+ if ((cores++ == 0)); then
+ _dump_err_cont "[dumped core]"
+ fi
+ (_adjust_oom_score 250; _save_coredump "$i")
+ tc_status="fail"
+ done
if [ -f $seqres.notrun ]; then
$timestamp && _timestamp
$timestamp && echo " [not run]" && \
echo -n " $seqnum -- "
cat $seqres.notrun
- notrun="$notrun $seqnum"
- n_notrun=`expr $n_notrun + 1`
tc_status="notrun"
+ _stash_test_status "$seqnum" "$tc_status"
# Unmount the scratch fs so that we can wipe the scratch
# dev state prior to the next test run.
_scratch_unmount 2> /dev/null
rm -f ${RESULT_DIR}/require_test*
rm -f ${RESULT_DIR}/require_scratch*
- err=true
+ # Even though we failed, there may be something interesting in
+ # dmesg which can help debugging.
+ _check_dmesg
+ (_adjust_oom_score 250; _check_filesystems)
+ tc_status="fail"
else
# The test apparently passed, so check for corruption
# and log messages that shouldn't be there. Run the
# checking tools from a subshell with adjusted OOM
# score so that the OOM killer will target them instead
# of the check script itself.
- (_adjust_oom_score 250; _check_filesystems) || err=true
- _check_dmesg || err=true
+ (_adjust_oom_score 250; _check_filesystems) || tc_status="fail"
+ _check_dmesg || tc_status="fail"
+
+ # Save any coredumps from the post-test fs checks
+ for i in core core.*; do
+ test -f "$i" || continue
+ if ((cores++ == 0)); then
+ _dump_err_cont "[dumped core]"
+ fi
+ (_adjust_oom_score 250; _save_coredump "$i")
+ tc_status="fail"
+ done
fi
# Reload the module after each test to check for leaks or
# Scan for memory leaks after every test so that associating
# a leak to a particular test will be as accurate as possible.
- _check_kmemleak || err=true
+ _check_kmemleak || tc_status="fail"
# test ends after all checks are done.
$timestamp && _timestamp
if [ ! -f $seq.out ]; then
_dump_err "no qualified output"
- err=true
+ tc_status="fail"
+ _stash_test_status "$seqnum" "$tc_status"
continue;
fi
# version.
sed -i "s/\`/\'/g" $tmp.out
if diff $seq.out $tmp.out >/dev/null 2>&1 ; then
- if ! $err ; then
+ if [ "$tc_status" != "fail" ]; then
echo "$seqnum `expr $stop - $start`" >>$tmp.time
echo -n " `expr $stop - $start`s"
fi
echo "(Run '$diff $here/$seq.out $seqres.out.bad'" \
" to see the entire diff)"
fi; } | sed -e 's/^\(.\)/ \1/'
- err=true
+ tc_status="fail"
fi
if [ -f $seqres.hints ]; then
- if $err; then
+ if [ "$tc_status" == "fail" ]; then
echo
cat $seqres.hints
else
rm -f $seqres.hints
fi
fi
+ _stash_test_status "$seqnum" "$tc_status"
done
- # make sure we record the status of the last test we ran.
- if $err ; then
- bad="$bad $seqnum"
- n_bad=`expr $n_bad + 1`
- tc_status="fail"
- fi
- if $do_report && ! $first_test ; then
- if [ $tc_status != "expunge" ] ; then
- _make_testcase_report "$prev_seq" "$tc_status"
- fi
- fi
-
sect_stop=`_wallclock`
interrupt=false
_wrapup
[ -n "$CHACL_PROG" ] || _notrun "chacl command not found"
#
- # Test if chacl is able to list ACLs on the target filesystems. On really
- # old kernels the system calls might not be implemented at all, but the
- # more common case is that the tested filesystem simply doesn't support
- # ACLs.
+ # Test if chacl is able to set an ACL on a file. On really old kernels
+ # the system calls might not be implemented at all, but the more common
+ # case is that the tested filesystem simply doesn't support ACLs.
#
touch $TEST_DIR/syscalltest
- chacl -l $TEST_DIR/syscalltest > $TEST_DIR/syscalltest.out 2>&1
+ chacl 'u::rw-,g::---,o::---' $TEST_DIR/syscalltest > $TEST_DIR/syscalltest.out 2>&1
cat $TEST_DIR/syscalltest.out >> $seqres.full
if grep -q 'Function not implemented' $TEST_DIR/syscalltest.out; then
_btrfs_get_subvolid()
{
- mnt=$1
- name=$2
+ local mnt=$1
+ local name=$2
- $BTRFS_UTIL_PROG sub list $mnt | egrep "\s$name$" | $AWK_PROG '{ print $2 }'
+ $BTRFS_UTIL_PROG subvolume list $mnt | grep -E "\s$name$" | $AWK_PROG '{ print $2 }'
}
# _require_btrfs_command <command> [<subcommand>|<option>]
_notrun "Feature $feat not supported in the available version of mkfs.btrfs"
}
+_require_btrfs_mkfs_uuid_option()
+{
+ local cnt
+
+ cnt=$($MKFS_BTRFS_PROG --help 2>&1 | \
+ grep -E --count -- "--uuid|--device-uuid")
+ if [ $cnt != 2 ]; then
+ _notrun "Require $MKFS_BTRFS_PROG with --uuid and --device-uuid options"
+ fi
+}
+
_require_btrfs_fs_feature()
{
if [ -z $1 ]; then
fi
}
+_require_btrfs_no_nodatacow()
+{
+ if _normalize_mount_options "$MOUNT_OPTIONS" | grep -q "nodatacow"; then
+ _notrun "This test requires no nodatacow enabled"
+ fi
+}
+
+_require_btrfs_free_space_tree()
+{
+ _scratch_mkfs > /dev/null 2>&1
+ if ! $BTRFS_UTIL_PROG inspect-internal dump-super $SCRATCH_DEV | \
+ grep -q "FREE_SPACE_TREE"
+ then
+ _notrun "This test requires a free-space-tree"
+ fi
+}
+
+_require_btrfs_no_block_group_tree()
+{
+ _scratch_mkfs > /dev/null 2>&1
+ if $BTRFS_UTIL_PROG inspect-internal dump-super $SCRATCH_DEV | \
+ grep -q "BLOCK_GROUP_TREE"
+ then
+ _notrun "This test requires no block-group-tree"
+ fi
+}
+
_check_btrfs_filesystem()
{
device=$1
_require_btrfs_dev_del_by_devid()
{
- $BTRFS_UTIL_PROG device delete --help | egrep devid > /dev/null 2>&1
+ $BTRFS_UTIL_PROG device delete --help | grep -E devid > /dev/null 2>&1
[ $? -eq 0 ] || _notrun "$BTRFS_UTIL_PROG too old "\
"(must support 'btrfs device delete <devid> /<mnt>')"
}
return
fi
+ local unsupported=()
+ if [ "$1" == "replace" ]; then
+ # We can't do replace with these profiles because they
+ # imply only one device ($SCRATCH_DEV), and we need to
+ # keep $SCRATCH_DEV around for _scratch_mount
+ # and _check_scratch_fs.
+ unsupported+=(
+ "dup"
+ )
+ elif [ "$1" == "replace-missing" ]; then
+ # We can't replace missing devices with these profiles
+ # because there isn't enough redundancy.
+ unsupported+=(
+ "single"
+ "dup"
+ "raid0"
+ )
+ fi
+
+ if _scratch_btrfs_is_zoned; then
+ # Zoned btrfs only supports SINGLE profile
+ unsupported+=(
+ "dup"
+ "raid0"
+ "raid1"
+ "raid1c3"
+ "raid1c4"
+ "raid10"
+ "raid5"
+ "raid6"
+ )
+ fi
+
if [ -z "$BTRFS_PROFILE_CONFIGS" ]; then
# Default configurations to test.
local configs=(
"raid5:raid5"
"raid6:raid6"
)
+ if [ "$1" == "dup" ]; then
+ configs+=("dup:dup")
+ fi
else
# User-provided configurations.
local configs=(${BTRFS_PROFILE_CONFIGS[@]})
for cfg in "${configs[@]}"; do
local supported=true
local profiles=(${cfg/:/ })
- if [ "$1" == "replace" ]; then
- # We can't do replace with these profiles because they
- # imply only one device ($SCRATCH_DEV), and we need to
- # keep $SCRATCH_DEV around for _scratch_mount
- # and _check_scratch_fs.
- local unsupported=(
- "dup"
- )
- elif [ "$1" == "replace-missing" ]; then
- # We can't replace missing devices with these profiles
- # because there isn't enough redundancy.
- local unsupported=(
- "single"
- "dup"
- "raid0"
- )
- else
- local unsupported=()
- fi
-
- if _scratch_btrfs_is_zoned; then
- # Zoned btrfs only supports SINGLE profile
- unsupported+=(
- "dup"
- "raid0"
- "raid1"
- "raid1c3"
- "raid1c4"
- "raid10"
- "raid5"
- "raid6"
- )
- fi
for unsupp in "${unsupported[@]}"; do
if [ "${profiles[0]}" == "$unsupp" -o "${profiles[1]}" == "$unsupp" ]; then
return 1
}
-_require_btrfs_sysfs_fsid()
+_btrfs_get_fsid()
{
local fsid
+ local mnt=$1
- fsid=$($BTRFS_UTIL_PROG filesystem show $TEST_DIR |grep uuid: |\
+ fsid=$($BTRFS_UTIL_PROG filesystem show $mnt |grep uuid: |\
$AWK_PROG '{print $NF}')
+ echo $fsid
+}
+
+_require_btrfs_sysfs_fsid()
+{
+ local fsid
+
+ fsid=$(_btrfs_get_fsid $TEST_DIR)
+
# Check if the kernel has sysfs fsid support.
# Following kernel patch adds it:
# btrfs: sysfs add devinfo/fsid to retrieve fsid from the device
local sectorsize=$1
# PAGE_SIZE as sectorsize is always supported
- if [ $sectorsize -eq $(get_page_size) ]; then
+ if [ $sectorsize -eq $(_get_page_size) ]; then
return
fi
_notrun "sectorsize $sectorsize is not supported"
}
+_require_btrfs_inline_extents_creation()
+{
+ local ino
+
+ _require_xfs_io_command fiemap
+ _require_scratch
+
+ _scratch_mkfs &> /dev/null
+ _scratch_mount -o max_inline=2048,compress=none
+ _pwrite_byte 0x00 0 1024 $SCRATCH_MNT/inline &> /dev/null
+ sync
+ $XFS_IO_PROG -c "fiemap -v" $SCRATCH_MNT/inline | tail -n 1 > $tmp.fiemap
+ _scratch_unmount
+ # 0x200 means inlined, 0x100 means not block aligned, 0x1 means
+ # the last extent.
+ if ! grep -q "0x301" $tmp.fiemap; then
+ rm -f -- $tmp.fiemap
+ _notrun "No inline extent creation support, maybe subpage?"
+ fi
+ rm -f -- $tmp.fiemap
+}
+
_btrfs_metadump()
{
local device="$1"
$BTRFS_IMAGE_PROG "$device" "$dumpfile"
[ -n "$DUMP_COMPRESSOR" ] && $DUMP_COMPRESSOR -f "$dumpfile" &> /dev/null
}
+
+# Return the btrfs logical address for the first block in a file
+_btrfs_get_first_logical()
+{
+ local file=$1
+ _require_command "$FILEFRAG_PROG" filefrag
+
+ ${FILEFRAG_PROG} -v $file >> $seqres.full
+ ${FILEFRAG_PROG} -v $file | _filter_filefrag | cut -d '#' -f 1
+}
+
+# Find the device path for a btrfs logical offset
+_btrfs_get_device_path()
+{
+ local logical=$1
+ local stripe=$2
+
+ _require_command "$BTRFS_MAP_LOGICAL_PROG" btrfs-map-logical
+
+ $BTRFS_MAP_LOGICAL_PROG -l $logical $SCRATCH_DEV | \
+ $AWK_PROG "(\$1 ~ /mirror/ && \$2 ~ /$stripe/) { print \$8 }"
+}
+
+
+# Find the device physical sector for a btrfs logical offset
+_btrfs_get_physical()
+{
+ local logical=$1
+ local stripe=$2
+
+ _require_command "$BTRFS_MAP_LOGICAL_PROG" btrfs-map-logical
+
+ $BTRFS_MAP_LOGICAL_PROG -l $logical $SCRATCH_DEV >> $seqres.full 2>&1
+ $BTRFS_MAP_LOGICAL_PROG -l $logical $SCRATCH_DEV | \
+ $AWK_PROG "(\$1 ~ /mirror/ && \$2 ~ /$stripe/) { print \$6 }"
+}
+
+# Read from a specific stripe to test read recovery that corrupted a specific
+# stripe. Btrfs uses the PID to select the mirror, so keep reading until the
+# xfs_io process that performed the read was executed with a PID that ends up
+# on the intended mirror.
+_btrfs_direct_read_on_mirror()
+{
+ local mirror=$1
+ local nr_mirrors=$2
+ local file=$3
+ local offset=$4
+ local size=$5
+
+ while [[ -z $( (( BASHPID % nr_mirrors == mirror )) &&
+ exec $XFS_IO_PROG -d \
+ -c "pread -b $size $offset $size" $file) ]]; do
+ :
+ done
+}
+
+# Read from a specific stripe to test read recovery that corrupted a specific
+# stripe. Btrfs uses the PID to select the mirror, so keep reading until the
+# xfs_io process that performed the read was executed with a PID that ends up
+# on the intended mirror.
+_btrfs_buffered_read_on_mirror()
+{
+ local mirror=$1
+ local nr_mirrors=$2
+ local file=$3
+ local offset=$4
+ local size=$5
+
+ # The drop_caches doesn't seem to drop every pages on aarch64 with
+ # 64K page size.
+ # So here as another workaround, cycle mount the SCRATCH_MNT to ensure
+ # the cache are dropped, but we can not use _scratch_cycle_mount, as
+ # we may mount whatever dm device at SCRATCH_MNT.
+ # So here we grab the mounted block device and its mount options, then
+ # unmount and re-mount with the same device and options.
+ local dev=$(findmnt -n -T $SCRATCH_MNT -o SOURCE)
+ local opts=$(findmnt -n -T $SCRATCH_MNT -o OPTIONS)
+ if [ -z "$dev" -o -z "$opts" ]; then
+ _fail "failed to grab mount info of $SCRATCH_MNT"
+ fi
+ _scratch_unmount
+ _mount $dev -o $opts $SCRATCH_MNT
+ while [[ -z $( (( BASHPID % nr_mirrors == mirror )) &&
+ exec $XFS_IO_PROG \
+ -c "pread -b $size $offset $size" $file) ]]; do
+ :
+ done
+}
+
+_require_btrfs_corrupt_block()
+{
+ _require_command "$BTRFS_CORRUPT_BLOCK_PROG" btrfs-corrupt-block
+}
+
+_require_btrfs_send_version()
+{
+ local version=$1
+
+ # Check first if btrfs-progs supports the v2 stream.
+ _require_btrfs_command send --compressed-data
+
+ # Now check the kernel support. If send_stream_version does not exists,
+ # then it's a kernel that only supports v1.
+ [ -f /sys/fs/btrfs/features/send_stream_version ] || \
+ _notrun "kernel does not support send stream $version"
+
+ [ $(cat /sys/fs/btrfs/features/send_stream_version) -ge $version ] || \
+ _notrun "kernel does not support send stream $version"
+}
+
+# Get the bytenr associated to a file extent item at a given file offset.
+#
+# NOTE: At the moment this only works if the file is on a filesystem on top of
+# the scratch device and the file is in the default subvolume (tree id 5).
+_btrfs_get_file_extent_item_bytenr()
+{
+ local file="$1"
+ local offset="$2"
+ local ino=$(stat -c "%i" "$file")
+ local file_extent_key="($ino EXTENT_DATA $offset)"
+
+ _require_btrfs_command inspect-internal dump-tree
+
+ # The tree dump command below works on committed roots, by reading from
+ # a device directly, so we have to sync the filesystem to commit any
+ # open transaction.
+ $BTRFS_UTIL_PROG filesystem sync $SCRATCH_MNT
+
+ $BTRFS_UTIL_PROG inspect-internal dump-tree -t 5 $SCRATCH_DEV | \
+ grep -A4 "$file_extent_key" | grep "disk byte" | \
+ $AWK_PROG '{ print $5 }'
+}
+
+# Check that btrfs-progs has support for the logical-resolve command, with the
+# -o option, and that the kernel supports the logical to ino ioctl v2 (which
+# adds the ignore offset parameter).
+_require_btrfs_scratch_logical_resolve_v2()
+{
+ local bytenr
+
+ # First check if we have support for calling the v2 logical resolve
+ # ioctl in btrfs-progs. Check if the -o options exists, which makes
+ # btrfs-progs call v2 of the ioctl (because the flag
+ # BTRFS_LOGICAL_INO_ARGS_IGNORE_OFFSET is only supported in v2).
+ _require_btrfs_command inspect-internal logical-resolve -o
+ _require_scratch
+
+ _scratch_mkfs > /dev/null || \
+ _fail "_require_btrfs_scratch_logical_resolve_v2: mkfs failed"
+ _scratch_mount
+
+ $XFS_IO_PROG -f -c "pwrite -q 0 128K" "$SCRATCH_MNT/file1"
+ bytenr=$(_btrfs_get_file_extent_item_bytenr "$SCRATCH_MNT/file1" 0)
+ $BTRFS_UTIL_PROG inspect-internal logical-resolve -o $bytenr \
+ $SCRATCH_MNT > /dev/null
+ if [ $? -ne 0 ]; then
+ _scratch_unmount
+ _notrun "Logical resolve ioctl v2 not supported in the kernel"
+ fi
+ _scratch_unmount
+}
+
+_qgroup_mode()
+{
+ local dev=$1
+
+ if [ ! -b "$dev" ]; then
+ _fail "Usage: _qgroup_mode <mounted_device>"
+ fi
+
+ if _has_fs_sysfs_attr $dev /qgroups/mode; then
+ _get_fs_sysfs_attr $dev qgroups/mode
+ else
+ echo "qgroup"
+ fi
+}
+
+_check_regular_qgroup()
+{
+ _qgroup_mode "$@" | grep -q 'qgroup'
+}
+
+_qgroup_rescan()
+{
+ local mnt=$1
+ local dev=$(findmnt -n -o SOURCE $mnt)
+
+ _check_regular_qgroup $dev || return 1
+ _run_btrfs_util_prog quota rescan -w $mnt
+}
+
+_require_qgroup_rescan()
+{
+ _scratch_mkfs >>$seqres.full 2>&1
+ _scratch_mount
+ _run_btrfs_util_prog quota enable $SCRATCH_MNT
+ # Wait for the first rescan.
+ $BTRFS_UTIL_PROG quota rescan -W $SCRATCH_MNT || \
+ _notrun "not able to wait on a quota rescan"
+ # Make sure we can start a rescan.
+ $BTRFS_UTIL_PROG quota rescan -w $SCRATCH_MNT >> $seqres.full || \
+ _notrun "not able to run quota rescan"
+ _scratch_unmount
+}
+
+_require_scratch_qgroup()
+{
+ _scratch_mkfs >>$seqres.full 2>&1
+ _scratch_mount
+ $BTRFS_UTIL_PROG quota enable $SCRATCH_MNT
+ _check_regular_qgroup $SCRATCH_DEV || \
+ _notrun "not running normal qgroups"
+ _scratch_unmount
+}
+
+_require_scratch_enable_simple_quota()
+{
+ _scratch_mkfs >>$seqres.full 2>&1
+ _scratch_mount
+ _qgroup_mode $SCRATCH_DEV | grep 'squota' && \
+ _notrun "cannot enable simple quota; on by default"
+ $BTRFS_UTIL_PROG quota enable --simple $SCRATCH_MNT || \
+ _notrun "simple quotas not available"
+ _scratch_unmount
+}
+
+_has_btrfs_sysfs_feature_attr()
+{
+ local feature_attr=$1
+
+ [ -z $feature_attr ] && \
+ _fail "Missing feature name argument for _has_btrfs_sysfs_attr"
+
+ modprobe btrfs &> /dev/null
+
+ test -e /sys/fs/btrfs/features/$feature_attr
+}
+
+# Print the fsid and metadata uuid replaced with constant strings FSID and
+# METADATA_UUID. Compare temp_fsid with fsid and metadata_uuid, then echo what
+# it matches to or TEMP_FSID. This helps in comparing with the golden output.
+check_fsid()
+{
+ local dev1=$1
+ local fsid
+ local metadata_uuid
+
+ _require_btrfs_fs_sysfs
+ _require_btrfs_fs_feature temp_fsid
+ _require_btrfs_fs_feature metadata_uuid
+ _require_btrfs_command inspect-internal dump-super
+
+ # on disk fsid
+ fsid=$($BTRFS_UTIL_PROG inspect-internal dump-super $dev1 | \
+ grep ^fsid | $AWK_PROG -d" " '{print $2}')
+ echo -e "On disk fsid:\t\t$fsid" | sed -e "s/$fsid/FSID/g"
+
+ # Print FSID even if it is not the same as metadata_uuid because it has
+ # to match in the golden output.
+ metadata_uuid=$(cat /sys/fs/btrfs/$fsid/metadata_uuid)
+ echo -e "Metadata uuid:\t\tFSID"
+
+ # This returns the temp_fsid if set
+ tempfsid=$(_btrfs_get_fsid $dev1)
+ if [[ $tempfsid == $fsid ]]; then
+ echo -e "Temp fsid:\t\tFSID"
+ elif [[ $tempfsid == $metadata_uuid ]]; then
+ # If we are here, it means there is a bug; let it not match with
+ # the golden output.
+ echo -e "Temp fsid:\t\t$metadata_uuid"
+ else
+ echo -e "Temp fsid:\t\tTEMPFSID"
+ fi
+
+ echo -e -n "Tempfsid status:\t"
+ cat /sys/fs/btrfs/$tempfsid/temp_fsid
+}
+
+mkfs_clone()
+{
+ local fsid
+ local uuid
+ local dev1=$1
+ local dev2=$2
+
+ _require_btrfs_command inspect-internal dump-super
+ _require_btrfs_mkfs_uuid_option
+
+ [[ -z $dev1 || -z $dev2 ]] && \
+ _fail "mkfs_clone requires two devices as arguments"
+
+ _mkfs_dev -fq $dev1
+
+ fsid=$($BTRFS_UTIL_PROG inspect-internal dump-super $dev1 | \
+ grep -E ^fsid | $AWK_PROG '{print $2}')
+ uuid=$($BTRFS_UTIL_PROG inspect-internal dump-super $dev1 | \
+ grep -E ^dev_item.uuid | $AWK_PROG '{print $2}')
+
+ _mkfs_dev -fq --uuid $fsid --device-uuid $uuid $dev2
+}
# KEEP_DMESG - whether to keep all dmesg for each test case.
# yes: keep all dmesg
# no: only keep dmesg with error/warning (default)
+# CANON_DEVS - whether or not to canonicalize device symlinks
+# yes: canonicalize device symlinks
+# no (default) do not canonicalize device if they are symlinks
#
# - These can be added to $HOST_CONFIG_DIR (witch default to ./config)
# below or a separate local configuration file can be used (using
export SOAK_STRESS=10000 # -n option to fsstress
export SOAK_PASSES=-1 # count of repetitions of fsstress (while soaking)
export EMAIL=root@localhost # where auto-qa will send its status messages
+
export HOST_OPTIONS=${HOST_OPTIONS:=local.config}
export CHECK_OPTIONS=${CHECK_OPTIONS:="-g auto"}
export BENCH_PASSES=${BENCH_PASSES:=5}
export TIME_FACTOR=${TIME_FACTOR:=1}
export LOAD_FACTOR=${LOAD_FACTOR:=1}
+export SOAK_DURATION=${SOAK_DURATION:=}
export DEBUGFS_MNT=${DEBUGFS_MNT:="/sys/kernel/debug"}
# some constants for overlayfs setup
# Note: mkfs.f2fs doesn't support the --help option yet, but it doesn't
# matter since it also prints the help when an invalid option is given.
if [ "$p" != "" ] && \
- $p --help |& grep -q "[[:space:]]-f[[:space:]|]"; then
+ $p --help |& grep -q "[[:space:]]-f[[:space:]|,]"; then
echo "$p -f"
else
echo $p
export XFS_REPAIR_PROG="$(type -P xfs_repair)"
export XFS_DB_PROG="$(type -P xfs_db)"
export XFS_METADUMP_PROG="$(type -P xfs_metadump)"
+export XFS_MDRESTORE_PROG="$(type -P xfs_mdrestore)"
export XFS_ADMIN_PROG="$(type -P xfs_admin)"
export XFS_GROWFS_PROG=$(type -P xfs_growfs)
export XFS_SPACEMAN_PROG="$(type -P xfs_spaceman)"
export NFS4_GETFACL_PROG="$(type -P nfs4_getfacl)"
export UBIUPDATEVOL_PROG="$(type -P ubiupdatevol)"
export THIN_CHECK_PROG="$(type -P thin_check)"
-export PYTHON2_PROG="$(type -P python2)"
+export PYTHON3_PROG="$(type -P python3)"
export SQLITE3_PROG="$(type -P sqlite3)"
export TIMEOUT_PROG="$(type -P timeout)"
export SETCAP_PROG="$(type -P setcap)"
export BLKZONE_PROG="$(type -P blkzone)"
export GZIP_PROG="$(type -P gzip)"
export BTRFS_IMAGE_PROG="$(type -P btrfs-image)"
+export BTRFS_MAP_LOGICAL_PROG=$(type -P btrfs-map-logical)
# use 'udevadm settle' or 'udevsettle' to wait for lv to be settled.
# newer systems have udevadm command but older systems like RHEL5 don't.
# Set MODPROBE_PATIENT_RM_TIMEOUT_SECONDS to "forever" if you want the patient
# modprobe removal to run forever trying to remove a module.
MODPROBE_REMOVE_PATIENT=""
-modprobe --help >& /dev/null && modprobe --help | grep -q -1 "remove-patiently"
+modprobe --help >& /dev/null && modprobe --help 2>&1 | grep -q -1 "remove-patiently"
if [[ $? -ne 0 ]]; then
if [[ -z "$MODPROBE_PATIENT_RM_TIMEOUT_SECONDS" ]]; then
# We will open code our own implementation of patient module
export BTRFS_SHOW_SUPER_PROG=$(type -P btrfs-show-super)
export BTRFS_CONVERT_PROG=$(type -P btrfs-convert)
export BTRFS_TUNE_PROG=$(type -P btrfstune)
+export BTRFS_CORRUPT_BLOCK_PROG=$(type -P btrfs-corrupt-block)
export XFS_FSR_PROG=$(type -P xfs_fsr)
export MKFS_NFS_PROG="false"
+export MKFS_AFS_PROG="false"
export MKFS_CIFS_PROG="false"
export MKFS_OVERLAY_PROG="false"
export MKFS_REISER4_PROG=$(type -P mkfs.reiser4)
export E2FSCK_PROG=$(type -P e2fsck)
export TUNE2FS_PROG=$(type -P tune2fs)
export FSCK_OVERLAY_PROG=$(type -P fsck.overlay)
+export MKFS_BCACHEFS_PROG=$(set_mkfs_prog_path_with_opts bcachefs)
# SELinux adds extra xattrs which can mess up our expected output.
# So, mount with a context, and they won't be created.
export XFS_MKFS_HAS_NO_META_SUPPORT
fi
-_mount_opts()
+_common_mount_opts()
{
case $FSTYP in
9p)
- export MOUNT_OPTIONS=$PLAN9_MOUNT_OPTIONS
+ echo $PLAN9_MOUNT_OPTIONS
+ ;;
+ fuse)
+ echo $FUSE_MOUNT_OPTIONS
;;
xfs)
- export MOUNT_OPTIONS=$XFS_MOUNT_OPTIONS
+ echo $XFS_MOUNT_OPTIONS
;;
udf)
- export MOUNT_OPTIONS=$UDF_MOUNT_OPTIONS
+ echo $UDF_MOUNT_OPTIONS
;;
nfs)
- export MOUNT_OPTIONS=$NFS_MOUNT_OPTIONS
+ echo $NFS_MOUNT_OPTIONS
+ ;;
+ afs)
+ echo $AFS_MOUNT_OPTIONS
;;
cifs)
- export MOUNT_OPTIONS=$CIFS_MOUNT_OPTIONS
+ echo $CIFS_MOUNT_OPTIONS
;;
ceph)
- export MOUNT_OPTIONS=$CEPHFS_MOUNT_OPTIONS
+ echo $CEPHFS_MOUNT_OPTIONS
;;
glusterfs)
- export MOUNT_OPTIONS=$GLUSTERFS_MOUNT_OPTIONS
+ echo $GLUSTERFS_MOUNT_OPTIONS
;;
overlay)
- export MOUNT_OPTIONS=$OVERLAY_MOUNT_OPTIONS
+ echo $OVERLAY_MOUNT_OPTIONS
;;
ext2|ext3|ext4|ext4dev)
# acls & xattrs aren't turned on by default on ext$FOO
- export MOUNT_OPTIONS="-o acl,user_xattr $EXT_MOUNT_OPTIONS"
+ echo "-o acl,user_xattr $EXT_MOUNT_OPTIONS"
;;
f2fs)
- export MOUNT_OPTIONS="-o acl,user_xattr $F2FS_MOUNT_OPTIONS"
+ echo "-o acl,user_xattr $F2FS_MOUNT_OPTIONS"
;;
reiserfs)
# acls & xattrs aren't turned on by default on reiserfs
- export MOUNT_OPTIONS="-o acl,user_xattr $REISERFS_MOUNT_OPTIONS"
+ echo "-o acl,user_xattr $REISERFS_MOUNT_OPTIONS"
;;
reiser4)
- # acls & xattrs aren't supported by reiser4
- export MOUNT_OPTIONS=$REISER4_MOUNT_OPTIONS
- ;;
+ # acls & xattrs aren't supported by reiser4
+ echo $REISER4_MOUNT_OPTIONS
+ ;;
gfs2)
# acls aren't turned on by default on gfs2
- export MOUNT_OPTIONS="-o acl $GFS2_MOUNT_OPTIONS"
+ echo "-o acl $GFS2_MOUNT_OPTIONS"
;;
tmpfs)
# We need to specify the size at mount, use 1G by default
- export MOUNT_OPTIONS="-o size=1G $TMPFS_MOUNT_OPTIONS"
+ echo "-o size=1G $TMPFS_MOUNT_OPTIONS"
;;
ubifs)
- export MOUNT_OPTIONS=$UBIFS_MOUNT_OPTIONS
+ echo $UBIFS_MOUNT_OPTIONS
;;
*)
;;
esac
}
+_mount_opts()
+{
+ export MOUNT_OPTIONS=$(_common_mount_opts)
+}
+
_test_mount_opts()
{
- case $FSTYP in
- 9p)
- export TEST_FS_MOUNT_OPTS=$PLAN9_MOUNT_OPTIONS
- ;;
- cifs)
- export TEST_FS_MOUNT_OPTS=$CIFS_MOUNT_OPTIONS
- ;;
- ceph)
- export TEST_FS_MOUNT_OPTS=$CEPHFS_MOUNT_OPTIONS
- ;;
- nfs)
- export TEST_FS_MOUNT_OPTS=$NFS_MOUNT_OPTIONS
- ;;
- glusterfs)
- export TEST_FS_MOUNT_OPTS=$GLUSTERFS_MOUNT_OPTIONS
- ;;
- ext2|ext3|ext4|ext4dev)
- # acls & xattrs aren't turned on by default on older ext$FOO
- export TEST_FS_MOUNT_OPTS="-o acl,user_xattr $EXT_MOUNT_OPTIONS"
- ;;
- *)
- ;;
- esac
+ export TEST_FS_MOUNT_OPTS=$(_common_mount_opts)
}
_mkfs_opts()
nfs)
export MKFS_OPTIONS=$NFS_MKFS_OPTIONS
;;
+ afs)
+ export MKFS_OPTIONS=$AFS_MKFS_OPTIONS
+ ;;
cifs)
export MKFS_OPTIONS=$CIFS_MKFS_OPTIONS
;;
esac
}
+# check necessary running dependences then source sepcific fs helpers
+_source_specific_fs()
+{
+ local fs=$1
+
+ if [ -z "$fs" ];then
+ fs=$FSTYP
+ fi
+
+ case "$fs" in
+ xfs)
+ [ "$XFS_LOGPRINT_PROG" = "" ] && _fatal "xfs_logprint not found"
+ [ "$XFS_REPAIR_PROG" = "" ] && _fatal "xfs_repair not found"
+ [ "$XFS_DB_PROG" = "" ] && _fatal "xfs_db not found"
+ [ "$MKFS_XFS_PROG" = "" ] && _fatal "mkfs_xfs not found"
+ [ "$XFS_INFO_PROG" = "" ] && _fatal "xfs_info not found"
+
+ . ./common/xfs
+ ;;
+ udf)
+ [ "$MKFS_UDF_PROG" = "" ] && _fatal "mkfs_udf/mkudffs not found"
+ ;;
+ btrfs)
+ [ "$MKFS_BTRFS_PROG" = "" ] && _fatal "mkfs.btrfs not found"
+
+ . ./common/btrfs
+ ;;
+ ext4)
+ [ "$MKFS_EXT4_PROG" = "" ] && _fatal "mkfs.ext4 not found"
+ . ./common/ext4
+ ;;
+ ext2|ext3|ext4dev)
+ . ./common/ext4
+ ;;
+ f2fs)
+ [ "$MKFS_F2FS_PROG" = "" ] && _fatal "mkfs.f2fs not found"
+ ;;
+ nfs)
+ . ./common/nfs
+ ;;
+ afs)
+ ;;
+ cifs)
+ ;;
+ 9p)
+ ;;
+ fuse)
+ ;;
+ ceph)
+ . ./common/ceph
+ ;;
+ glusterfs)
+ ;;
+ overlay)
+ . ./common/overlay
+ ;;
+ reiser4)
+ [ "$MKFS_REISER4_PROG" = "" ] && _fatal "mkfs.reiser4 not found"
+ ;;
+ pvfs2)
+ ;;
+ ubifs)
+ [ "$UBIUPDATEVOL_PROG" = "" ] && _fatal "ubiupdatevol not found"
+ . ./common/ubifs
+ ;;
+ esac
+}
+
known_hosts()
{
[ "$HOST_CONFIG_DIR" ] || HOST_CONFIG_DIR=`pwd`/configs
fi
case "$FSTYP" in
- 9p|tmpfs|virtiofs)
- # 9p and virtiofs mount tags are just plain strings, so anything is allowed
- # tmpfs doesn't use mount source, ignore
+ 9p|fuse|tmpfs|virtiofs|afs)
+ # 9p, fuse, virtiofs and afs mount tags are just plain strings,
+ # so anything is allowed tmpfs doesn't use mount source, ignore
;;
ceph)
# ceph has two different possible syntaxes for mount devices. The
echo "$parent/$base"
}
+# Enables usage of /dev/disk/by-id/ symlinks to persist target devices
+# over reboots
+_canonicalize_devices()
+{
+ if [ "$CANON_DEVS" != "yes" ]; then
+ return
+ fi
+ [ -L "$TEST_DEV" ] && TEST_DEV=$(readlink -e "$TEST_DEV")
+ [ -L "$SCRATCH_DEV" ] && SCRATCH_DEV=$(readlink -e "$SCRATCH_DEV")
+ [ -L "$TEST_LOGDEV" ] && TEST_LOGDEV=$(readlink -e "$TEST_LOGDEV")
+ [ -L "$TEST_RTDEV" ] && TEST_RTDEV=$(readlink -e "$TEST_RTDEV")
+ [ -L "$SCRATCH_RTDEV" ] && SCRATCH_RTDEV=$(readlink -e "$SCRATCH_RTDEV")
+ [ -L "$LOGWRITES_DEV" ] && LOGWRITES_DEV=$(readlink -e "$LOGWRITES_DEV")
+ if [ ! -z "$SCRATCH_DEV_POOL" ]; then
+ local NEW_SCRATCH_POOL=""
+ for i in $SCRATCH_DEV_POOL; do
+ if [ -L $i ]; then
+ NEW_SCRATCH_POOL="$NEW_SCRATCH_POOL $(readlink -e $i)"
+ else
+ NEW_SCRATCH_POOL="$NEW_SCRATCH_POOL $i"
+ fi
+ done
+ SCRATCH_DEV_POOL="$NEW_SCRATCH_POOL"
+ fi
+}
+
# On check -overlay, for the non multi section config case, this
# function is called on every test, before init_rc().
# When SCRATCH/TEST_* vars are defined in config file, config file
fi
parse_config_section $1
-
if [ ! -z "$OLD_FSTYP" ] && [ $OLD_FSTYP != $FSTYP ]; then
[ -z "$MOUNT_OPTIONS" ] && _mount_opts
[ -z "$TEST_FS_MOUNT_OPTS" ] && _test_mount_opts
fi
fi
+_canonicalize_devices
+
# make sure this script returns success
/bin/true
#
# common functions for setting up and tearing down a dmerror device
+_dmerror_setup_vars()
+{
+ local backing_dev="$1"
+ local tag="$2"
+ local target="$3"
+
+ test -z "$target" && target=error
+ local blk_dev_size=$(blockdev --getsz "$backing_dev")
+
+ eval export "DMLINEAR_${tag}TABLE=\"0 $blk_dev_size linear $backing_dev 0\""
+ eval export "DMERROR_${tag}TABLE=\"0 $blk_dev_size $target $backing_dev 0\""
+}
+
_dmerror_setup()
{
- local dm_backing_dev=$SCRATCH_DEV
+ local rt_target=
+ local log_target=
- local blk_dev_size=`blockdev --getsz $dm_backing_dev`
+ for arg in "$@"; do
+ case "${arg}" in
+ no_rt) rt_target=linear;;
+ no_log) log_target=linear;;
+ *) echo "${arg}: Unknown _dmerror_setup arg.";;
+ esac
+ done
+ # Scratch device
export DMERROR_DEV='/dev/mapper/error-test'
+ _dmerror_setup_vars $SCRATCH_DEV
- export DMLINEAR_TABLE="0 $blk_dev_size linear $dm_backing_dev 0"
+ # Realtime device. We reassign SCRATCH_RTDEV so that all the scratch
+ # helpers continue to work unmodified.
+ if [ -n "$SCRATCH_RTDEV" ]; then
+ if [ -z "$NON_ERROR_RTDEV" ]; then
+ # Set up the device switch
+ local dm_backing_dev=$SCRATCH_RTDEV
+ export NON_ERROR_RTDEV="$SCRATCH_RTDEV"
+ SCRATCH_RTDEV='/dev/mapper/error-rttest'
+ else
+ # Already set up; recreate tables
+ local dm_backing_dev="$NON_ERROR_RTDEV"
+ fi
- export DMERROR_TABLE="0 $blk_dev_size error $dm_backing_dev 0"
+ _dmerror_setup_vars $dm_backing_dev RT $rt_target
+ fi
+
+ # External log device. We reassign SCRATCH_LOGDEV so that all the
+ # scratch helpers continue to work unmodified.
+ if [ -n "$SCRATCH_LOGDEV" ]; then
+ if [ -z "$NON_ERROR_LOGDEV" ]; then
+ # Set up the device switch
+ local dm_backing_dev=$SCRATCH_LOGDEV
+ export NON_ERROR_LOGDEV="$SCRATCH_LOGDEV"
+ SCRATCH_LOGDEV='/dev/mapper/error-logtest'
+ else
+ # Already set up; recreate tables
+ local dm_backing_dev="$NON_ERROR_LOGDEV"
+ fi
+
+ _dmerror_setup_vars $dm_backing_dev LOG $log_target
+ fi
}
_dmerror_init()
{
- _dmerror_setup
+ _dmerror_setup "$@"
+
_dmsetup_remove error-test
_dmsetup_create error-test --table "$DMLINEAR_TABLE" || \
_fatal "failed to create dm linear device"
+
+ if [ -n "$NON_ERROR_RTDEV" ]; then
+ _dmsetup_remove error-rttest
+ _dmsetup_create error-rttest --table "$DMLINEAR_RTTABLE" || \
+ _fatal "failed to create dm linear rt device"
+ fi
+
+ if [ -n "$NON_ERROR_LOGDEV" ]; then
+ _dmsetup_remove error-logtest
+ _dmsetup_create error-logtest --table "$DMLINEAR_LOGTABLE" || \
+ _fatal "failed to create dm linear log device"
+ fi
}
_dmerror_mount()
_dmerror_cleanup()
{
+ test -n "$NON_ERROR_LOGDEV" && $DMSETUP_PROG resume error-logtest &>/dev/null
+ test -n "$NON_ERROR_RTDEV" && $DMSETUP_PROG resume error-rttest &>/dev/null
$DMSETUP_PROG resume error-test > /dev/null 2>&1
+
$UMOUNT_PROG $SCRATCH_MNT > /dev/null 2>&1
+
+ test -n "$NON_ERROR_LOGDEV" && _dmsetup_remove error-logtest
+ test -n "$NON_ERROR_RTDEV" && _dmsetup_remove error-rttest
_dmsetup_remove error-test
unset DMERROR_DEV DMLINEAR_TABLE DMERROR_TABLE
+
+ if [ -n "$NON_ERROR_LOGDEV" ]; then
+ SCRATCH_LOGDEV="$NON_ERROR_LOGDEV"
+ unset NON_ERROR_LOGDEV DMLINEAR_LOGTABLE DMERROR_LOGTABLE
+ fi
+
+ if [ -n "$NON_ERROR_RTDEV" ]; then
+ SCRATCH_RTDEV="$NON_ERROR_RTDEV"
+ unset NON_ERROR_RTDEV DMLINEAR_RTTABLE DMERROR_RTTABLE
+ fi
}
_dmerror_load_error_table()
suspend_opt="$*"
fi
+ # If the full environment is set up, configure ourselves for shutdown
+ type _prepare_for_eio_shutdown &>/dev/null && \
+ _prepare_for_eio_shutdown $DMERROR_DEV
+
+ # Suspend the scratch device before the log and realtime devices so
+ # that the kernel can freeze and flush the filesystem if the caller
+ # wanted a freeze.
$DMSETUP_PROG suspend $suspend_opt error-test
[ $? -ne 0 ] && _fail "dmsetup suspend failed"
+ if [ -n "$NON_ERROR_RTDEV" ]; then
+ $DMSETUP_PROG suspend $suspend_opt error-rttest
+ [ $? -ne 0 ] && _fail "failed to suspend error-rttest"
+ fi
+
+ if [ -n "$NON_ERROR_LOGDEV" ]; then
+ $DMSETUP_PROG suspend $suspend_opt error-logtest
+ [ $? -ne 0 ] && _fail "failed to suspend error-logtest"
+ fi
+
+ # Load new table
$DMSETUP_PROG load error-test --table "$DMERROR_TABLE"
load_res=$?
+ if [ -n "$NON_ERROR_RTDEV" ]; then
+ $DMSETUP_PROG load error-rttest --table "$DMERROR_RTTABLE"
+ [ $? -ne 0 ] && _fail "failed to load error table into error-rttest"
+ fi
+
+ if [ -n "$NON_ERROR_LOGDEV" ]; then
+ $DMSETUP_PROG load error-logtest --table "$DMERROR_LOGTABLE"
+ [ $? -ne 0 ] && _fail "failed to load error table into error-logtest"
+ fi
+
+ # Resume devices in the opposite order that we suspended them.
+ if [ -n "$NON_ERROR_LOGDEV" ]; then
+ $DMSETUP_PROG resume error-logtest
+ [ $? -ne 0 ] && _fail "failed to resume error-logtest"
+ fi
+
+ if [ -n "$NON_ERROR_RTDEV" ]; then
+ $DMSETUP_PROG resume error-rttest
+ [ $? -ne 0 ] && _fail "failed to resume error-rttest"
+ fi
+
$DMSETUP_PROG resume error-test
resume_res=$?
suspend_opt="$*"
fi
+ # Suspend the scratch device before the log and realtime devices so
+ # that the kernel can freeze and flush the filesystem if the caller
+ # wanted a freeze.
$DMSETUP_PROG suspend $suspend_opt error-test
[ $? -ne 0 ] && _fail "dmsetup suspend failed"
+ if [ -n "$NON_ERROR_RTDEV" ]; then
+ $DMSETUP_PROG suspend $suspend_opt error-rttest
+ [ $? -ne 0 ] && _fail "failed to suspend error-rttest"
+ fi
+
+ if [ -n "$NON_ERROR_LOGDEV" ]; then
+ $DMSETUP_PROG suspend $suspend_opt error-logtest
+ [ $? -ne 0 ] && _fail "failed to suspend error-logtest"
+ fi
+
+ # Load new table
$DMSETUP_PROG load error-test --table "$DMLINEAR_TABLE"
load_res=$?
+ if [ -n "$NON_ERROR_RTDEV" ]; then
+ $DMSETUP_PROG load error-rttest --table "$DMLINEAR_RTTABLE"
+ [ $? -ne 0 ] && _fail "failed to load working table into error-rttest"
+ fi
+
+ if [ -n "$NON_ERROR_LOGDEV" ]; then
+ $DMSETUP_PROG load error-logtest --table "$DMLINEAR_LOGTABLE"
+ [ $? -ne 0 ] && _fail "failed to load working table into error-logtest"
+ fi
+
+ # Resume devices in the opposite order that we suspended them.
+ if [ -n "$NON_ERROR_LOGDEV" ]; then
+ $DMSETUP_PROG resume error-logtest
+ [ $? -ne 0 ] && _fail "failed to resume error-logtest"
+ fi
+
+ if [ -n "$NON_ERROR_RTDEV" ]; then
+ $DMSETUP_PROG resume error-rttest
+ [ $? -ne 0 ] && _fail "failed to resume error-rttest"
+ fi
+
$DMSETUP_PROG resume error-test
resume_res=$?
[ $load_res -ne 0 ] && _fail "dmsetup failed to load error table"
[ $resume_res -ne 0 ] && _fail "dmsetup resume failed"
}
+
+# Given a list of (start, length) tuples on stdin, combine adjacent tuples into
+# larger ones and write the new list to stdout.
+__dmerror_combine_extents()
+{
+ local awk_program='
+ BEGIN {
+ start = 0; len = 0;
+ }
+ {
+ if (start + len == $1) {
+ len += $2;
+ } else {
+ if (len > 0)
+ printf("%d %d\n", start, len);
+ start = $1;
+ len = $2;
+ }
+ }
+ END {
+ if (len > 0)
+ printf("%d %d\n", start, len);
+ }'
+
+ awk "$awk_program"
+}
+
+# Given a block device, the name of a preferred dm target, the name of an
+# implied dm target, and a list of (start, len) tuples on stdin, create a new
+# dm table which maps each of the tuples to the preferred target and all other
+# areas to the implied dm target.
+__dmerror_recreate_map()
+{
+ local device="$1"
+ local preferred_tgt="$2"
+ local implied_tgt="$3"
+ local size=$(blockdev --getsz "$device")
+
+ local awk_program='
+ BEGIN {
+ implied_start = 0;
+ }
+ {
+ extent_start = $1;
+ extent_len = $2;
+
+ if (extent_start > size) {
+ extent_start = size;
+ extent_len = 0;
+ } else if (extent_start + extent_len > size) {
+ extent_len = size - extent_start;
+ }
+
+ if (implied_start < extent_start)
+ printf("%d %d %s %s %d\n", implied_start,
+ extent_start - implied_start,
+ implied_tgt, device, implied_start);
+ printf("%d %d %s %s %d\n", extent_start, extent_len,
+ preferred_tgt, device, extent_start);
+ implied_start = extent_start + extent_len;
+ }
+ END {
+ if (implied_start < size)
+ printf("%d %d %s %s %d\n", implied_start,
+ size - implied_start, implied_tgt,
+ device, implied_start);
+ }'
+
+ awk -v device="$device" -v size=$size -v implied_tgt="$implied_tgt" \
+ -v preferred_tgt="$preferred_tgt" "$awk_program"
+}
+
+# Update the dm error table so that the range (start, len) maps to the
+# preferred dm target, overriding anything that maps to the implied dm target.
+# This assumes that the only desired targets for this dm device are the
+# preferred and and implied targets. The fifth argument is the scratch device
+# that we want to change the table for.
+__dmerror_change()
+{
+ local start="$1"
+ local len="$2"
+ local preferred_tgt="$3"
+ local implied_tgt="$4"
+ local whichdev="$5"
+ local old_table
+ local new_table
+
+ case "$whichdev" in
+ "SCRATCH_DEV"|"") whichdev="$SCRATCH_DEV";;
+ "SCRATCH_LOGDEV"|"LOG") whichdev="$NON_ERROR_LOGDEV";;
+ "SCRATCH_RTDEV"|"RT") whichdev="$NON_ERROR_RTDEV";;
+ esac
+
+ case "$whichdev" in
+ "$SCRATCH_DEV") old_table="$DMERROR_TABLE";;
+ "$NON_ERROR_LOGDEV") old_table="$DMERROR_LOGTABLE";;
+ "$NON_ERROR_RTDEV") old_table="$DMERROR_RTTABLE";;
+ *)
+ echo "$whichdev: Unknown dmerror device."
+ return
+ ;;
+ esac
+
+ new_table="$( (echo "$old_table"; echo "$start $len $preferred_tgt") | \
+ awk -v type="$preferred_tgt" '{if ($3 == type) print $0;}' | \
+ sort -g | \
+ __dmerror_combine_extents | \
+ __dmerror_recreate_map "$whichdev" "$preferred_tgt" \
+ "$implied_tgt" )"
+
+ case "$whichdev" in
+ "$SCRATCH_DEV") DMERROR_TABLE="$new_table";;
+ "$NON_ERROR_LOGDEV") DMERROR_LOGTABLE="$new_table";;
+ "$NON_ERROR_RTDEV") DMERROR_RTTABLE="$new_table";;
+ esac
+}
+
+# Reset the dm error table to everything ok. The dm device itself must be
+# remapped by calling _dmerror_load_error_table.
+_dmerror_reset_table()
+{
+ DMERROR_TABLE="$DMLINEAR_TABLE"
+ DMERROR_LOGTABLE="$DMLINEAR_LOGTABLE"
+ DMERROR_RTTABLE="$DMLINEAR_RTTABLE"
+}
+
+# Update the dm error table so that IOs to the given range will return EIO.
+# The dm device itself must be remapped by calling _dmerror_load_error_table.
+_dmerror_mark_range_bad()
+{
+ local start="$1"
+ local len="$2"
+ local dev="$3"
+
+ __dmerror_change "$start" "$len" error linear "$dev"
+}
+
+# Update the dm error table so that IOs to the given range will succeed.
+# The dm device itself must be remapped by calling _dmerror_load_error_table.
+_dmerror_mark_range_good()
+{
+ local start="$1"
+ local len="$2"
+ local dev="$3"
+
+ __dmerror_change "$start" "$len" linear error "$dev"
+}
# Check options to be sure.
# XFS ignores dax option(or changes it to dax=never)
# and goes on if dev underneath does not support dax.
- _fs_options $LOGWRITES_DMDEV | egrep -q "dax(=always|,|$)"
+ _fs_options $LOGWRITES_DMDEV | grep -Eq "dax(=always|,|$)"
ret=$?
_log_writes_cleanup
if [ $ret -ne 0 ]; then
fi
}
+# Set up a dm-log-writes device
+#
+# blkdev: the specified target device
+# length(optional): the mapped length in bytes
+# Note that the entire size of the target device will be used
+# if length is not specified.
_log_writes_init()
{
- blkdev=$1
+ local blkdev=$1
+ local length=$2
+ local BLK_DEV_SIZE
[ -z "$blkdev" ] && _fail \
"block dev must be specified for _log_writes_init"
- local BLK_DEV_SIZE=`blockdev --getsz $blkdev`
+ if [ -z "$length" ]; then
+ BLK_DEV_SIZE=`blockdev --getsz $blkdev`
+ else
+ local blksz=`blockdev --getss $blkdev`
+ BLK_DEV_SIZE=$((length / blksz))
+ fi
+
LOGWRITES_NAME=logwrites-test
LOGWRITES_DMDEV=/dev/mapper/$LOGWRITES_NAME
LOGWRITES_TABLE="0 $BLK_DEV_SIZE log-writes $blkdev $LOGWRITES_DEV"
_dmthin_mkfs()
{
_scratch_options mkfs
- _mkfs_dev $SCRATCH_OPTIONS $@ $DMTHIN_VOL_DEV
+ _mkfs_dev $SCRATCH_OPTIONS "$@" $DMTHIN_VOL_DEV
+}
+_dmthin_try_mkfs()
+{
+ _scratch_options mkfs
+ _try_mkfs_dev $SCRATCH_OPTIONS "$@" $DMTHIN_VOL_DEV
}
fi
- if egrep -i 'onl|ready' $tmp.status | grep -iv 'not ready' >/dev/null; then
+ if grep -Ei 'onl|ready' $tmp.status | grep -iv 'not ready' >/dev/null; then
:
else
echo "ERROR: $dumptape is not online"
i=0
while [ $i -lt 20 ]; do
echo "Checking status..." >>$seqres.full
- if _mt status 2>&1 | tee -a $seqres.full | egrep -i "onl|ready" >/dev/null; then
+ if _mt status 2>&1 | tee -a $seqres.full | grep -Ei "onl|ready" >/dev/null; then
break;
else
sleep 1
--no-check-quota)
do_quota_check=false
;;
- -K|-R)
+ -K|-R|-x)
restore_args="$restore_args $1"
;;
*)
#
# _require_scratch_encryption [-c CONTENTS_MODE] [-n FILENAMES_MODE]
# [-f POLICY_FLAGS] [-v POLICY_VERSION]
+# [-s LOG2_DUSIZE]
#
# Require encryption support on the scratch device.
#
#
_require_scratch_encryption()
{
- _require_scratch
+ local arg
+ _require_scratch
_require_xfs_io_command "set_encpolicy"
+ for arg; do
+ if [ "$arg" = "-s" ]; then
+ # -s option was added later. Make sure it's available.
+ _require_xfs_io_command "set_encpolicy" "-s"
+ fi
+ done
+
# The 'test_dummy_encryption' mount option interferes with trying to use
# encryption for real, even if we are just trying to get/set policies
# and never put any keys in the keyring. So skip the real encryption
# some older kernels and is ext4-specific anyway.)
mkdir $SCRATCH_MNT/tmpdir
if _set_encpolicy $SCRATCH_MNT/tmpdir 2>&1 >>$seqres.full | \
- egrep -q 'Inappropriate ioctl for device|Operation not supported'
+ grep -Eq 'Inappropriate ioctl for device|Operation not supported'
then
_notrun "kernel does not support $FSTYP encryption"
fi
local c
OPTIND=2
- while getopts "c:n:f:v:" c; do
+ while getopts "c:n:f:s:v:" c; do
case $c in
- c|n)
+ c|n|s)
set_encpolicy_args+=" -$c $OPTARG"
;;
f)
policy_version=$OPTARG
;;
*)
- _fail "Unrecognized option '$c'"
+ _fail "${FUNCNAME[0]}: unrecognized option '$c'"
;;
esac
done
local keyspec=$(_generate_session_encryption_key)
fi
if _set_encpolicy $dir $keyspec $set_encpolicy_args \
- 2>&1 >>$seqres.full | egrep -q 'Invalid argument'; then
+ 2>&1 >>$seqres.full | grep -Eq 'Invalid argument'; then
_notrun "kernel does not support encryption policy: '$set_encpolicy_args'"
fi
# fscrypt allows setting policies with modes it knows about, even
# erase the UBI volume; reformated automatically on next mount
$UBIUPDATEVOL_PROG ${SCRATCH_DEV} -t
;;
+ ceph)
+ _scratch_cleanup_files
+ ;;
*)
_notrun "No encryption support for $FSTYP"
;;
nonce=$(_get_encryption_nonce $SCRATCH_DEV $inode)
_dump_ciphertext_blocks $SCRATCH_DEV $blocklist > $tmp.actual_contents
$crypt_contents_cmd $contents_encryption_mode $raw_key_hex \
- --file-nonce=$nonce --block-size=$blocksize \
- --inode-number=$inode < $src > $tmp.expected_contents
+ --file-nonce=$nonce --inode-number=$inode \
+ < $src > $tmp.expected_contents
if ! cmp $tmp.expected_contents $tmp.actual_contents; then
_fail "Expected encrypted contents != actual encrypted contents. File: $f"
fi
$crypt_contents_cmd $contents_encryption_mode $raw_key_hex \
- --decrypt --file-nonce=$nonce --block-size=$blocksize \
- --inode-number=$inode \
+ --decrypt --file-nonce=$nonce --inode-number=$inode \
< $tmp.actual_contents > $tmp.decrypted_contents
if ! cmp $src $tmp.decrypted_contents; then
_fail "Contents decryption sanity check failed. File: $f"
echo -n "$name" | \
$crypt_filename_cmd $filenames_encryption_mode \
$raw_key_hex --file-nonce=$nonce --padding=$padding \
- --block-size=255 --inode-number=$dir_inode \
+ --data-unit-size=255 --inode-number=$dir_inode \
> $tmp.expected_name
if ! cmp $tmp.expected_name $tmp.actual_name; then
_fail "Expected encrypted filename != actual encrypted filename. File: $f"
fi
$crypt_filename_cmd $filenames_encryption_mode $raw_key_hex \
--decrypt --file-nonce=$nonce --padding=$padding \
- --block-size=255 --inode-number=$dir_inode \
+ --data-unit-size=255 --inode-number=$dir_inode \
< $tmp.actual_name > $tmp.decrypted_name
decrypted_name=$(tr -d '\0' < $tmp.decrypted_name)
if [ "$name" != "$decrypted_name" ]; then
FSCRYPT_MODE_AES_128_CBC=5
FSCRYPT_MODE_AES_128_CTS=6
FSCRYPT_MODE_ADIANTUM=9
+FSCRYPT_MODE_AES_256_HCTR2=10
FSCRYPT_POLICY_FLAG_DIRECT_KEY=0x04
FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64=0x08
AES-128-CBC-ESSIV) echo $FSCRYPT_MODE_AES_128_CBC ;;
AES-128-CTS-CBC) echo $FSCRYPT_MODE_AES_128_CTS ;;
Adiantum) echo $FSCRYPT_MODE_ADIANTUM ;;
+ AES-256-HCTR2) echo $FSCRYPT_MODE_AES_256_HCTR2 ;;
*) _fail "Unknown fscrypt mode: $name" ;;
esac
}
# 'direct': test the DIRECT_KEY policy flag
# 'iv_ino_lblk_64': test the IV_INO_LBLK_64 policy flag
# 'iv_ino_lblk_32': test the IV_INO_LBLK_32 policy flag
+# 'log2_dusize=N': test the log2_data_unit_size field
#
_verify_ciphertext_for_encryption_policy()
{
local opt
local policy_version=1
local policy_flags=0
+ local log2_dusize=0
local set_encpolicy_args=""
local crypt_util_args=""
local crypt_util_contents_args=""
iv_ino_lblk_32)
(( policy_flags |= FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32 ))
;;
+ log2_dusize=*)
+ log2_dusize=$(echo "$opt" | sed 's/^log2_dusize=//')
+ ;;
*)
_fail "Unknown option '$opt' passed to ${FUNCNAME[0]}"
;;
set_encpolicy_args+=" -c $contents_mode_num"
set_encpolicy_args+=" -n $filenames_mode_num"
+ if (( log2_dusize != 0 )); then
+ set_encpolicy_args+=" -s $log2_dusize"
+ fi
crypt_util_contents_args+=" --mode-num=$contents_mode_num"
crypt_util_filename_args+=" --mode-num=$filenames_mode_num"
fi
_scratch_mount
+ if (( log2_dusize != 0 )); then
+ crypt_util_contents_args+=" --data-unit-size=$((1 << log2_dusize))"
+ else
+ crypt_util_contents_args+=" --data-unit-size=$(_get_block_size $SCRATCH_MNT)"
+ fi
+
crypt_util_args+=" --fs-uuid=$(blkid -s UUID -o value $SCRATCH_DEV | tr -d -)"
crypt_util_contents_args+="$crypt_util_args"
--- /dev/null
+#
+# ext4 specific common functions
+#
+
+__generate_ext4_report_vars() {
+ __generate_blockdev_report_vars TEST_LOGDEV
+ __generate_blockdev_report_vars SCRATCH_LOGDEV
+}
+
+_setup_large_ext4_fs()
+{
+ local fs_size=$1
+ local tmp_dir=/tmp/
+
+ [ "$LARGE_SCRATCH_DEV" != yes ] && return 0
+ [ -z "$SCRATCH_DEV_EMPTY_SPACE" ] && SCRATCH_DEV_EMPTY_SPACE=0
+ [ $SCRATCH_DEV_EMPTY_SPACE -ge $fs_size ] && return 0
+
+ # Default free space in the FS is 50GB, but you can specify more via
+ # SCRATCH_DEV_EMPTY_SPACE
+ local space_to_consume=$(($fs_size - 50*1024*1024*1024 - $SCRATCH_DEV_EMPTY_SPACE))
+
+ # mount the filesystem and create 16TB - 4KB files until we consume
+ # all the necessary space.
+ _try_scratch_mount 2>&1 >$tmp_dir/mnt.err
+ local status=$?
+ if [ $status -ne 0 ]; then
+ echo "mount failed"
+ cat $tmp_dir/mnt.err >&2
+ rm -f $tmp_dir/mnt.err
+ return $status
+ fi
+ rm -f $tmp_dir/mnt.err
+
+ local file_size=$((16*1024*1024*1024*1024 - 4096))
+ local nfiles=0
+ while [ $space_to_consume -gt $file_size ]; do
+
+ xfs_io -F -f \
+ -c "truncate $file_size" \
+ -c "falloc -k 0 $file_size" \
+ $SCRATCH_MNT/.use_space.$nfiles 2>&1
+ status=$?
+ if [ $status -ne 0 ]; then
+ break;
+ fi
+
+ space_to_consume=$(( $space_to_consume - $file_size ))
+ nfiles=$(($nfiles + 1))
+ done
+
+ # consume the remaining space.
+ if [ $space_to_consume -gt 0 ]; then
+ xfs_io -F -f \
+ -c "truncate $space_to_consume" \
+ -c "falloc -k 0 $space_to_consume" \
+ $SCRATCH_MNT/.use_space.$nfiles 2>&1
+ status=$?
+ fi
+ export NUM_SPACE_FILES=$nfiles
+
+ _scratch_unmount
+ if [ $status -ne 0 ]; then
+ echo "large file prealloc failed"
+ cat $tmp_dir/mnt.err >&2
+ return $status
+ fi
+ return 0
+}
+
+_scratch_mkfs_ext4_opts()
+{
+ mkfs_opts=$*
+
+ _scratch_options mkfs
+
+ echo "$MKFS_EXT4_PROG $SCRATCH_OPTIONS $mkfs_opts"
+}
+
+_scratch_mkfs_ext4()
+{
+ local mkfs_cmd="`_scratch_mkfs_ext4_opts`"
+ local mkfs_filter="grep -v -e ^Warning: -e \"^mke2fs \" | grep -v \"^$\""
+ local tmp=`mktemp -u`
+ local mkfs_status
+
+ if [ "$USE_EXTERNAL" = yes -a ! -z "$SCRATCH_LOGDEV" ]; then
+ $MKFS_EXT4_PROG -F -O journal_dev $MKFS_OPTIONS $* $SCRATCH_LOGDEV 2>$tmp.mkfserr 1>$tmp.mkfsstd
+ mkjournal_status=$?
+
+ if [ $mkjournal_status -ne 0 ]; then
+ cat $tmp.mkfsstd
+ cat $tmp.mkfserr >&2
+ return $mkjournal_status
+ fi
+ fi
+
+ _scratch_do_mkfs "$mkfs_cmd" "$mkfs_filter" $* 2>$tmp.mkfserr 1>$tmp.mkfsstd
+ mkfs_status=$?
+
+ if [ $mkfs_status -eq 0 -a "$LARGE_SCRATCH_DEV" = yes ]; then
+ # manually parse the mkfs output to get the fs size in bytes
+ local fs_size=`cat $tmp.mkfsstd | awk ' \
+ /^Block size/ { split($2, a, "="); bs = a[2] ; } \
+ / inodes, / { blks = $3 } \
+ /reserved for the super user/ { resv = $1 } \
+ END { fssize = bs * blks - resv; print fssize }'`
+
+ _setup_large_ext4_fs $fs_size
+ mkfs_status=$?
+ fi
+
+ # output mkfs stdout and stderr
+ cat $tmp.mkfsstd
+ cat $tmp.mkfserr >&2
+ rm -f $tmp.mkfserr $tmp.mkfsstd
+
+ return $mkfs_status
+}
+
+_ext4_metadump()
+{
+ local device="$1"
+ local dumpfile="$2"
+ local compressopt="$3"
+
+ test -n "$E2IMAGE_PROG" || _fail "e2image not installed"
+ $E2IMAGE_PROG -Q "$device" "$dumpfile"
+ [ "$compressopt" = "compress" ] && [ -n "$DUMP_COMPRESSOR" ] &&
+ $DUMP_COMPRESSOR -f "$dumpfile" &>> "$seqres.full"
+}
+
+_ext4_mdrestore()
+{
+ local metadump="$1"
+ local device="$2"
+ shift; shift
+ local options="$@"
+
+ # 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 [ ! -e "$metadump" ] && [ -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
+
+ $E2IMAGE_PROG $options -r "${metadump}" "${SCRATCH_DEV}"
+}
+
+# this test requires the ext4 kernel support crc feature on scratch device
+#
+_require_scratch_ext4_crc()
+{
+ _scratch_mkfs_ext4 >/dev/null 2>&1
+ dumpe2fs -h $SCRATCH_DEV 2> /dev/null | grep -q metadata_csum || _notrun "metadata_csum not supported by this filesystem"
+ _try_scratch_mount >/dev/null 2>&1 \
+ || _notrun "Kernel doesn't support metadata_csum feature"
+ _scratch_unmount
+}
+
+# Check whether the specified feature whether it is supported by
+# mkfs.ext4 and the kernel.
+_require_scratch_ext4_feature()
+{
+ if [ -z "$1" ]; then
+ echo "Usage: _require_scratch_ext4_feature feature"
+ exit 1
+ fi
+ $MKFS_EXT4_PROG -F $MKFS_OPTIONS -O "$1" \
+ $SCRATCH_DEV 512m >/dev/null 2>&1 \
+ || _notrun "mkfs.ext4 doesn't support $1 feature"
+ _try_scratch_mount >/dev/null 2>&1 \
+ || _notrun "Kernel doesn't support the ext4 feature(s): $1"
+ _scratch_unmount
+}
+
+# Disable extent zeroing for ext4 on the given device
+_ext4_disable_extent_zeroout()
+{
+ local dev=${1:-$TEST_DEV}
+ local sdev=`_short_dev $dev`
+
+ [ -f /sys/fs/ext4/$sdev/extent_max_zeroout_kb ] && \
+ echo 0 >/sys/fs/ext4/$sdev/extent_max_zeroout_kb
+}
+
+_require_scratch_richacl_ext4()
+{
+ _scratch_mkfs -O richacl >/dev/null 2>&1 \
+ || _notrun "can't mkfs $FSTYP with option -O richacl"
+ _try_scratch_mount >/dev/null 2>&1 \
+ || _notrun "kernel doesn't support richacl feature on $FSTYP"
+ _scratch_unmount
+}
+
+_scratch_ext4_options()
+{
+ local type=$1
+ local log_opt=""
+
+ case $type in
+ mkfs)
+ SCRATCH_OPTIONS="$SCRATCH_OPTIONS -F"
+ log_opt="-J device=$SCRATCH_LOGDEV"
+ ;;
+ mount)
+ # As of kernel 5.19, the kernel mount option path parser only
+ # accepts direct paths to block devices--the final path
+ # component cannot be a symlink.
+ log_opt="-o journal_path=$(realpath -q "$SCRATCH_LOGDEV")"
+ ;;
+ esac
+ [ "$USE_EXTERNAL" = yes -a ! -z "$SCRATCH_LOGDEV" ] && \
+ SCRATCH_OPTIONS="$SCRATCH_OPTIONS ${log_opt}"
+}
+
+# Get the inode flags for a particular inode number
+_ext4_get_inum_iflags() {
+ local dev="$1"
+ local inumber="$2"
+
+ debugfs -R "stat <${inumber}>" "${dev}" 2> /dev/null | \
+ sed -n 's/^.*Flags: \([0-9a-fx]*\).*$/\1/p'
+}
--- /dev/null
+##/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# common functions for setting up and tearing down block device error injection
+
+_require_fail_make_request()
+{
+ [ -f "$DEBUGFS_MNT/fail_make_request/probability" ] \
+ || _notrun "$DEBUGFS_MNT/fail_make_request \
+ not found. Seems that CONFIG_FAULT_INJECTION_DEBUG_FS kernel config option not enabled"
+}
+
+_allow_fail_make_request()
+{
+ local prob="${1:-100}"
+ local times="${2:-9999999}"
+ local verbose="${3:-0}"
+
+ echo "Allow global fail_make_request feature"
+ echo "$prob" > $DEBUGFS_MNT/fail_make_request/probability
+ echo "$times" > $DEBUGFS_MNT/fail_make_request/times
+ echo "$verbose" > $DEBUGFS_MNT/fail_make_request/verbose
+}
+
+_disallow_fail_make_request()
+{
+ echo "Disallow global fail_make_request feature"
+ echo 0 > $DEBUGFS_MNT/fail_make_request/probability
+ echo 0 > $DEBUGFS_MNT/fail_make_request/times
+ echo 0 > $DEBUGFS_MNT/fail_make_request/verbose
+}
+
+_bdev_fail_make_request()
+{
+ local bdev="$1"
+ local status="$2"
+ local sysfs_bdev=$(_sysfs_dev $bdev)
+
+ echo " echo $status > $sysfs_bdev/make-it-fail" >> $seqres.full
+ echo "$status" > $sysfs_bdev/make-it-fail
+}
+
+_start_fail_scratch_dev()
+{
+ echo "Force SCRATCH_DEV device failure"
+
+ _prepare_for_eio_shutdown $SCRATCH_DEV
+ _bdev_fail_make_request $SCRATCH_DEV 1
+ [ "$USE_EXTERNAL" = yes -a ! -z "$SCRATCH_LOGDEV" ] && \
+ _bdev_fail_make_request $SCRATCH_LOGDEV 1
+ [ "$USE_EXTERNAL" = yes -a ! -z "$SCRATCH_RTDEV" ] && \
+ _bdev_fail_make_request $SCRATCH_RTDEV 1
+}
+
+_stop_fail_scratch_dev()
+{
+ echo "Make SCRATCH_DEV device operable again"
+
+ [ "$USE_EXTERNAL" = yes -a ! -z "$SCRATCH_RTDEV" ] && \
+ _bdev_fail_make_request $SCRATCH_RTDEV 0
+ [ "$USE_EXTERNAL" = yes -a ! -z "$SCRATCH_LOGDEV" ] && \
+ _bdev_fail_make_request $SCRATCH_LOGDEV 0
+ _bdev_fail_make_request $SCRATCH_DEV 0
+}
_filter_xfs_io_blocks_modified()
{
- BLOCK_SIZE=$(_get_block_size $SCRATCH_MNT)
+ BLOCK_SIZE=$(_get_file_block_size $SCRATCH_MNT)
_filter_xfs_io_units_modified "Block" $BLOCK_SIZE
}
_filter_xfs_io_pages_modified()
{
- PAGE_SIZE=$(get_page_size)
+ PAGE_SIZE=$(_get_page_size)
_filter_xfs_io_units_modified "Page" $PAGE_SIZE
}
# until the GETNEXTQUOTA ioctl came into use. Filter it out.
# But if you specify a name for ID 0, that means you want to
# deal with it by yourself, this function won't filter it out.
- _filter_quota | grep -v "^\#0 \|^(null) "
+ _filter_quota | grep -v "^#0 \|^(null) "
}
# Account for different "ln" failure messages
# verbose output
_filter_fstrim()
{
- egrep -o "[0-9]+ bytes" | $AWK_PROG '{print $1}'
+ grep -Eo "[0-9]+ bytes" | $AWK_PROG '{print $1}'
}
# Remove the ending dot appended to mount error message, util-linux 2.30
# ancient: mount: cannot remount block device <device> read-write, is write-protected
# prior to v2.30: mount: cannot remount <device> read-write, is write-protected
# v2.30 and later: mount: <mountpoint>: cannot remount <device> read-write, is write-protected.
+# v2.38 and later:
+# dmesg(1) may have more information after failed mount mount system call
#
# Now use _filter_ro_mount to unify all these differences across old & new
# util-linux versions. So the filtered format would be:
print "mount: cannot remount device read-write, is write-protected\n";
} else {
print "$_";
- }' | _filter_ending_dot
+ }' | grep -v "dmesg(1) may have more information after failed mount" | \
+ _filter_ending_dot
}
# Filter a failed mount output due to EUCLEAN and USTALE, util-linux changed
# mount: mount <device> on <mountpoint> failed: Structure needs cleaning
# v2.30 and later:
# mount: <mountpoint>: mount(2) system call failed: Structure needs cleaning.
+# v2.38 and later:
+# dmesg(1) may have more information after failed mount mount system call
#
# This is also true for ESTALE error. So let's remove all the changing parts
# and keep the 'prior to v2.21' format:
# mount: Stale file handle
_filter_error_mount()
{
- sed -e "s/mount:\(.*failed:\)/mount:/" | _filter_ending_dot
+ grep -v "dmesg(1) may have more information after failed mount" | \
+ sed -e "s/mount:\(.*failed:\)/mount:/" | _filter_ending_dot
}
# Similar to _filter_error_mount, filter a busy mount output.
# old: mount: <device> is already mounted or <mountpoint> busy
# new: mount: <mountpoint>: <device> already mounted or mount point busy.
# filtered: mount: device already mounted or mount point busy
+# v2.38 and later, filter out:
+# dmesg(1) may have more information after failed mount mount system call
_filter_busy_mount()
{
+ grep -v "dmesg(1) may have more information after failed mount" | \
sed -e "s/.*: .* already mounted or .* busy/mount: device already mounted or mount point busy/" | \
_filter_ending_dot
}
_filter_od()
{
- BLOCK_SIZE=$(_get_block_size $SCRATCH_MNT)
+ BLOCK_SIZE=$(_get_file_block_size $SCRATCH_MNT)
$AWK_PROG -v block_size=$BLOCK_SIZE '
/^[0-9]+/ {
offset = strtonum("0"$1);
sed -e "s/^bash: line 1: /bash: /"
}
-#
-# blkzone report added zone capacity to be printed from v2.37.
-# This filter will add an extra column 'cap' with the same value of
-# 'len'(zone size) for blkzone version < 2.37
-#
-# Before: start: 0x000100000, len 0x040000, wptr 0x000000 ..
-# After: start: 0x000100000, len 0x040000, cap 0x040000, wptr 0x000000 ..
-_filter_blkzone_report()
+_filter_trailing_whitespace()
{
- $AWK_PROG -F "," 'BEGIN{OFS=",";} $3 !~ /cap/ {$2=$2","$2;} {print;}' |\
- sed -e 's/len/cap/2'
+ sed -E -e "s/\s+$//"
}
# make sure this script returns success
_filter_size | _filter_btrfs_version | _filter_devid | \
_filter_zero_size | \
sed -e "s/\(Total devices\) $NUMDEVS/\1 $NUM_SUBST/g" | \
- uniq
+ uniq > $tmp.btrfs_filesystem_show
+
+ # The first two lines are Label/UUID and total devices
+ head -n 2 $tmp.btrfs_filesystem_show
+
+ # The remaining is the device list, first filter out the detailed
+ # missing device to the generic one.
+ # Then sort and uniq the result
+ tail -n +3 $tmp.btrfs_filesystem_show | \
+ sed -e "s/devid <DEVID> size 0 used 0 path MISSING/*** Some devices missing/" | \
+ sort -r | uniq
+ rm -f $tmp.btrfs_filesystem_show
}
# This eliminates all numbers, and shows only unique lines,
}
_filter_transaction_commit() {
- sed -e "/Transaction commit: none (default)/d" | \
- sed -e "s/Delete subvolume (.*commit):/Delete subvolume/g"
+ sed -e "/Transaction commit: none (default)/d" \
+ -e "s/Delete subvolume [0-9]\+ (.*commit):/Delete subvolume/g" \
+ -e "s/Delete subvolume (.*commit):/Delete subvolume/g"
}
_filter_btrfs_subvol_delete()
sed -e "s/\(clone failed:\) Operation not supported/\1 Invalid argument/g"
}
+# filter output of "btrfs inspect-internal dump-tree -t raid-stripe"
+_filter_stripe_tree()
+{
+ _filter_trailing_whitespace | _filter_btrfs_version |\
+ sed -E -e "s/leaf [0-9]+ items [0-9]+ free space [0-9]+ generation [0-9]+ owner RAID_STRIPE_TREE/leaf XXXXXXXXX items X free space XXXXX generation X owner RAID_STRIPE_TREE/" \
+ -e "s/leaf [0-9]+ flags 0x1\(WRITTEN\) backref revision 1/leaf XXXXXXXXX flags 0x1\(WRITTEN\) backref revision 1/" \
+ -e "s/checksum stored [0-9a-f]+/checksum stored <CHECKSUM>/" \
+ -e "s/checksum calced [0-9a-f]+/checksum calced <CHECKSUM>/" \
+ -e "s/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/<UUID>/" \
+ -e "s/item ([0-9]+) key \([0-9]+ RAID_STRIPE ([0-9]+)\) itemoff [0-9]+ itemsize ([0-9]+)/item \1 key \(XXXXXX RAID_STRIPE \2\) itemoff XXXXX itemsize \3/" \
+ -e "s/stripe ([0-9]+) devid ([0-9]+) physical [0-9]+/stripe \1 devid \2 physical XXXXXXXXX/" \
+ -e "s/total bytes [0-9]+/total bytes XXXXXXXX/" \
+ -e "s/bytes used [0-9]+/bytes used XXXXXX/"
+}
+
+# filter output of "btrfs balance start -[smd] convert
+_filter_balance_convert()
+{
+ _filter_scratch | \
+ sed -e "s/relocate [0-9]\+ out of [0-9]\+ chunks/relocate X out of X chunks/g"
+}
+
+# filter output of "btrfs device add"
+_filter_device_add()
+{
+ _filter_scratch | _filter_scratch_pool | \
+ sed -e "/Resetting device zones SCRATCH_DEV ([0-9]\+/d"
+
+}
+
# make sure this script returns success
/bin/true
# Modify various files after a fuzzing operation
_scratch_fuzz_modify() {
- nr="$1"
-
- test -z "${nr}" && nr=50000
- echo "+++ touch ${nr} files"
- blk_sz=$(stat -f -c '%s' ${SCRATCH_MNT})
- $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
- # try to remove append, immutable (and even dax) flag if exists
- $XFS_IO_PROG -rc 'chattr -x -i -a' "$f" > /dev/null 2>&1
- setfattr -n "user.date" -v "${date}" "$f"
- cat "/tmp/afile" >> "$f"
- mv "$f" "$f.longer"
- done
- sync
- rm -rf "/tmp/afile"
-
- echo "+++ create files"
- mkdir -p "${SCRATCH_MNT}/test.moo"
- $XFS_IO_PROG -f -c 'pwrite -S 0x80 0 65536' "${SCRATCH_MNT}/test.moo/urk" > /dev/null
- sync
+ echo "+++ stressing filesystem"
+ mkdir -p $SCRATCH_MNT/data
+ [ "$FSTYP" == "xfs" ] && _xfs_force_bdev data $SCRATCH_MNT/data
+ $FSSTRESS_PROG -n $((TIME_FACTOR * 10000)) -p $((LOAD_FACTOR * 4)) -d $SCRATCH_MNT/data
- echo "+++ remove files"
- rm -rf "${SCRATCH_MNT}/test.moo"
+ if [ "$FSTYP" = "xfs" ]; then
+ if _xfs_has_feature "$SCRATCH_MNT" realtime; then
+ mkdir -p $SCRATCH_MNT/rt
+ _xfs_force_bdev realtime $SCRATCH_MNT/rt
+ $FSSTRESS_PROG -n $((TIME_FACTOR * 10000)) -p $((LOAD_FACTOR * 4)) -d $SCRATCH_MNT/rt
+ else
+ echo "+++ xfs realtime not configured"
+ fi
+ fi
}
# Try to access files after fuzzing
esac
}
-# Filter out any keys with an array index >= 10, collapse any array range
-# ("[1-195]") to the first item, and ignore padding fields.
-__filter_xfs_db_keys() {
- sed -e '/\([a-z]*\)\[\([0-9][0-9]\+\)\].*/d' \
- -e 's/\([a-zA-Z0-9_]*\)\[\([0-9]*\)-[0-9]*\]/\1[\2]/g' \
- -e '/pad/d'
+# Expand indexed keys (i.e. arrays) into a long format so that we can filter
+# the array indices individually, and pass regular keys right through.
+#
+# For example, "u3.bmx[0-1] = [foo,bar]" is exploded into:
+# u3.bmx[0] = [foo,bar]
+# u3.bmx[1] = [foo,bar]
+#
+# Note that we restrict array indices to [0-9] to reduce fuzz runtime. The
+# minimum and maximum array indices can be changed by setting the variables
+# SCRATCH_XFS_{MIN,MAX}_ARRAY_IDX.
+#
+# Also filter padding fields.
+__explode_xfs_db_fields() {
+ local min_idx="${SCRATCH_XFS_MIN_ARRAY_IDX}"
+ local max_idx="${SCRATCH_XFS_MAX_ARRAY_IDX}"
+
+ test -z "${min_idx}" && min_idx=0
+ test -z "${max_idx}" && max_idx=9
+ test "${max_idx}" = "none" && max_idx=99999
+
+ grep ' = ' | \
+ sed -e 's/^\([.a-zA-Z0-9_]*\)\[\([0-9]*\)-\([0-9]*\)\]\(.*\) = \(.*\)$/\1[%d]\4 \2 \3 = \5/g' \
+ -e 's/^\([.a-zA-Z0-9_]*\)\[\([0-9]*\)\]\(.*\) = \(.*\)$/\1[%d]\3 \2 \2 = \4/g' | \
+ while read name col1 col2 rest; do
+ if [[ "${name}" == *pad* ]]; then
+ continue
+ fi
+
+ if [ "${col1}" = "=" ]; then
+ echo "${name} ${col1} ${col2} ${rest}"
+ continue
+ fi
+
+ test "${min_idx}" -gt "${col1}" && col1="${min_idx}"
+ test "${max_idx}" -lt "${col2}" && col2="${max_idx}"
+
+ seq "${col1}" "${col2}" | while read idx; do
+ printf "${name} %s\n" "${idx}" "${rest}"
+ done
+ done
+}
+
+# Filter out metadata fields that are completely controlled by userspace
+# or are arbitrary bit sequences. In other words, fields where the filesystem
+# does no validation.
+__filter_unvalidated_xfs_db_fields() {
+ sed -e '/\.sec/d' \
+ -e '/\.nsec/d' \
+ -e '/^lsn$/d' \
+ -e '/\.lsn/d' \
+ -e '/^core.flushiter/d' \
+ -e '/^core.dmevmask/d' \
+ -e '/^core.dmstate/d' \
+ -e '/^core.gen/d' \
+ -e '/^core.prealloc/d' \
+ -e '/^core.immutable/d' \
+ -e '/^core.append/d' \
+ -e '/^core.sync/d' \
+ -e '/^core.noatime/d' \
+ -e '/^core.nodump/d' \
+ -e '/^core.nodefrag/d' \
+ -e '/^v3.dax/d' \
+ -e '/^nvlist.*value/d' \
+ -e '/^entries.*root/d' \
+ -e '/^entries.*secure/d' \
+ -e '/^a.sfattr.list.*value/d' \
+ -e '/^a.sfattr.list.*root/d' \
+ -e '/^a.sfattr.list.*secure/d'
}
# Filter the xfs_db print command's field debug information
if [ -z "${filter}" ] || [ "${filter}" = "nofilter" ]; then
filter='^'
fi
- grep ' = ' | while read key equals value; do
- fuzzkey="$(echo "${key}" | __filter_xfs_db_keys)"
+ __explode_xfs_db_fields | while read key equals value; do
+ fuzzkey="$(echo "${key}")"
if [ -z "${fuzzkey}" ]; then
continue
elif [[ "${value}" == "["* ]]; then
echo "${value}" | sed -e 's/^.//g' -e 's/.$//g' -e 's/,/\n/g' | while read subfield; do
echo "${fuzzkey}.${subfield}"
- done | __filter_xfs_db_keys
+ done
else
echo "${fuzzkey}"
fi
- done | egrep "${filter}"
+ done | grep -E "${filter}" | __filter_unvalidated_xfs_db_fields
+}
+
+# Dump the current contents of a metadata object.
+# All arguments are xfs_db commands to locate the metadata.
+_scratch_xfs_dump_metadata() {
+ local cmds=()
+ for arg in "$@"; do
+ cmds+=("-c" "${arg}")
+ done
+ _scratch_xfs_db "${cmds[@]}" -c print
+}
+
+# Decide from the output of the xfs_db "stack" command if the debugger's io
+# cursor is pointed at a block that is an unstructured data format (blob).
+__scratch_xfs_detect_blob_from_stack() {
+ grep -q -E 'inode.*, type (data|rtsummary|rtbitmap)'
}
# Navigate to some part of the filesystem and print the field info.
-# The first argument is an egrep filter for the fields
+# The first argument is an grep filter for the fields
# The rest of the arguments are xfs_db commands to locate the metadata.
_scratch_xfs_list_metadata_fields() {
filter="$1"
for arg in "$@"; do
cmds+=("-c" "${arg}")
done
- _scratch_xfs_db "${cmds[@]}" -c print | __filter_xfs_db_print_fields "${filter}"
+
+ # Does the path argument point towards something that is an
+ # unstructured blob?
+ if _scratch_xfs_db "${cmds[@]}" -c stack 2>/dev/null | \
+ __scratch_xfs_detect_blob_from_stack; then
+ echo "<blob>"
+ return
+ fi
+
+ _scratch_xfs_db "${cmds[@]}" -c print | \
+ __filter_xfs_db_print_fields "${filter}"
}
# Fuzz a metadata field
return 0
}
+# List the fuzzing verbs available for unstructured blobs
+__scratch_xfs_list_blob_fuzz_verbs() {
+ cat << ENDL
+zeroes
+ones
+firstbit
+middlebit
+lastbit
+random
+ENDL
+}
+
+# Fuzz a metadata blob
+# The first arg is a blob fuzzing verb
+# The rest of the arguments are xfs_db commands to find the metadata.
+_scratch_xfs_fuzz_metadata_blob() {
+ local fuzzverb="$1"
+ shift
+ local trashcmd=(blocktrash -z)
+
+ local cmds=()
+ for arg in "$@"; do
+ cmds+=("-c" "${arg}")
+ done
+
+ local bytecount=$(_scratch_xfs_db "${cmds[@]}" -c "stack" | grep 'byte.*length' | awk '{print $5}')
+ local bitmax=$((bytecount * 8))
+
+ case "${fuzzverb}" in
+ "zeroes")
+ trashcmd+=(-0 -o 0 -x "${bitmax}" -y "${bitmax}");;
+ "ones")
+ trashcmd+=(-1 -o 0 -x "${bitmax}" -y "${bitmax}");;
+ "firstbit")
+ trashcmd+=(-2 -o 0 -x 1 -y 1);;
+ "middlebit")
+ trashcmd+=(-2 -o $((bitmax / 2)) -x 1 -y 1);;
+ "lastbit")
+ trashcmd+=(-2 -o "${bitmax}" -x 1 -y 1);;
+ "random")
+ trashcmd+=(-3 -o 0 -x "${bitmax}" -y "${bitmax}");;
+ *)
+ echo "Unknown blob fuzz verb \"${fuzzverb}\"."
+ return 1
+ ;;
+ esac
+
+ trashcmd="${trashcmd[@]}"
+ oldval="$(_scratch_xfs_get_metadata_field "" "$@")"
+ while true; do
+ _scratch_xfs_db -x "${cmds[@]}" -c "${trashcmd}"
+ echo
+ newval="$(_scratch_xfs_get_metadata_field "" "$@" 2> /dev/null)"
+ if [ "${fuzzverb}" != "random" ] || [ "${oldval}" != "${newval}" ]; then
+ break;
+ fi
+ done
+ if [ "${oldval}" = "${newval}" ]; then
+ echo "Blob already set to new value, skipping test."
+ return 1
+ fi
+ return 0
+}
+
# Try to forcibly unmount the scratch fs
__scratch_xfs_fuzz_unmount()
{
# Restore metadata to scratch device prior to field-fuzzing.
__scratch_xfs_fuzz_mdrestore()
{
- test -e "${POPULATE_METADUMP}" || _fail "Need to set POPULATE_METADUMP"
-
__scratch_xfs_fuzz_unmount
- xfs_mdrestore "${POPULATE_METADUMP}" "${SCRATCH_DEV}"
+ _xfs_mdrestore "${POPULATE_METADUMP}" "${SCRATCH_DEV}" || \
+ _fail "${POPULATE_METADUMP}: Could not find metadump to restore?"
}
__fuzz_notify() {
- echo "$@"
- test -w /dev/ttyprintk && echo "$@" >> /dev/ttyprintk
+ echo '========================================'
+ echo "$*"
+ echo '========================================'
+ _kernlog "$*"
}
-# Fuzz one field of some piece of metadata.
-# First arg is the field name
-# Second arg is the fuzz verb (ones, zeroes, random, add, sub...)
-# Third arg is the repair mode (online, offline, both, none)
-__scratch_xfs_fuzz_field_test() {
- field="$1"
- fuzzverb="$2"
- repair="$3"
- shift; shift; shift
+# Perform the online repair part of a fuzz test.
+__scratch_xfs_fuzz_field_online() {
+ local fuzz_action="$1"
- # Set the new field value
- __fuzz_notify "+ Fuzz ${field} = ${fuzzverb}"
- echo "========================"
- _scratch_xfs_fuzz_metadata_field "${field}" ${fuzzverb} "$@"
+ # Mount or else we can't do anything online
+ __fuzz_notify "+ Mount filesystem to try online repair"
+ _try_scratch_mount 2>&1
res=$?
- test $res -ne 0 && return
+ if [ $res -ne 0 ]; then
+ (>&2 echo "${fuzz_action}: mount failed ($res).")
+ return 1
+ fi
- # Try to catch the error with scrub
- echo "+ Try to catch the error"
- _try_scratch_mount 2>&1
+ # Make sure online scrub will catch whatever we fuzzed
+ __fuzz_notify "++ Detect fuzzed field (online)"
+ _scratch_scrub -n -a 1 -e continue 2>&1
res=$?
- if [ $res -eq 0 ]; then
- # Try an online scrub unless we're fuzzing ag 0's sb,
- # which scrub doesn't know how to fix.
- if [ "${repair}" != "none" ]; then
- echo "++ Online scrub"
- if [ "$1" != "sb 0" ]; then
- _scratch_scrub -n -a 1 -e continue 2>&1
- res=$?
- test $res -eq 0 && \
- (>&2 echo "scrub didn't fail with ${field} = ${fuzzverb}.")
- fi
+ test $res -eq 0 && \
+ (>&2 echo "${fuzz_action}: online scrub didn't fail.")
+
+ # Does the health status report reflect the corruption?
+ if [ $res -ne 0 ]; then
+ __fuzz_notify "++ Detect fuzzed field ill-health report"
+ _check_xfs_health $SCRATCH_MNT 2>&1
+ res=$?
+ test $res -ne 1 && \
+ (>&2 echo "${fuzz_action}: online health check failed ($res).")
+ fi
+
+ # Try fixing the filesystem online
+ __fuzz_notify "++ Try to repair filesystem (online)"
+ _scratch_scrub 2>&1
+ res=$?
+ test $res -ne 0 && \
+ (>&2 echo "${fuzz_action}: online repair failed ($res).")
+
+ # Online scrub should pass now
+ __fuzz_notify "++ Make sure error is gone (online)"
+ _scratch_scrub -n -a 1 -e continue 2>&1
+ res=$?
+ test $res -ne 0 && \
+ (>&2 echo "${fuzz_action}: online re-scrub failed ($res).")
+
+ __scratch_xfs_fuzz_unmount
+
+ # Offline scrub should pass now
+ __fuzz_notify "+ Make sure error is gone (offline)"
+ _scratch_xfs_repair -P -n 2>&1
+ res=$?
+ test $res -ne 0 && \
+ (>&2 echo "${fuzz_action}: offline re-scrub failed ($res).")
+
+ return 0
+}
+
+# Perform the offline repair part of a fuzz test.
+__scratch_xfs_fuzz_field_offline() {
+ local fuzz_action="$1"
+
+ # Make sure offline scrub will catch whatever we fuzzed
+ __fuzz_notify "+ Detect fuzzed field (offline)"
+ _scratch_xfs_repair -P -n 2>&1
+ res=$?
+ test $res -eq 0 && \
+ (>&2 echo "${fuzz_action}: offline scrub didn't fail.")
+
+ # Make sure xfs_repair catches at least as many things as the old
+ # xfs_check did.
+ if [ -n "${SCRATCH_XFS_FUZZ_CHECK}" ]; then
+ __fuzz_notify "+ Detect fuzzed field (xfs_check)"
+ _scratch_xfs_check 2>&1
+ res1=$?
+ if [ $res1 -ne 0 ] && [ $res -eq 0 ]; then
+ (>&2 echo "${fuzz_action}: xfs_repair passed but xfs_check failed ($res1).")
fi
+ fi
+
+ # Repair the filesystem offline
+ __fuzz_notify "+ Try to repair the filesystem (offline)"
+ _repair_scratch_fs -P 2>&1
+ res=$?
+ test $res -ne 0 && \
+ (>&2 echo "${fuzz_action}: offline repair failed ($res).")
+
+ # See if repair finds a clean fs
+ __fuzz_notify "+ Make sure error is gone (offline)"
+ _scratch_xfs_repair -P -n 2>&1
+ res=$?
+ test $res -ne 0 && \
+ (>&2 echo "${fuzz_action}: offline re-scrub failed ($res).")
+
+ return 0
+}
+
+# Perform the no-repair part of a fuzz test.
+__scratch_xfs_fuzz_field_norepair() {
+ local fuzz_action="$1"
+
+ # Make sure offline scrub will catch whatever we fuzzed
+ __fuzz_notify "+ Detect fuzzed field (offline)"
+ _scratch_xfs_repair -P -n 2>&1
+ res=$?
+ test $res -eq 0 && \
+ (>&2 echo "${fuzz_action}: offline scrub didn't fail.")
+
+ # Mount or else we can't do anything in norepair mode
+ __fuzz_notify "+ Mount filesystem to try online scan"
+ _try_scratch_mount 2>&1
+ res=$?
+ if [ $res -ne 0 ]; then
+ (>&2 echo "${fuzz_action}: mount failed ($res).")
+ return 1
+ fi
+
+ # Skip scrub and health check if scrub is not supported
+ if ! _supports_xfs_scrub $SCRATCH_MNT $SCRATCH_DEV; then
+ __scratch_xfs_fuzz_unmount
+ return 0
+ fi
+
+ # Make sure online scrub will catch whatever we fuzzed
+ __fuzz_notify "++ Detect fuzzed field (online)"
+ _scratch_scrub -n -a 1 -e continue 2>&1
+ res=$?
+ test $res -eq 0 && \
+ (>&2 echo "${fuzz_action}: online scrub didn't fail.")
+
+ # Does the health status report reflect the corruption?
+ if [ $res -ne 0 ]; then
+ __fuzz_notify "++ Detect fuzzed field ill-health report"
+ _check_xfs_health $SCRATCH_MNT 2>&1
+ res=$?
+ test $res -ne 1 && \
+ (>&2 echo "${fuzz_action}: online health check failed ($res).")
+ fi
+
+ __scratch_xfs_fuzz_unmount
+
+ return 0
+}
+
+# Perform the online-then-offline repair part of a fuzz test.
+__scratch_xfs_fuzz_field_both() {
+ local fuzz_action="$1"
+
+ # Make sure offline scrub will catch whatever we fuzzed
+ __fuzz_notify "+ Detect fuzzed field (offline)"
+ _scratch_xfs_repair -P -n 2>&1
+ res=$?
+ test $res -eq 0 && \
+ (>&2 echo "${fuzz_action}: offline scrub didn't fail.")
- # Try fixing the filesystem online?!
- if [ "${repair}" = "online" ] || [ "${repair}" = "both" ]; then
- __fuzz_notify "++ Try to repair filesystem online"
- _scratch_scrub 2>&1
+ # Mount or else we can't do anything in both repair mode
+ __fuzz_notify "+ Mount filesystem to try both repairs"
+ _try_scratch_mount 2>&1
+ res=$?
+ if [ $res -ne 0 ]; then
+ (>&2 echo "${fuzz_action}: mount failed ($res).")
+ else
+ # Make sure online scrub will catch whatever we fuzzed
+ __fuzz_notify "++ Detect fuzzed field (online)"
+ _scratch_scrub -n -a 1 -e continue 2>&1
+ res=$?
+ test $res -eq 0 && \
+ (>&2 echo "${fuzz_action}: online scrub didn't fail.")
+
+ # Does the health status report reflect the corruption?
+ if [ $res -ne 0 ]; then
+ __fuzz_notify "++ Detect fuzzed field ill-health report"
+ _check_xfs_health $SCRATCH_MNT 2>&1
res=$?
- test $res -ne 0 && \
- (>&2 echo "online repair failed ($res) with ${field} = ${fuzzverb}.")
+ test $res -ne 1 && \
+ (>&2 echo "${fuzz_action}: online health check failed ($res).")
fi
+ # Try fixing the filesystem online
+ __fuzz_notify "++ Try to repair filesystem (online)"
+ _scratch_scrub 2>&1
+ res=$?
+ test $res -ne 0 && \
+ (>&2 echo "${fuzz_action}: online repair failed ($res).")
+
__scratch_xfs_fuzz_unmount
- elif [ "${repair}" = "online" ] || [ "${repair}" = "both" ]; then
- (>&2 echo "mount failed ($res) with ${field} = ${fuzzverb}.")
fi
- # Repair the filesystem offline?
- if [ "${repair}" = "offline" ] || [ "${repair}" = "both" ]; then
- echo "+ Try to repair the filesystem offline"
- _repair_scratch_fs 2>&1
+ # Repair the filesystem offline if online repair failed?
+ if [ $res -ne 0 ]; then
+ __fuzz_notify "+ Try to repair the filesystem (offline)"
+ _repair_scratch_fs -P 2>&1
res=$?
test $res -ne 0 && \
- (>&2 echo "offline repair failed ($res) with ${field} = ${fuzzverb}.")
+ (>&2 echo "${fuzz_action}: offline repair failed ($res).")
fi
# See if repair finds a clean fs
- if [ "${repair}" != "none" ]; then
- echo "+ Make sure error is gone (offline)"
- _scratch_xfs_repair -n 2>&1
- res=$?
- test $res -ne 0 && \
- (>&2 echo "offline re-scrub ($res) with ${field} = ${fuzzverb}.")
+ __fuzz_notify "+ Make sure error is gone (offline)"
+ _scratch_xfs_repair -P -n 2>&1
+ res=$?
+ test $res -ne 0 && \
+ (>&2 echo "${fuzz_action}: offline re-scrub failed ($res).")
+
+ # Mount so that we can see what scrub says after we've fixed the fs
+ __fuzz_notify "+ Re-mount filesystem to re-try online scan"
+ _try_scratch_mount 2>&1
+ res=$?
+ if [ $res -ne 0 ]; then
+ (>&2 echo "${fuzz_action}: mount failed ($res).")
+ return 1
fi
- # See if scrub finds a clean fs
- echo "+ Make sure error is gone (online)"
+ # Online scrub should pass now
+ __fuzz_notify "++ Make sure error is gone (online)"
+ _scratch_scrub -n -a 1 -e continue 2>&1
+ res=$?
+ test $res -ne 0 && \
+ (>&2 echo "${fuzz_action}: online re-scrub failed ($res).")
+
+ __scratch_xfs_fuzz_unmount
+
+ return 0
+}
+
+# Assess the state of the filesystem after a repair strategy has been run by
+# trying to make changes to it.
+_scratch_xfs_fuzz_field_modifyfs() {
+ local fuzz_action="$1"
+ local repair="$2"
+
+ # Try to mount the filesystem so that we can make changes
+ __fuzz_notify "+ Mount filesystem to make changes"
_try_scratch_mount 2>&1
res=$?
- if [ $res -eq 0 ]; then
- # Try an online scrub unless we're fuzzing ag 0's sb,
- # which scrub doesn't know how to fix.
- if [ "${repair}" != "none" ]; then
- echo "++ Online scrub"
- if [ "$1" != "sb 0" ]; then
- _scratch_scrub -n -e continue 2>&1
- res=$?
- test $res -ne 0 && \
- (>&2 echo "online re-scrub ($res) with ${field} = ${fuzzverb}.")
- fi
- fi
+ if [ $res -ne 0 ]; then
+ (>&2 echo "${fuzz_action}: pre-mod mount failed ($res).")
+ return $res
+ fi
+
+ # Try modifying the filesystem again
+ __fuzz_notify "++ Try to write filesystem again"
+ _scratch_fuzz_modify 2>&1
- # Try modifying the filesystem again!
- __fuzz_notify "++ Try to write filesystem again"
- _scratch_fuzz_modify 100 2>&1
+ # If we didn't repair anything, there's no point in checking further,
+ # the fs is still corrupt.
+ if [ "${repair}" = "none" ]; then
__scratch_xfs_fuzz_unmount
+ return 0
+ fi
+
+ # Run an online check to make sure the fs is still ok, unless we
+ # are running the norepair strategy.
+ __fuzz_notify "+ Re-check the filesystem (online)"
+ _scratch_scrub -n -e continue 2>&1
+ res=$?
+ test $res -ne 0 && \
+ (>&2 echo "${fuzz_action}: online post-mod scrub failed ($res).")
+
+ __scratch_xfs_fuzz_unmount
+
+ # Run an offline check to make sure the fs is still ok, unless we
+ # are running the norepair strategy.
+ __fuzz_notify "+ Re-check the filesystem (offline)"
+ _scratch_xfs_repair -P -n 2>&1
+ res=$?
+ test $res -ne 0 && \
+ (>&2 echo "${fuzz_action}: offline post-mod scrub failed ($res).")
+
+ return 0
+}
+
+# Fuzz one field of some piece of metadata.
+# First arg is the field name
+# Second arg is the fuzz verb (ones, zeroes, random, add, sub...)
+# Third arg is the repair mode (online, offline, both, none)
+__scratch_xfs_fuzz_field_test() {
+ field="$1"
+ fuzzverb="$2"
+ repair="$3"
+ shift; shift; shift
+
+ # Set the new field value
+ __fuzz_notify "+ Fuzz ${field} = ${fuzzverb}"
+ if [ "$field" = "<blob>" ]; then
+ _scratch_xfs_fuzz_metadata_blob ${fuzzverb} "$@"
else
- (>&2 echo "re-mount failed ($res) with ${field} = ${fuzzverb}.")
+ _scratch_xfs_fuzz_metadata_field "${field}" ${fuzzverb} "$@"
fi
+ res=$?
+ test $res -ne 0 && return
- # See if repair finds a clean fs
- if [ "${repair}" != "none" ]; then
- echo "+ Re-check the filesystem (offline)"
- _scratch_xfs_repair -n 2>&1
+ # Try to catch the error with whatever repair strategy we picked.
+ # The fs should not be mounted before or after the strategy call.
+ local fuzz_action="${field} = ${fuzzverb}"
+ case "${repair}" in
+ "online")
+ __scratch_xfs_fuzz_field_online "${fuzz_action}"
res=$?
- test $res -ne 0 && \
- (>&2 echo "re-repair failed ($res) with ${field} = ${fuzzverb}.")
- fi
+ ;;
+ "offline")
+ __scratch_xfs_fuzz_field_offline "${fuzz_action}"
+ res=$?
+ ;;
+ "none")
+ __scratch_xfs_fuzz_field_norepair "${fuzz_action}"
+ res=$?
+ ;;
+ "both")
+ __scratch_xfs_fuzz_field_both "${fuzz_action}"
+ res=$?
+ ;;
+ *)
+ (>&2 echo "unknown repair strategy ${repair}.")
+ res=2
+ ;;
+ esac
+ test $res -eq 0 || return $res
+
+ # See what happens when we modify the fs
+ _scratch_xfs_fuzz_field_modifyfs "${fuzz_action}" "${repair}"
+ return $?
}
# Make sure we have all the pieces we need for field fuzzing
_require_xfs_db_command "fuzz"
}
+# Sets the array SCRATCH_XFS_DIR_FUZZ_TYPES to the list of directory formats
+# available for fuzzing. Each list item must match one of the /S_IFDIR.FMT_*
+# files created by the fs population code. Users can override this by setting
+# SCRATCH_XFS_LIST_FUZZ_DIRTYPE in the environment. BTREE is omitted here
+# because that refers to the fork format and does not affect the directory
+# structure at all.
+_scratch_xfs_set_dir_fuzz_types() {
+ if [ -n "${SCRATCH_XFS_LIST_FUZZ_DIRTYPE}" ]; then
+ mapfile -t SCRATCH_XFS_DIR_FUZZ_TYPES < \
+ <(echo "${SCRATCH_XFS_LIST_FUZZ_DIRTYPE}" | tr '[ ,]' '[\n\n]')
+ return
+ fi
+
+ SCRATCH_XFS_DIR_FUZZ_TYPES=(BLOCK LEAF LEAFN NODE)
+}
+
+# Sets the array SCRATCH_XFS_XATTR_FUZZ_TYPES to the list of xattr formats
+# available for fuzzing. Each list item must match one of the /ATTR.FMT_*
+# files created by the fs population code. Users can override this by setting
+# SCRATCH_XFS_LIST_FUZZ_XATTRTYPE in the environment. BTREE is omitted here
+# because that refers to the fork format and does not affect the extended
+# attribute structure at all.
+_scratch_xfs_set_xattr_fuzz_types() {
+ if [ -n "${SCRATCH_XFS_LIST_FUZZ_XATTRTYPE}" ]; then
+ mapfile -t SCRATCH_XFS_XATTR_FUZZ_TYPES < \
+ <(echo "${SCRATCH_XFS_LIST_FUZZ_XATTRTYPE}" | tr '[ ,]' '[\n\n]')
+ return
+ fi
+
+ SCRATCH_XFS_XATTR_FUZZ_TYPES=(EXTENTS_REMOTE3K EXTENTS_REMOTE4K LEAF NODE)
+}
+
# Grab the list of available fuzzing verbs
_scratch_xfs_list_fuzz_verbs() {
if [ -n "${SCRATCH_XFS_LIST_FUZZ_VERBS}" ]; then
echo "${SCRATCH_XFS_LIST_FUZZ_VERBS}" | tr '[ ,]' '[\n\n]'
return;
fi
- _scratch_xfs_db -x -c 'sb 0' -c 'fuzz' | grep '^Fuzz commands:' | \
- sed -e 's/[,.]//g' -e 's/Fuzz commands: //g' -e 's/ /\n/g'
+
+ local cmds=()
+ for arg in "$@"; do
+ cmds+=("-c" "${arg}")
+ done
+ test "${#cmds[@]}" -eq 0 && cmds=('-c' 'sb 0')
+
+ # Does the path argument point towards something that is an
+ # unstructured blob?
+ if _scratch_xfs_db "${cmds[@]}" -c stack 2>/dev/null | \
+ __scratch_xfs_detect_blob_from_stack; then
+ __scratch_xfs_list_blob_fuzz_verbs
+ return
+ fi
+
+ _scratch_xfs_db -x "${cmds[@]}" -c 'fuzz' | grep '^Fuzz commands:' | \
+ sed -e 's/[,.]//g' -e 's/Fuzz commands: //g' -e 's/ /\n/g' | \
+ grep -v '^random$'
}
# Fuzz some of the fields of some piece of metadata
-# The first argument is an egrep filter for the field names
+# The first argument is an grep filter for the field names
# The second argument is the repair mode (online, offline, both)
# The rest of the arguments are xfs_db commands to locate the metadata.
#
shift; shift
fields="$(_scratch_xfs_list_metadata_fields "${filter}" "$@")"
- verbs="$(_scratch_xfs_list_fuzz_verbs)"
- echo "Fields we propose to fuzz under: $@"
+ verbs="$(_scratch_xfs_list_fuzz_verbs "$@")"
+ echo "Fields we propose to fuzz with the \"${repair}\" repair strategy: $@"
echo $(echo "${fields}")
echo "Verbs we propose to fuzz with:"
echo $(echo "${verbs}")
+ echo "Current metadata object state:"
+ _scratch_xfs_dump_metadata "$@"
# Always capture full core dumps from crashing tools
ulimit -c unlimited
+ _xfs_skip_online_rebuild
+ _xfs_skip_offline_rebuild
+
echo "${fields}" | while read field; do
echo "${verbs}" | while read fuzzverb; do
__scratch_xfs_fuzz_mdrestore
__scratch_xfs_fuzz_field_test "${field}" "${fuzzverb}" "${repair}" "$@"
+
+ # Collect compresssed coredumps in the test results
+ # directory if the sysadmin didn't override the default
+ # coredump strategy.
+ for i in core core.*; do
+ test -f "$i" || continue
+ _save_coredump "$i"
+ done
+ done
+ done
+}
+
+# Functions to race fsstress, fs freeze, and xfs metadata scrubbing against
+# each other to shake out bugs in xfs online repair.
+
+# Filter freeze and thaw loop output so that we don't tarnish the golden output
+# if the kernel temporarily won't let us freeze.
+__stress_freeze_filter_output() {
+ _filter_scratch | \
+ sed -e '/Device or resource busy/d' \
+ -e '/Invalid argument/d'
+}
+
+# Filter scrub output so that we don't tarnish the golden output if the fs is
+# too busy to scrub. Note: Tests should _notrun if the scrub type is not
+# supported. Callers can provide extra strings to filter out as function
+# arguments.
+__stress_scrub_filter_output() {
+ local extra_args=()
+
+ for arg in "$@"; do
+ extra_args+=(-e "/${arg}/d")
+ done
+
+ _filter_scratch | \
+ sed -e '/Device or resource busy/d' \
+ -e '/Optimization possible/d' \
+ -e '/No space left on device/d' \
+ "${extra_args[@]}"
+}
+
+# Decide if the scratch filesystem is still alive.
+__stress_scrub_scratch_alive() {
+ # If we can't stat the scratch filesystem, there's a reasonably good
+ # chance that the fs shut down, which is not good.
+ stat "$SCRATCH_MNT" &>/dev/null
+}
+
+# Decide if we want to keep running stress tests. The first argument is the
+# stop time, and second argument is the path to the sentinel file.
+__stress_scrub_running() {
+ test -e "$2" && test "$(date +%s)" -lt "$1" && __stress_scrub_scratch_alive
+}
+
+# Run fs freeze and thaw in a tight loop.
+__stress_scrub_freeze_loop() {
+ local end="$1"
+ local runningfile="$2"
+
+ while __stress_scrub_running "$end" "$runningfile"; do
+ $XFS_IO_PROG -x -c 'freeze' -c 'thaw' $SCRATCH_MNT 2>&1 | \
+ __stress_freeze_filter_output
+ done
+}
+
+# Run individual xfs_io commands in a tight loop.
+__stress_xfs_io_loop() {
+ local end="$1"
+ local runningfile="$2"
+ shift; shift
+
+ local xfs_io_args=()
+ for arg in "$@"; do
+ xfs_io_args+=('-c' "$arg")
+ done
+
+ while __stress_scrub_running "$end" "$runningfile"; do
+ $XFS_IO_PROG -x "${xfs_io_args[@]}" "$SCRATCH_MNT" \
+ > /dev/null 2>> $seqres.full
+ done
+}
+
+# Run individual XFS online fsck commands in a tight loop with xfs_io.
+__stress_one_scrub_loop() {
+ local end="$1"
+ local runningfile="$2"
+ local scrub_tgt="$3"
+ local scrub_startat="$4"
+ local start_agno="$5"
+ shift; shift; shift; shift; shift
+ local agcount="$(_xfs_mount_agcount $SCRATCH_MNT)"
+
+ local xfs_io_args=()
+ for arg in "$@"; do
+ if [ -n "$SCRUBSTRESS_USE_FORCE_REBUILD" ]; then
+ arg="$(echo "$arg" | sed -e 's/^repair/repair -R/g')"
+ fi
+ if echo "$arg" | grep -q -w '%agno%'; then
+ # Substitute the AG number
+ for ((agno = start_agno; agno < agcount; agno++)); do
+ local ag_arg="$(echo "$arg" | sed -e "s|%agno%|$agno|g")"
+ xfs_io_args+=('-c' "$ag_arg")
+ done
+ else
+ xfs_io_args+=('-c' "$arg")
+ fi
+ done
+
+ local extra_filters=()
+ case "$scrub_tgt" in
+ "%file%"|"%datafile%"|"%attrfile%")
+ extra_filters+=('No such file or directory' 'No such device or address')
+ ;;
+ "%dir%")
+ extra_filters+=('No such file or directory' 'Not a directory')
+ ;;
+ "%regfile%"|"%cowfile%")
+ extra_filters+=('No such file or directory')
+ ;;
+ esac
+
+ local target_cmd=(echo "$scrub_tgt")
+ case "$scrub_tgt" in
+ "%file%") target_cmd=($here/src/xfsfind -q "$SCRATCH_MNT");;
+ "%attrfile%") target_cmd=($here/src/xfsfind -qa "$SCRATCH_MNT");;
+ "%datafile%") target_cmd=($here/src/xfsfind -qb "$SCRATCH_MNT");;
+ "%dir%") target_cmd=($here/src/xfsfind -qd "$SCRATCH_MNT");;
+ "%regfile%") target_cmd=($here/src/xfsfind -qr "$SCRATCH_MNT");;
+ "%cowfile%") target_cmd=($here/src/xfsfind -qs "$SCRATCH_MNT");;
+ esac
+
+ while __stress_scrub_running "$scrub_startat" "$runningfile"; do
+ sleep 1
+ done
+
+ while __stress_scrub_running "$end" "$runningfile"; do
+ readarray -t fnames < <("${target_cmd[@]}" 2>> $seqres.full)
+ for fname in "${fnames[@]}"; do
+ $XFS_IO_PROG -x "${xfs_io_args[@]}" "$fname" 2>&1 | \
+ __stress_scrub_filter_output "${extra_filters[@]}"
+ __stress_scrub_running "$end" "$runningfile" || break
done
done
}
+
+# Run xfs_scrub online fsck in a tight loop.
+__stress_xfs_scrub_loop() {
+ local end="$1"
+ local runningfile="$2"
+ local scrub_startat="$3"
+ shift; shift; shift
+ local sigint_ret="$(( $(kill -l SIGINT) + 128 ))"
+ local scrublog="$tmp.scrub"
+
+ while __stress_scrub_running "$scrub_startat" "$runningfile"; do
+ sleep 1
+ done
+
+ while __stress_scrub_running "$end" "$runningfile"; do
+ _scratch_scrub "$@" &> $scrublog
+ res=$?
+ if [ "$res" -eq "$sigint_ret" ]; then
+ # Ignore SIGINT because the cleanup function sends
+ # that to terminate xfs_scrub
+ res=0
+ fi
+ echo "xfs_scrub exits with $res at $(date)" >> $seqres.full
+ if [ "$res" -ge 128 ]; then
+ # Report scrub death due to fatal signals
+ echo "xfs_scrub died with SIG$(kill -l $res)"
+ cat $scrublog >> $seqres.full 2>/dev/null
+ elif [ "$((res & 0x1))" -gt 0 ]; then
+ # Report uncorrected filesystem errors
+ echo "xfs_scrub reports uncorrected errors:"
+ grep -E '(Repair unsuccessful;|Corruption:)' $scrublog
+ cat $scrublog >> $seqres.full 2>/dev/null
+ fi
+ rm -f $scrublog
+ done
+}
+
+# Clean the scratch filesystem between rounds of fsstress if there is 2%
+# available space or less because that isn't an interesting stress test.
+#
+# Returns 0 if we cleared anything, and 1 if we did nothing.
+__stress_scrub_clean_scratch() {
+ local used_pct="$(_used $SCRATCH_DEV)"
+
+ test "$used_pct" -lt 98 && return 1
+
+ echo "Clearing scratch fs at $(date)" >> $seqres.full
+ rm -r -f $SCRATCH_MNT/p*
+ return 0
+}
+
+# Run fsx while we're testing online fsck.
+__stress_scrub_fsx_loop() {
+ local end="$1"
+ local runningfile="$2"
+ local remount_period="$3"
+ local stress_tgt="$4" # ignored
+ local focus=(-q -X) # quiet, validate file contents
+
+ # As of November 2022, 2 million fsx ops should be enough to keep
+ # any filesystem busy for a couple of hours.
+ focus+=(-N 2000000)
+ focus+=(-o $((128000 * LOAD_FACTOR)) )
+ focus+=(-l $((600000 * LOAD_FACTOR)) )
+
+ local args="$FSX_AVOID ${focus[@]} ${SCRATCH_MNT}/fsx.$seq"
+ echo "Running $here/ltp/fsx $args" >> $seqres.full
+
+ if [ -n "$remount_period" ]; then
+ local mode="rw"
+ local rw_arg=""
+ while __stress_scrub_running "$end" "$runningfile"; do
+ # Need to recheck running conditions if we cleared
+ # anything.
+ test "$mode" = "rw" && __stress_scrub_clean_scratch && continue
+
+ timeout -s TERM "$remount_period" $here/ltp/fsx \
+ $args $rw_arg >> $seqres.full
+ res=$?
+ echo "$mode fsx exits with $res at $(date)" >> $seqres.full
+ if [ "$res" -ne 0 ] && [ "$res" -ne 124 ]; then
+ # Stop if fsstress returns error. Mask off
+ # the magic code 124 because that is how the
+ # timeout(1) program communicates that we ran
+ # out of time.
+ break;
+ fi
+ if [ "$mode" = "rw" ]; then
+ mode="ro"
+ rw_arg="-t 0 -w 0 -FHzCIJBE0"
+ else
+ mode="rw"
+ rw_arg=""
+ fi
+
+ # Try remounting until we get the result we wanted
+ while ! _scratch_remount "$mode" &>/dev/null && \
+ __stress_scrub_running "$end" "$runningfile"; do
+ sleep 0.2
+ done
+ done
+ rm -f "$runningfile"
+ return 0
+ fi
+
+ while __stress_scrub_running "$end" "$runningfile"; do
+ # Need to recheck running conditions if we cleared anything
+ __stress_scrub_clean_scratch && continue
+ $here/ltp/fsx $args >> $seqres.full
+ echo "fsx exits with $? at $(date)" >> $seqres.full
+ done
+ rm -f "$runningfile"
+}
+
+# Run fsstress while we're testing online fsck.
+__stress_scrub_fsstress_loop() {
+ local end="$1"
+ local runningfile="$2"
+ local remount_period="$3"
+ local stress_tgt="$4"
+ local focus=()
+
+ case "$stress_tgt" in
+ "parent")
+ focus+=('-z')
+
+ # Create a directory tree very gradually
+ for op in creat link mkdir; do
+ focus+=('-f' "${op}=2")
+ done
+ focus+=('-f' 'unlink=1' '-f' 'rmdir=1')
+
+ # But do a lot of renames to cycle parent pointers
+ for op in rename rnoreplace rexchange; do
+ focus+=('-f' "${op}=40")
+ done
+ ;;
+ "dir")
+ focus+=('-z')
+
+ # Create a directory tree rapidly
+ for op in creat link mkdir mknod symlink; do
+ focus+=('-f' "${op}=8")
+ done
+ focus+=('-f' 'rmdir=2' '-f' 'unlink=8')
+
+ # Rename half as often
+ for op in rename rnoreplace rexchange; do
+ focus+=('-f' "${op}=4")
+ done
+
+ # Read and sync occasionally
+ for op in getdents stat fsync; do
+ focus+=('-f' "${op}=1")
+ done
+ ;;
+ "xattr")
+ focus+=('-z')
+
+ # Create a directory tree slowly
+ for op in creat ; do
+ focus+=('-f' "${op}=2")
+ done
+ for op in unlink rmdir; do
+ focus+=('-f' "${op}=1")
+ done
+
+ # Create xattrs rapidly
+ for op in attr_set setfattr; do
+ focus+=('-f' "${op}=80")
+ done
+
+ # Remove xattrs 1/4 as quickly
+ for op in attr_remove removefattr; do
+ focus+=('-f' "${op}=20")
+ done
+
+ # Read and sync occasionally
+ for op in listfattr getfattr fsync; do
+ focus+=('-f' "${op}=10")
+ done
+ ;;
+ "writeonly")
+ # Only do things that cause filesystem writes
+ focus+=('-w')
+ ;;
+ "default")
+ # No new arguments
+ ;;
+ "symlink")
+ focus+=('-z')
+
+ # Only create, read, and delete symbolic links
+ focus+=('-f' 'symlink=4')
+ focus+=('-f' 'readlink=10')
+ focus+=('-f' 'unlink=1')
+ ;;
+ "mknod")
+ focus+=('-z')
+
+ # Only create and delete special files
+ focus+=('-f' 'mknod=4')
+ focus+=('-f' 'getdents=100')
+ focus+=('-f' 'unlink=1')
+ ;;
+ *)
+ echo "$stress_tgt: Unrecognized stress target, using defaults."
+ ;;
+ esac
+
+ # As of March 2022, 2 million fsstress ops should be enough to keep
+ # any filesystem busy for a couple of hours.
+ local args=$(_scale_fsstress_args -p 4 -d $SCRATCH_MNT -n 2000000 "${focus[@]}" $FSSTRESS_AVOID)
+ echo "Running $FSSTRESS_PROG $args" >> $seqres.full
+
+ if [ -n "$remount_period" ]; then
+ local mode="rw"
+ local rw_arg=""
+ while __stress_scrub_running "$end" "$runningfile"; do
+ # Need to recheck running conditions if we cleared
+ # anything.
+ test "$mode" = "rw" && __stress_scrub_clean_scratch && continue
+
+ timeout -s TERM "$remount_period" $FSSTRESS_PROG \
+ $args $rw_arg >> $seqres.full
+ res=$?
+ echo "$mode fsstress exits with $res at $(date)" >> $seqres.full
+ if [ "$res" -ne 0 ] && [ "$res" -ne 124 ]; then
+ # Stop if fsstress returns error. Mask off
+ # the magic code 124 because that is how the
+ # timeout(1) program communicates that we ran
+ # out of time.
+ break;
+ fi
+ if [ "$mode" = "rw" ]; then
+ mode="ro"
+ rw_arg="-R"
+ else
+ mode="rw"
+ rw_arg=""
+ fi
+
+ # Try remounting until we get the result we wanted
+ while ! _scratch_remount "$mode" &>/dev/null && \
+ __stress_scrub_running "$end" "$runningfile"; do
+ sleep 0.2
+ done
+ done
+ rm -f "$runningfile"
+ return 0
+ fi
+
+ while __stress_scrub_running "$end" "$runningfile"; do
+ # Need to recheck running conditions if we cleared anything
+ __stress_scrub_clean_scratch && continue
+ $FSSTRESS_PROG $args >> $seqres.full
+ echo "fsstress exits with $? at $(date)" >> $seqres.full
+ done
+ rm -f "$runningfile"
+}
+
+# Make sure we have everything we need to run stress and scrub
+_require_xfs_stress_scrub() {
+ _require_xfs_io_command "scrub"
+ _require_test_program "xfsfind"
+ _require_command "$KILLALL_PROG" killall
+ _require_freeze
+ command -v _filter_scratch &>/dev/null || \
+ _notrun 'xfs scrub stress test requires common/filter'
+}
+
+# Make sure that we can force repairs either by error injection or passing
+# FORCE_REBUILD via ioctl.
+__require_xfs_stress_force_rebuild() {
+ local output="$($XFS_IO_PROG -x -c 'repair -R probe' $SCRATCH_MNT 2>&1)"
+ test -z "$output" && return
+ _require_xfs_io_error_injection "force_repair"
+}
+
+# Make sure we have everything we need to run stress and online repair
+_require_xfs_stress_online_repair() {
+ _require_xfs_stress_scrub
+ _require_xfs_io_command "repair"
+ command -v _require_xfs_io_error_injection &>/dev/null || \
+ _notrun 'xfs repair stress test requires common/inject'
+ __require_xfs_stress_force_rebuild
+ _require_freeze
+}
+
+# Clean up after the loops in case they didn't do it themselves.
+_scratch_xfs_stress_scrub_cleanup() {
+ rm -f "$runningfile"
+ echo "Cleaning up scrub stress run at $(date)" >> $seqres.full
+
+ # Send SIGINT so that bash won't print a 'Terminated' message that
+ # distorts the golden output.
+ echo "Killing stressor processes at $(date)" >> $seqres.full
+ $KILLALL_PROG -INT xfs_io fsstress fsx xfs_scrub >> $seqres.full 2>&1
+
+ # Tests are not allowed to exit with the scratch fs frozen. If we
+ # started a fs freeze/thaw background loop, wait for that loop to exit
+ # and then thaw the filesystem. Cleanup for the freeze loop must be
+ # performed prior to waiting for the other children to avoid triggering
+ # a race condition that can hang fstests.
+ #
+ # If the xfs_io -c freeze process is asleep waiting for a write lock on
+ # s_umount or sb_write when the killall signal is delivered, it will
+ # not check for pending signals until after it has frozen the fs. If
+ # even one thread of the stress test processes (xfs_io, fsstress, etc.)
+ # is waiting for read locks on sb_write when the killall signals are
+ # delivered, they will block in the kernel until someone thaws the fs,
+ # and the `wait' below will wait forever.
+ #
+ # Hence we issue the killall, wait for the freezer loop to exit, thaw
+ # the filesystem, and wait for the rest of the children.
+ if [ -n "$__SCRUB_STRESS_FREEZE_PID" ]; then
+ echo "Waiting for fs freezer $__SCRUB_STRESS_FREEZE_PID to exit at $(date)" >> $seqres.full
+ wait "$__SCRUB_STRESS_FREEZE_PID"
+
+ echo "Thawing filesystem at $(date)" >> $seqres.full
+ $XFS_IO_PROG -x -c 'thaw' $SCRATCH_MNT >> $seqres.full 2>&1
+ __SCRUB_STRESS_FREEZE_PID=""
+ fi
+
+ # Wait for the remaining children to exit.
+ echo "Waiting for children to exit at $(date)" >> $seqres.full
+ wait
+
+ # Ensure the scratch fs is also writable before we exit.
+ if [ -n "$__SCRUB_STRESS_REMOUNT_LOOP" ]; then
+ echo "Remounting rw at $(date)" >> $seqres.full
+ _scratch_remount rw >> $seqres.full 2>&1
+ __SCRUB_STRESS_REMOUNT_LOOP=""
+ fi
+
+ echo "Cleanup finished at $(date)" >> $seqres.full
+}
+
+# Make sure the provided scrub/repair commands actually work on the scratch
+# filesystem before we start running them in a loop.
+__stress_scrub_check_commands() {
+ local scrub_tgt="$1"
+ local start_agno="$2"
+ shift; shift
+
+ local cooked_tgt="$scrub_tgt"
+ case "$scrub_tgt" in
+ "%file%"|"%dir%")
+ cooked_tgt="$SCRATCH_MNT"
+ ;;
+ "%regfile%"|"%datafile%")
+ cooked_tgt="$SCRATCH_MNT/testfile"
+ echo test > "$cooked_tgt"
+ ;;
+ "%attrfile%")
+ cooked_tgt="$SCRATCH_MNT/testfile"
+ $XFS_IO_PROG -f -c 'pwrite -S 0x58 0 64k' "$cooked_tgt" &>/dev/null
+ attr -s attrname "$cooked_tgt" < "$cooked_tgt" &>/dev/null
+ ;;
+ "%cowfile%")
+ cooked_tgt="$SCRATCH_MNT/testfile"
+ $XFS_IO_PROG -f -c 'pwrite -S 0x58 0 128k' "$cooked_tgt" &>/dev/null
+ _cp_reflink "$cooked_tgt" "$cooked_tgt.1"
+ $XFS_IO_PROG -f -c 'pwrite -S 0x58 0 1' "$cooked_tgt.1" &>/dev/null
+ ;;
+ esac
+
+ for arg in "$@"; do
+ local cooked_arg="$arg"
+ if [ -n "$SCRUBSTRESS_USE_FORCE_REBUILD" ]; then
+ cooked_arg="$(echo "$cooked_arg" | sed -e 's/^repair/repair -R/g')"
+ fi
+ cooked_arg="$(echo "$cooked_arg" | sed -e "s/%agno%/$start_agno/g")"
+ testio=`$XFS_IO_PROG -x -c "$cooked_arg" "$cooked_tgt" 2>&1`
+ echo $testio | grep -q "Unknown type" && \
+ _notrun "xfs_io scrub subcommand support is missing"
+ echo $testio | grep -q "Inappropriate ioctl" && \
+ _notrun "kernel scrub ioctl is missing"
+ echo $testio | grep -q "No such file or directory" && \
+ _notrun "kernel does not know about: $arg"
+ echo $testio | grep -q "Operation not supported" && \
+ _notrun "kernel does not support: $arg"
+ done
+}
+
+# Start scrub, freeze, and fsstress in background looping processes, and wait
+# for 30*TIME_FACTOR seconds to see if the filesystem goes down. Callers
+# must call _scratch_xfs_stress_scrub_cleanup from their cleanup functions.
+#
+# Various options include:
+#
+# -a For %agno% substitution, start with this AG instead of AG 0.
+# -f Run a freeze/thaw loop while we're doing other things. Defaults to
+# disabled, unless XFS_SCRUB_STRESS_FREEZE is set.
+# -i Pass this command to xfs_io to exercise something that is not scrub
+# in a separate loop. If zero -i options are specified, do not run.
+# Callers must check each of these commands (via _require_xfs_io_command)
+# before calling here.
+# -r Run fsstress for this amount of time, then remount the fs ro or rw.
+# The default is to run fsstress continuously with no remount, unless
+# XFS_SCRUB_STRESS_REMOUNT_PERIOD is set.
+# -s Pass this command to xfs_io to test scrub. If zero -s options are
+# specified, xfs_io will not be run.
+# -S Pass this option to xfs_scrub. If zero -S options are specified,
+# xfs_scrub will not be run. To select repair mode, pass '-k' or '-v'.
+# -t Run online scrub against this file; $SCRATCH_MNT is the default.
+# Special values are as follows:
+#
+# %file% all files
+# %regfile% regular files
+# %dir% direct
+# %datafile% regular files with data blocks
+# %attrfile% regular files with xattr blocks
+# %cowfile% regular files with shared blocks
+#
+# File selection races with fsstress, so the selection is best-effort.
+# -w Delay the start of the scrub/repair loop by this number of seconds.
+# Defaults to no delay unless XFS_SCRUB_STRESS_DELAY is set. This value
+# will be clamped to ten seconds before the end time.
+# -x Focus on this type of fsstress operation. Possible values:
+#
+# 'dir': Grow the directory trees as much as possible.
+# 'xattr': Grow extended attributes in a small tree.
+# 'default': Run fsstress with default arguments.
+# 'writeonly': Only perform fs updates, no reads.
+# 'symlink': Only create symbolic links.
+# 'mknod': Only create special files.
+# 'parent': Focus on updating parent pointers
+#
+# The default is 'default' unless XFS_SCRUB_STRESS_TARGET is set.
+# -X Run this program to exercise the filesystem. Currently supported
+# options are 'fsx' and 'fsstress'. The default is 'fsstress'.
+_scratch_xfs_stress_scrub() {
+ local one_scrub_args=()
+ local xfs_scrub_args=()
+ local scrub_tgt="$SCRATCH_MNT"
+ local runningfile="$tmp.fsstress"
+ local freeze="${XFS_SCRUB_STRESS_FREEZE}"
+ local scrub_delay="${XFS_SCRUB_STRESS_DELAY:--1}"
+ local exerciser="fsstress"
+ local io_args=()
+ local remount_period="${XFS_SCRUB_STRESS_REMOUNT_PERIOD}"
+ local stress_tgt="${XFS_SCRUB_STRESS_TARGET:-default}"
+ local start_agno=0
+
+ __SCRUB_STRESS_FREEZE_PID=""
+ __SCRUB_STRESS_REMOUNT_LOOP=""
+ rm -f "$runningfile"
+ touch "$runningfile"
+
+ OPTIND=1
+ while getopts "a:fi:r:s:S:t:w:x:X:" c; do
+ case "$c" in
+ a) start_agno="$OPTARG";;
+ f) freeze=yes;;
+ i) io_args+=("$OPTARG");;
+ r) remount_period="$OPTARG";;
+ s) one_scrub_args+=("$OPTARG");;
+ S) xfs_scrub_args+=("$OPTARG");;
+ t) scrub_tgt="$OPTARG";;
+ w) scrub_delay="$OPTARG";;
+ x) stress_tgt="$OPTARG";;
+ X) exerciser="$OPTARG";;
+ *) return 1; ;;
+ esac
+ done
+
+ __stress_scrub_check_commands "$scrub_tgt" "$start_agno" \
+ "${one_scrub_args[@]}"
+
+ if ! command -v "__stress_scrub_${exerciser}_loop" &>/dev/null; then
+ echo "${exerciser}: Unknown fs exercise program."
+ return 1
+ fi
+
+ if [ "${#xfs_scrub_args[@]}" -gt 0 ]; then
+ _scratch_scrub "${xfs_scrub_args[@]}" &> "$tmp.scrub"
+ res=$?
+ if [ $res -ne 0 ]; then
+ echo "xfs_scrub ${xfs_scrub_args[@]} failed, err $res" >> $seqres.full
+ cat "$tmp.scrub" >> $seqres.full
+ rm -f "$tmp.scrub"
+ _notrun 'scrub not supported on scratch filesystem'
+ fi
+ rm -f "$tmp.scrub"
+ fi
+
+ _xfs_skip_online_rebuild
+ _xfs_skip_offline_rebuild
+
+ local start="$(date +%s)"
+ local end
+ if [ -n "$SOAK_DURATION" ]; then
+ end="$((start + SOAK_DURATION))"
+ else
+ end="$((start + (30 * TIME_FACTOR) ))"
+ fi
+ local scrub_startat="$((start + scrub_delay))"
+ test "$scrub_startat" -gt "$((end - 10))" &&
+ scrub_startat="$((end - 10))"
+
+ echo "Loop started at $(date --date="@${start}")," \
+ "ending at $(date --date="@${end}")" >> $seqres.full
+
+ if [ -n "$remount_period" ]; then
+ __SCRUB_STRESS_REMOUNT_LOOP="1"
+ fi
+
+ "__stress_scrub_${exerciser}_loop" "$end" "$runningfile" \
+ "$remount_period" "$stress_tgt" &
+
+ if [ -n "$freeze" ]; then
+ __stress_scrub_freeze_loop "$end" "$runningfile" &
+ __SCRUB_STRESS_FREEZE_PID="$!"
+ fi
+
+ if [ "${#io_args[@]}" -gt 0 ]; then
+ __stress_xfs_io_loop "$end" "$runningfile" \
+ "${io_args[@]}" &
+ fi
+
+ if [ "${#one_scrub_args[@]}" -gt 0 ]; then
+ __stress_one_scrub_loop "$end" "$runningfile" "$scrub_tgt" \
+ "$scrub_startat" "$start_agno" \
+ "${one_scrub_args[@]}" &
+ fi
+
+ if [ "${#xfs_scrub_args[@]}" -gt 0 ]; then
+ __stress_xfs_scrub_loop "$end" "$runningfile" "$scrub_startat" \
+ "${xfs_scrub_args[@]}" &
+ fi
+
+ # Wait until the designated end time or fsstress dies, then kill all of
+ # our background processes.
+ while __stress_scrub_running "$end" "$runningfile"; do
+ sleep 1
+ done
+ _scratch_xfs_stress_scrub_cleanup
+
+ # Warn the user if we think the scratch filesystem went down.
+ __stress_scrub_scratch_alive || \
+ echo "Did the scratch filesystem die?"
+
+ echo "Loop finished at $(date)" >> $seqres.full
+}
+
+# Decide if we're going to force repairs either by error injection or passing
+# FORCE_REBUILD via ioctl.
+__scratch_xfs_stress_setup_force_rebuild() {
+ local output="$($XFS_IO_PROG -x -c 'repair -R probe' $SCRATCH_MNT 2>&1)"
+
+ if [ -z "$output" ]; then
+ SCRUBSTRESS_USE_FORCE_REBUILD=1
+ return
+ fi
+
+ $XFS_IO_PROG -x -c 'inject force_repair' $SCRATCH_MNT
+}
+
+# Start online repair, freeze, and fsstress in background looping processes,
+# and wait for 30*TIME_FACTOR seconds to see if the filesystem goes down.
+# Same requirements and arguments as _scratch_xfs_stress_scrub.
+_scratch_xfs_stress_online_repair() {
+ __scratch_xfs_stress_setup_force_rebuild
+ XFS_SCRUB_FORCE_REPAIR=1 _scratch_xfs_stress_scrub "$@"
+}
--- /dev/null
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023 Oracle. All Rights Reserved.
+#
+# Routines for capturing kernel code coverage reports
+
+GCOV_DIR=/sys/kernel/debug/gcov
+
+# Find the topmost directories of the .gcno directory hierarchy
+__gcov_find_topdirs() {
+ find "${GCOV_DIR}/" -name '*.gcno' -printf '%d|%h\n' | \
+ sort -g -k 1 | \
+ uniq | \
+ $AWK_PROG -F '|' 'BEGIN { x = -1 } { if (x < 0) x = $1; if ($1 == x) printf("%s\n", $2);}'
+}
+
+# Generate lcov html report from kernel gcov data if configured
+_gcov_generate_report() {
+ local output_dir="$1"
+ test -n "${output_dir}" || return
+
+ # Kernel support built in?
+ test -d "$GCOV_DIR" || return
+
+ readarray -t gcno_dirs < <(__gcov_find_topdirs)
+ test "${#gcno_dirs[@]}" -gt 0 || return
+
+ mkdir -p "${output_dir}/raw/"
+
+ # Collect raw coverage data from the kernel
+ readarray -t source_dirs < <(find "${GCOV_DIR}/" -mindepth 1 -maxdepth 1 -type d)
+ for dir in "${source_dirs[@]}"; do
+ cp -p -R -d -u "${dir}" "${output_dir}/raw/"
+ done
+
+ # If lcov is installed, use it to summarize the gcda data.
+ # If it is not installed, there's no point in going forward
+ command -v lcov > /dev/null || return
+ local lcov=(lcov --exclude 'include*' --capture)
+ lcov+=(--output-file "${output_dir}/gcov.report")
+ for d in "${gcno_dirs[@]}"; do
+ lcov+=(--directory "${d}")
+ done
+
+ # Generate a detailed HTML report from the summary
+ local gcov_start_time="$(date --date="${fstests_start_time:-now}")"
+ local genhtml=()
+ if command -v genhtml > /dev/null; then
+ genhtml+=(genhtml -o "${output_dir}/" "${output_dir}/gcov.report")
+ genhtml+=(--title "fstests on $(hostname -s) @ ${gcov_start_time}" --legend)
+ fi
+
+ # Try to convert the HTML report summary as text for easier grepping if
+ # there's an HTML renderer present
+ local totext=()
+ test "${#totext[@]}" -eq 0 && \
+ command -v lynx &>/dev/null && \
+ totext=(lynx -dump "${output_dir}/index.html" -width 120 -nonumbers -nolist)
+ test "${#totext[@]}" -eq 0 && \
+ command -v links &>/dev/null && \
+ totext=(links -dump "${output_dir}/index.html" -width 120)
+ test "${#totext[@]}" -eq 0 && \
+ command -v elinks &>/dev/null && \
+ totext=(elinks -dump "${output_dir}/index.html" --dump-width 120 --no-numbering --no-references)
+
+ # Analyze kernel data
+ "${lcov[@]}" > "${output_dir}/gcov.stdout" 2> "${output_dir}/gcov.stderr"
+ test "${#genhtml[@]}" -ne 0 && \
+ "${genhtml[@]}" >> "${output_dir}/gcov.stdout" 2>> "${output_dir}/gcov.stderr"
+ test "${#totext[@]}" -ne 0 && \
+ "${totext[@]}" > "${output_dir}/index.txt" 2>> "${output_dir}/gcov.stderr"
+}
+
+# Reset gcov usage data
+_gcov_reset() {
+ echo 1 > "${GCOV_DIR}/reset"
+}
+
+# If the caller wanted us to capture gcov reports but the kernel doesn't
+# support it, turn it off.
+_gcov_check_report_gcov() {
+ test -z "$REPORT_GCOV" && return 0
+ test -w "${GCOV_DIR}/reset" && return 0
+
+ unset REPORT_GCOV
+ return 1
+}
--- /dev/null
+##/bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2024 Oracle. All Rights Reserved.
+#
+# Filesystem metadata dump testing functions.
+#
+
+# Set up environment variables for a metadump test. Requires the test and
+# scratch devices. Sets XFS_METADUMP_{FILE,IMG} and MAX_XFS_METADUMP_VERSION.
+_xfs_setup_verify_metadump()
+{
+ XFS_METADUMP_FILE="$TEST_DIR/${seq}_metadump"
+ XFS_METADUMP_IMG="$TEST_DIR/${seq}_image"
+ MAX_XFS_METADUMP_VERSION="$(_xfs_metadump_max_version)"
+
+ rm -f "$XFS_METADUMP_FILE" "$XFS_METADUMP_IMG"*
+}
+
+_xfs_cleanup_verify_metadump()
+{
+ local img
+
+ _scratch_unmount &>> $seqres.full
+
+ test -n "$XFS_METADUMP_FILE" && rm -f "$XFS_METADUMP_FILE"
+
+ if [ -n "$XFS_METADUMP_IMG" ]; then
+ losetup -n -a -O BACK-FILE,NAME | grep "^$XFS_METADUMP_IMG" | while read backing ldev; do
+ losetup -d "$ldev"
+ done
+
+ # Don't call rm directly with a globbed argument here to avoid
+ # issues issues with variable expansions.
+ for img in "$XFS_METADUMP_IMG"*; do
+ test -e "$img" && rm -f "$img"
+ done
+ fi
+}
+
+# Can xfs_metadump snapshot the fs metadata to a v1 metadump file?
+_scratch_xfs_can_metadump_v1()
+{
+ # metadump v1 does not support log devices
+ [ "$USE_EXTERNAL" = yes ] && [ -n "$SCRATCH_LOGDEV" ] && return 1
+
+ # metadump v1 does not support realtime devices
+ [ "$USE_EXTERNAL" = yes ] && [ -n "$SCRATCH_RTDEV" ] && return 1
+
+ return 0
+}
+
+# Can xfs_metadump snapshot the fs metadata to a v2 metadump file?
+_scratch_xfs_can_metadump_v2()
+{
+ test "$MAX_XFS_METADUMP_VERSION" -ge 2
+}
+
+# Create a metadump in v1 format, restore it to fs image files, then mount the
+# images and fsck them.
+_xfs_verify_metadump_v1()
+{
+ local metadump_args="$1"
+ local extra_test="$2"
+
+ local metadump_file="$XFS_METADUMP_FILE"
+ local version=""
+ local data_img="$XFS_METADUMP_IMG.data"
+ local data_loop
+
+ # Force v1 if we detect v2 support
+ if [[ $MAX_XFS_METADUMP_FORMAT > 1 ]]; then
+ version="-v 1"
+ fi
+
+ # Capture metadump, which creates metadump_file
+ _scratch_xfs_metadump $metadump_file $metadump_args $version
+
+ # Restore metadump, which creates data_img
+ SCRATCH_DEV=$data_img _scratch_xfs_mdrestore $metadump_file
+
+ # Create loopdev for data device so we can mount the fs
+ data_loop=$(_create_loop_device $data_img)
+
+ # Mount fs, run an extra test, fsck, and unmount
+ SCRATCH_DEV=$data_loop _scratch_mount
+ if [ -n "$extra_test" ]; then
+ SCRATCH_DEV=$data_loop $extra_test
+ fi
+ SCRATCH_DEV=$data_loop _check_xfs_scratch_fs
+ SCRATCH_DEV=$data_loop _scratch_unmount
+
+ # Tear down what we created
+ _destroy_loop_device $data_loop
+ rm -f $data_img
+}
+
+# Create a metadump in v2 format, restore it to fs image files, then mount the
+# images and fsck them.
+_xfs_verify_metadump_v2()
+{
+ local metadump_args="$1"
+ local extra_test="$2"
+
+ local metadump_file="$XFS_METADUMP_FILE"
+ local version="-v 2"
+ local data_img="$XFS_METADUMP_IMG.data"
+ local data_loop
+ local log_img=""
+ local log_loop
+
+ # Capture metadump, which creates metadump_file
+ _scratch_xfs_metadump $metadump_file $metadump_args $version
+
+ #
+ # Metadump v2 files can contain contents dumped from an external log
+ # device. Use a temporary file to hold the log device contents restored
+ # from such a metadump file.
+ test -n "$SCRATCH_LOGDEV" && log_img="$XFS_METADUMP_IMG.log"
+
+ # Restore metadump, which creates data_img and log_img
+ SCRATCH_DEV=$data_img SCRATCH_LOGDEV=$log_img \
+ _scratch_xfs_mdrestore $metadump_file
+
+ # Create loopdev for data device so we can mount the fs
+ data_loop=$(_create_loop_device $data_img)
+
+ # Create loopdev for log device if we recovered anything
+ test -s "$log_img" && log_loop=$(_create_loop_device $log_img)
+
+ # Mount fs, run an extra test, fsck, and unmount
+ SCRATCH_DEV=$data_loop SCRATCH_LOGDEV=$log_loop _scratch_mount
+ if [ -n "$extra_test" ]; then
+ SCRATCH_DEV=$data_loop SCRATCH_LOGDEV=$log_loop $extra_test
+ fi
+ SCRATCH_DEV=$data_loop SCRATCH_LOGDEV=$log_loop _check_xfs_scratch_fs
+ SCRATCH_DEV=$data_loop _scratch_unmount
+
+ # Tear down what we created
+ if [ -b "$log_loop" ]; then
+ _destroy_loop_device $log_loop
+ rm -f $log_img
+ fi
+ _destroy_loop_device $data_loop
+ rm -f $data_img
+}
+
+# Verify both metadump formats if possible
+_xfs_verify_metadumps()
+{
+ _scratch_xfs_can_metadump_v1 && _xfs_verify_metadump_v1 "$@"
+ _scratch_xfs_can_metadump_v2 && _xfs_verify_metadump_v2 "$@"
+}
modprobe "${module}" || _notrun "${module} load failed"
}
-# Check that the module for FSTYP can be loaded.
-_require_loadable_fs_module()
+# Test if the module for FSTYP can be unloaded and reloaded.
+#
+# If not, returns 1 if $FSTYP is not a loadable module; 2 if the module could
+# not be unloaded; or 3 if loading the module fails.
+_test_loadable_fs_module()
{
local module="$1"
- modinfo "${module}" > /dev/null 2>&1 || _notrun "${module}: must be a module."
+ modinfo "${module}" > /dev/null 2>&1 || return 1
# Unload test fs, try to reload module, remount
local had_testfs=""
modprobe "${module}" || load_ok=0
test -n "${had_scratchfs}" && _scratch_mount 2> /dev/null
test -n "${had_testfs}" && _test_mount 2> /dev/null
- test -z "${unload_ok}" || _notrun "Require module ${module} to be unloadable"
- test -z "${load_ok}" || _notrun "${module} load failed"
+ test -z "${unload_ok}" || return 2
+ test -z "${load_ok}" || return 3
+ return 0
+}
+
+_require_loadable_fs_module()
+{
+ local module="$1"
+
+ _test_loadable_fs_module "${module}"
+ ret=$?
+ case "$ret" in
+ 1)
+ _notrun "${module}: must be a module."
+ ;;
+ 2)
+ _notrun "${module}: module could not be unloaded"
+ ;;
+ 3)
+ _notrun "${module}: module reload failed"
+ ;;
+ esac
}
# Print the value of a filesystem module parameter
export OVL_XATTR_UPPER="trusted.overlay.upper"
export OVL_XATTR_METACOPY="trusted.overlay.metacopy"
+if [ -n "$OVL_BASE_FSTYP" ];then
+ _source_specific_fs $OVL_BASE_FSTYP
+fi
+
# helper function to do the actual overlayfs mount operation
+# accepts "-" as upperdir for non-upper overlayfs
_overlay_mount_dirs()
{
local lowerdir=$1
local upperdir=$2
local workdir=$3
shift 3
+ local diropts="-olowerdir=$lowerdir"
+
+ [ -n "$upperdir" ] && [ "$upperdir" != "-" ] && \
+ diropts+=",upperdir=$upperdir,workdir=$workdir"
+
+ $MOUNT_PROG -t overlay $diropts `_common_dev_mount_options $*`
+}
- $MOUNT_PROG -t overlay -o lowerdir=$lowerdir -o upperdir=$upperdir \
- -o workdir=$workdir `_common_dev_mount_options $*`
+# Mount with mnt/dev of scratch mount and custom mount options
+_overlay_scratch_mount_opts()
+{
+ $MOUNT_PROG -t overlay $OVL_BASE_SCRATCH_MNT $SCRATCH_MNT $*
}
# Mount with same options/mnt/dev of scratch mount, but optionally
return 1
fi
- _mount -t $OVL_BASE_FSTYP $* $dev $mnt
+ if [ $OVL_BASE_FSTYP ]; then
+ _mount -t $OVL_BASE_FSTYP $* $dev $mnt
+ else
+ _mount $* $dev $mnt
+ fi
+
_idmapped_mount $dev $mnt
}
_scratch_unmount
}
+_check_scratch_overlay_xattr_escapes()
+{
+ local testfile=$1
+
+ touch $testfile
+ ! ($GETFATTR_PROG -n trusted.overlay.foo $testfile 2>&1 | grep -E -q "not (permitted|supported)")
+}
+
+_require_scratch_overlay_xattr_escapes()
+{
+ _scratch_mkfs > /dev/null 2>&1
+ _scratch_mount
+
+ _check_scratch_overlay_xattr_escapes $SCRATCH_MNT/file || \
+ _notrun "xattr escaping is not supported by overlay"
+
+ _scratch_unmount
+}
+
+_require_scratch_overlay_verity()
+{
+ local lowerdirs="$OVL_BASE_SCRATCH_MNT/$OVL_UPPER:$OVL_BASE_SCRATCH_MNT/$OVL_LOWER"
+
+ _require_scratch_verity "$OVL_BASE_FSTYP" "$OVL_BASE_SCRATCH_MNT"
+
+ _scratch_mkfs > /dev/null 2>&1
+ _overlay_scratch_mount_dirs "$lowerdirs" "-" "-" \
+ -o ro,redirect_dir=follow,metacopy=on,verity=on > /dev/null 2>&1 || \
+ _notrun "overlay verity not supported on ${SCRATCH_DEV}"
+
+ _scratch_unmount
+}
+
+# Check kernel support for <lowerdirs>::<lowerdatadir> format
+_require_scratch_overlay_lowerdata_layers()
+{
+ local lowerdirs="$OVL_BASE_SCRATCH_MNT/$OVL_UPPER::$OVL_BASE_SCRATCH_MNT/$OVL_LOWER"
+
+ _scratch_mkfs > /dev/null 2>&1
+ _overlay_scratch_mount_dirs "$lowerdirs" "-" "-" \
+ -o ro,redirect_dir=follow,metacopy=on > /dev/null 2>&1 || \
+ _notrun "overlay data-only layers not supported on ${SCRATCH_DEV}"
+
+ _scratch_unmount
+}
+
+# Check kernel support for lowerdir+=<lowerdir>,datadir+=<lowerdatadir> format
+_require_scratch_overlay_lowerdir_add_layers()
+{
+ local lowerdir="$OVL_BASE_SCRATCH_MNT/$OVL_UPPER"
+ local datadir="$OVL_BASE_SCRATCH_MNT/$OVL_LOWER"
+
+ _scratch_mkfs > /dev/null 2>&1
+ _overlay_scratch_mount_opts \
+ -o"lowerdir+=$lowerdir,datadir+=$datadir" \
+ -o"redirect_dir=follow,metacopy=on" > /dev/null 2>&1 || \
+ _notrun "overlay lowerdir+,datadir+ not supported on ${SCRATCH_DEV}"
+
+ _scratch_unmount
+}
+
# Helper function to check underlying dirs of overlay filesystem
_overlay_fsck_dirs()
{
_require_xfs_io_command "falloc"
_require_xfs_io_command "fpunch"
_require_test_program "punch-alternating"
- _require_command "$XFS_DB_PROG" "xfs_db"
+ _require_test_program "popdir.pl"
+ if [ -n "${PYTHON3_PROG}" ]; then
+ _require_command $PYTHON3_PROG python3
+ _require_test_program "popattr.py"
+ fi
+ case "${FSTYP}" in
+ "xfs")
+ _require_command "$XFS_DB_PROG" "xfs_db"
+ _require_command "$WIPEFS_PROG" "wipefs"
+ _require_command "$XFS_MDRESTORE_PROG" "xfs_mdrestore"
+ ;;
+ ext*)
+ _require_command "$DUMPE2FS_PROG" "dumpe2fs"
+ _require_command "$E2IMAGE_PROG" "e2image"
+ ;;
+ esac
}
_require_xfs_db_blocktrash_z_command() {
$XFS_IO_PROG -f -c "pwrite -S 0x62 -W -b 1m 0 $sz" "${fname}"
}
+# Fail the test if we failed to create some kind of filesystem metadata.
+# Create a metadata dump of the failed filesystem so that we can analyze
+# how things went rong.
+__populate_fail() {
+ local flatdev="$(basename "$SCRATCH_DEV")"
+ local metadump="$seqres.$flatdev.populate.md"
+
+ case "$FSTYP" in
+ xfs)
+ _scratch_unmount
+ _scratch_xfs_metadump "$metadump" -a -o
+ ;;
+ ext4)
+ _scratch_unmount
+ _ext4_metadump "${SCRATCH_DEV}" "$metadump"
+ ;;
+ esac
+
+ _fail "$@"
+}
+
# 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
# Create a large directory
__populate_create_dir() {
- name="$1"
- nr="$2"
- missing="$3"
+ local name="$1"
+ local nr="$2"
+ local missing="$3"
+ shift; shift; shift
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
+ $here/src/popdir.pl --dir "${name}" --end "${nr}" "$@"
test -z "${missing}" && return
- seq 1 2 "${nr}" | while read d; do
- rm -rf "${name}/$(printf "%.08d" "$d")"
+ $here/src/popdir.pl --dir "${name}" --start 1 --incr 19 --end "${nr}" --remove "$@"
+}
+
+# Create a large directory and ensure that it's a btree format
+__populate_xfs_create_btree_dir() {
+ local name="$1"
+ local isize="$2"
+ local dblksz="$3"
+ local missing="$4"
+ local icore_size="$(_xfs_get_inode_core_bytes $SCRATCH_MNT)"
+ # We need enough extents to guarantee that the data fork is in
+ # btree format. Cycling the mount to use xfs_db is too slow, so
+ # watch for when the extent count exceeds the space after the
+ # inode core.
+ local max_nextents="$(((isize - icore_size) / 16))"
+ local nr
+ local incr
+
+ # Add about one block's worth of dirents before we check the data fork
+ # format.
+ incr=$(( (dblksz / 8) / 100 * 100 ))
+
+ mkdir -p "${name}"
+ for ((nr = 0; ; nr += incr)); do
+ $here/src/popdir.pl --dir "${name}" --start "${nr}" --end "$((nr + incr - 1))"
+
+ # Extent count checks use data blocks only to avoid the removal
+ # step from removing dabtree index blocks and reducing the
+ # number of extents below the required threshold.
+ local nextents="$(xfs_bmap ${name} | grep -v hole | wc -l)"
+ [ "$((nextents - 1))" -gt $max_nextents ] && break
done
+
+ test -z "${missing}" && return
+ $here/src/popdir.pl --dir "${name}" --start 1 --incr 19 --end "${nr}" --remove
}
# Add a bunch of attrs to a file
missing="$3"
touch "${name}"
- seq 0 "${nr}" | while read d; do
- setfattr -n "user.$(printf "%.08d" "$d")" -v "$(printf "%.08d" "$d")" "${name}"
- done
+
+ if [ -n "${PYTHON3_PROG}" ]; then
+ ${PYTHON3_PROG} $here/src/popattr.py --file "${name}" --end "${nr}"
+
+ test -z "${missing}" && return
+ ${PYTHON3_PROG} $here/src/popattr.py --file "${name}" --start 1 --incr 19 --end "${nr}" --remove
+ return
+ fi
+
+ # Simulate a getfattr dump file so we can bulk-add attrs.
+ (
+ echo "# file: ${name}";
+ seq --format "user.%08g=\"abcdefgh\"" 0 "${nr}"
+ echo
+ ) | setfattr --restore -
test -z "${missing}" && return
seq 1 2 "${nr}" | while read d; do
done
}
+# Create an extended attr structure and ensure that the fork is btree format
+__populate_xfs_create_btree_attr() {
+ local name="$1"
+ local isize="$2"
+ local dblksz="$3"
+ local icore_size="$(_xfs_get_inode_core_bytes $SCRATCH_MNT)"
+ # We need enough extents to guarantee that the attr fork is in btree
+ # format. Cycling the mount to use xfs_db is too slow, so watch for
+ # when the number of holes that we can punch in the attr fork by
+ # deleting remote xattrs exceeds the number of extent mappings that can
+ # fit in the inode core.
+ local max_nextents="$(((isize - icore_size) / 16))"
+ local nr
+ local i
+ local incr
+ local bigval
+
+ # Add about one block's worth of attrs in betweeen creating punchable
+ # remote value blocks.
+ incr=$(( (dblksz / 16) / 100 * 100 ))
+ bigval="$(perl -e "print \"@\" x $dblksz;")"
+
+ touch "${name}"
+
+ # We cannot control the mapping behaviors of the attr fork leaf and
+ # dabtree blocks, but we do know that remote values are stored in a
+ # single extent, and that those mappings are removed if the xattr is
+ # deleted.
+ #
+ # The extended attribute structure tends to grow from offset zero
+ # upwards, so we try to set up a sparse attr fork mapping by
+ # iteratively creating at least one leaf block's worth of local attrs,
+ # and then one remote attr, until the number of remote xattrs exceeds
+ # the number of mappings that fit in the inode core...
+ for ((nr = 0; nr < (incr * max_nextents); nr += incr)); do
+ # Simulate a getfattr dump file so we can bulk-add attrs.
+ (
+ echo "# file: ${name}";
+ seq --format "user.%08g=\"abcdefgh\"" "${nr}" "$((nr + incr + 1))"
+ echo "user.v$(printf "%.08d" "$nr")=\"${bigval}\""
+ echo
+ ) | setfattr --restore -
+ done
+
+ # ... and in the second loop we delete all the remote attrs to
+ # fragment the attr fork mappings.
+ for ((i = 0; i < nr; i += incr)); do
+ setfattr -x "user.v$(printf "%.08d" "$i")" "${name}"
+ done
+}
+
# Fill up some percentage of the remaining free space
__populate_fill_fs() {
dir="$1"
fi
# Turn on all the quotas
- if $XFS_INFO_PROG "${TEST_DIR}" | grep -q 'crc=1'; then
+ if _xfs_has_feature "$TEST_DIR" crc; then
# v5 filesystems can have group & project quotas
quota="usrquota,grpquota,prjquota"
else
# Inject our quota mount options
if echo "${MOUNT_OPTIONS}" | grep -q "${quota}"; then
return
- elif echo "${MOUNT_OPTIONS}" | egrep -q '(quota|noenforce)'; then
+ elif echo "${MOUNT_OPTIONS}" | grep -Eq '(quota|noenforce)'; then
_qmount_option "${quota}"
else
export MOUNT_OPTIONS="$MOUNT_OPTIONS -o ${quota}"
_xfs_force_bdev data $SCRATCH_MNT
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')"
+ dblksz="$(_xfs_get_dir_blocksize "$SCRATCH_MNT")"
+ isize="$(_xfs_get_inode_size "$SCRATCH_MNT")"
+ crc="$(_xfs_has_feature "$SCRATCH_MNT" crc -v)"
if [ $crc -eq 1 ]; then
leaf_hdr_size=64
else
# 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
+ $here/src/popdir.pl --dir "${SCRATCH_MNT}" --start 1 --end 64 --format "dummy%u" --file-pct 100
# Regular files
# - FMT_EXTENTS
# - BTREE
echo "+ btree dir"
- __populate_create_dir "${SCRATCH_MNT}/S_IFDIR.FMT_BTREE" "$((128 * dblksz / 40))" true
+ __populate_xfs_create_btree_dir "${SCRATCH_MNT}/S_IFDIR.FMT_BTREE" "$isize" "$dblksz" true
# Symlinks
# - FMT_LOCAL
# BTREE
echo "+ btree attr"
- __populate_create_attr "${SCRATCH_MNT}/ATTR.FMT_BTREE" "$((64 * blksz / 40))" true
+ __populate_xfs_create_btree_attr "${SCRATCH_MNT}/ATTR.FMT_BTREE" "$isize" "$dblksz"
# trusted namespace
touch ${SCRATCH_MNT}/ATTR.TRUSTED
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
+ __populate_create_dir "${dir}" "${nr}" true --file-pct 100
# Reverse-mapping btree
- is_rmapbt="$($XFS_INFO_PROG "${SCRATCH_MNT}" | grep -c 'rmapbt=1')"
+ is_rmapbt="$(_xfs_has_feature "$SCRATCH_MNT" rmapbt -v)"
if [ $is_rmapbt -gt 0 ]; then
echo "+ rmapbt btree"
nr="$((blksz * 2 / 24))"
fi
# Realtime Reverse-mapping btree
- is_rt="$($XFS_INFO_PROG "${SCRATCH_MNT}" | grep -c 'rtextents=[1-9]')"
+ is_rt="$(_xfs_get_rtextents "$SCRATCH_MNT")"
if [ $is_rmapbt -gt 0 ] && [ $is_rt -gt 0 ]; then
echo "+ rtrmapbt btree"
nr="$((blksz * 2 / 32))"
fi
# Reference-count btree
- is_reflink="$($XFS_INFO_PROG "${SCRATCH_MNT}" | grep -c 'reflink=1')"
+ is_reflink="$(_xfs_has_feature "$SCRATCH_MNT" reflink -v)"
if [ $is_reflink -gt 0 ]; then
echo "+ reflink btree"
nr="$((blksz * 2 / 12))"
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}"
+ test "${format}" = "${fmt}" || __populate_fail "failed to create ino ${inode} dformat expected ${format} saw ${fmt}"
}
# Check attr fork format of XFS file
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}"
+ test "${format}" = "${fmt}" || __populate_fail "failed to create ino ${inode} aformat expected ${format} saw ${fmt}"
}
# Check structure of XFS directory
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}"
+ (test "${datab}" -eq 0 && test "${leafb}" -eq 0 && test "${freeb}" -eq 0) || __populate_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}"
+ (test "${datab}" -eq 1 && test "${leafb}" -eq 0 && test "${freeb}" -eq 0) || __populate_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}"
+ (test "${datab}" -eq 1 && test "${leafb}" -eq 1 && test "${freeb}" -eq 0) || __populate_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}"
+ __populate_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}"
+ (test "${datab}" -eq 1 && test "${leafb}" -eq 1 && test "${freeb}" -eq 1) || __populate_fail "failed to create ${dtype} dir ino ${inode} datab ${datab} leafb ${leafb} freeb ${freeb}"
;;
*)
_fail "Unknown directory type ${dtype}"
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}"
+ (test "${datab}" -eq 0 && test "${leafb}" -eq 0) || __populate_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}"
+ (test "${datab}" -eq 1 && test "${leafb}" -eq 0) || __populate_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}"
+ (test "${datab}" -eq 1 && test "${leafb}" -eq 1) || __populate_fail "failed to create ${atype} attr ino ${inode} datab ${datab} leafb ${leafb}"
;;
*)
_fail "Unknown attribute type ${atype}"
# 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}')
+ local bt_type="$1"
+ local agcount=$(_scratch_xfs_db -c 'sb 0' -c 'p agcount' | awk '{print $3}')
case "${bt_type}" in
"bno"|"cnt"|"rmap"|"refcnt")
;;
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}')
+ for ((agno = 0; agno < agcount; agno++)); do
+ bt_level=$(_scratch_xfs_db -c "${hdr} ${agno}" -c "p ${bt_prefix}level" | awk '{print $3}')
+ # "level" is really the btree height
if [ "${bt_level}" -gt 1 ]; then
- return 100
+ return 0
fi
done
- test $? -eq 100 || _fail "Failed to create ${bt_type} of sufficient height!"
+ __populate_fail "Failed to create ${bt_type} of sufficient height!"
return 1
}
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')
+ is_finobt=$(_xfs_has_feature "$SCRATCH_MNT" finobt -v)
+ is_rmapbt=$(_xfs_has_feature "$SCRATCH_MNT" rmapbt -v)
+ is_reflink=$(_xfs_has_feature "$SCRATCH_MNT" reflink -v)
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')"
+ dblksz="$(_xfs_get_dir_blocksize "$SCRATCH_MNT")"
leaf_lblk="$((32 * 1073741824 / blksz))"
node_lblk="$((64 * 1073741824 / blksz))"
umount "${SCRATCH_MNT}"
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')"
+ iflags="$(_ext4_get_inum_iflags "${dev}" "${inode}")"
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"
+ test "${extents}" -eq 0 || __populate_fail "failed to create ino ${inode} with blockmap"
;;
"extent"|"extents")
- test "${extents}" -eq 1 || _fail "failed to create ino ${inode} with extents"
+ test "${extents}" -eq 1 || __populate_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"
+ (test "${extents}" -eq 1 && test "${etree}" -eq 1) || __populate_fail "failed to create ino ${inode} with extent tree"
;;
*)
_fail "Unknown dformat ${format}"
case "${format}" in
"local"|"inline")
- test "${ablock}" -eq 0 || _fail "failed to create inode ${inode} with ${format} xattr"
+ test "${ablock}" -eq 0 || __populate_fail "failed to create inode ${inode} with ${format} xattr"
;;
"block")
- test "${extents}" -eq 1 || _fail "failed to create inode ${inode} with ${format} xattr"
+ test "${extents}" -eq 1 || __populate_fail "failed to create inode ${inode} with ${format} xattr"
;;
*)
_fail "Unknown aformat ${format}"
htree=0
inline=0
- iflags="$(debugfs -R "stat <${inode}>" "${dev}" 2> /dev/null | grep 'Flags:' | sed -e 's/^.*Flags: \([0-9a-fx]*\).*$/\1/g')"
+ iflags="$(_ext4_get_inum_iflags "${dev}" "${inode}")"
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}"
+ (test "${inline}" -eq 1 && test "${htree}" -eq 0) || __populate_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}"
+ (test "${inline}" -eq 0 && test "${htree}" -eq 0) || __populate_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}"
+ (test "${inline}" -eq 0 && test "${htree}" -eq 1) || __populate_fail "failed to create ${dtype} dir ino ${inode} htree ${htree} inline ${inline}"
;;
*)
_fail "Unknown directory type ${dtype}"
_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
+ _xfs_mdrestore "${metadump}" "${SCRATCH_DEV}"
+ res=$?
+ test $res -ne 0 && return $res
+
+ # Cached images should have been unmounted cleanly, so if
+ # there's an external log we need to wipe it and run repair to
+ # format it to match this filesystem.
+ if [ -n "${SCRATCH_LOGDEV}" ]; then
+ $WIPEFS_PROG -a "${SCRATCH_LOGDEV}"
+ _scratch_xfs_repair
+ res=$?
+ fi
+ return $res
;;
"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
+ _ext4_mdrestore "${metadump}" "${SCRATCH_DEV}"
+ ret=$?
+ test $ret -ne 0 && return $ret
+
+ # ext4 cannot e2image external logs, so we have to reformat
+ # the scratch device to match the restored fs
+ if [ -n "${SCRATCH_LOGDEV}" ]; then
+ local fsuuid="$($DUMPE2FS_PROG -h "${SCRATCH_DEV}" 2>/dev/null | \
+ grep 'Journal UUID:' | \
+ sed -e 's/Journal UUID:[[:space:]]*//g')"
+ $MKFS_EXT4_PROG -O journal_dev "${SCRATCH_LOGDEV}" \
+ -F -U "${fsuuid}"
+ fi
+ return 0
;;
esac
return 1
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
+ # This variable is shared outside this function
POPULATE_METADUMP="${metadump_stem}.metadump"
- POPULATE_METADUMP_DESCR="${metadump_stem}.txt"
+ local 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}") || \
+ cmp -s "${populate_metadump_descr}" <(echo "${meta_descr}") || \
rm -rf "${POPULATE_METADUMP}"
# Try to restore from the metadump
# Oh well, just create one from scratch
_scratch_mkfs
- echo "${meta_descr}" > "${POPULATE_METADUMP_DESCR}"
+ echo "${meta_descr}" > "${populate_metadump_descr}"
case "${FSTYP}" in
"xfs")
_scratch_xfs_populate $@
_scratch_xfs_populate_check
- _scratch_xfs_metadump "${POPULATE_METADUMP}"
- local logdev=
+ local logdev=none
[ "$USE_EXTERNAL" = yes -a ! -z "$SCRATCH_LOGDEV" ] && \
logdev=$SCRATCH_LOGDEV
_xfs_metadump "$POPULATE_METADUMP" "$SCRATCH_DEV" "$logdev" \
- compress
+ compress -a -o
;;
"ext2"|"ext3"|"ext4")
_scratch_ext4_populate $@
_filter_fiemap_flags()
{
- $AWK_PROG '
+ local include_encoded_flag=0
+
+ # Unless a first argument is passed and with a value of 1, the fiemap
+ # encoded flag is filtered out.
+ # This is because tests that use this filter's output in their golden
+ # output may get the encoded flag set or not depending on the filesystem
+ # and its configuration. For example, running btrfs with "-o compress"
+ # (or compress-force) set in MOUNT_OPTIONS, then extents that get
+ # compressed are reported with the encoded flag, otherwise that flag is
+ # not reported. Like this the fs configuration does not cause a mismatch
+ # with the golden output, and tests that exercise specific configurations
+ # can explicitly ask for the encoded flag to be printed.
+ if [ ! -z "$1" ] && [ $1 -eq 1 ]; then
+ include_encoded_flag=1
+ fi
+
+ local awk_script='
$3 ~ /hole/ {
print $1, $2, $3;
next;
if (and(flags, 0x2000)) {
flag_str = "shared";
set = 1;
- }
+ }'
+
+ if [ $include_encoded_flag -eq 1 ]; then
+ awk_script=$awk_script'
+ if (and(flags, 0x8)) {
+ if (set) {
+ flag_str = flag_str"|";
+ } else {
+ flag_str = "";
+ }
+ flag_str = flag_str"encoded";
+ set = 1;
+ }'
+ fi
+
+ awk_script=$awk_script'
if (and(flags, 0x1)) {
if (set) {
flag_str = flag_str"|";
+ } else {
+ flag_str = "";
}
flag_str = flag_str"last";
}
print $1, $2, flag_str
- }' |
- _coalesce_extents
+ }'
+
+ $AWK_PROG -e "$awk_script" | _coalesce_extents
}
# Filters fiemap output to only print the
_coalesce_extents
}
+# Column 7 for datadev files and column 5 for rtdev files
# 10000 Unwritten preallocated extent
# 01000 Doesn't begin on stripe unit
# 00100 Doesn't end on stripe unit
print $1, $2, $3;
next;
}
+ $5 ~ /1[01][01][01][01]/ {
+ print $1, $2, "unwritten";
+ next;
+ }
+ $5 ~ /0[01][01][01][01]/ {
+ print $1, $2, "data"
+ }
$7 ~ /1[01][01][01][01]/ {
print $1, $2, "unwritten";
next;
_8k="$((multiple * 8))k"
_12k="$((multiple * 12))k"
_20k="$((multiple * 20))k"
+ _require_congruent_file_oplen "$(dirname "$testfile")" $((multiple * 4096))
# initial test state must be defined, otherwise the first test can fail
# due ot stale file state left from previous tests.
if [ "$remove_testfile" ]; then
rm -f $testfile
fi
- block_size=`_get_block_size $TEST_DIR`
+ block_size=`_get_file_block_size $TEST_DIR`
$XFS_IO_PROG -f -c "truncate $block_size" \
-c "pwrite 0 $block_size" $sync_cmd \
-c "$zero_cmd 128 128" \
[ -n "$XFS_QUOTA_PROG" ] || _notrun "XFS quota user tools not installed"
}
+# Check that a mounted fs has a particular type of quota accounting turned on.
+#
+# The first argument must be the data device of a mounted fs. It must not be
+# the actual mountpath.
+#
+# The second argument is the quota type ('usrquota', 'grpquota', 'prjquota',
+# 'any', or 'all').
+_xfs_quota_acct_enabled()
+{
+ local dev="$1"
+ local qtype="$2"
+ local f_args=()
+ local any=
+
+ case "$qtype" in
+ "usrquota"|"uquota") f_args=("-U");;
+ "grpquota"|"gquota") f_args=("-G");;
+ "prjquota"|"pquota") f_args=("-P");;
+ "all") f_args=("-U" "-G" "-P");;
+ "any") f_args=("-U" "-G" "-P"); any=1;;
+ *) echo "$qtype: Unknown quota type."; return 1;;
+ esac
+
+ if [ "$any" = "1" ]; then
+ for arg in "$f_args"; do
+ $here/src/feature "$arg" "$dev" && return 0
+ done
+ return 1
+ fi
+
+ $here/src/feature "${f_args[@]}" "$dev"
+}
+
+# Require that a mounted fs has a particular type of quota turned on. This
+# takes the same arguments as _xfs_quota_acct_enabled. If the third argument is
+# '-u' (or is empty and dev is $SCRATCH_DEV) the fs will be unmounted on
+# failure.
+_require_xfs_quota_acct_enabled()
+{
+ local dev="$1"
+ local qtype="$2"
+ local umount="$3"
+ local fsname="$dev"
+
+ _xfs_quota_acct_enabled "$dev" "$qtype" "$qmode" && return 0
+
+ if [ -z "$umount" ] && [ "$dev" = "$SCRATCH_DEV" ]; then
+ umount="-u"
+ fi
+ test "$umount" = "-u" && umount "$dev" &>/dev/null
+
+ case "$dev" in
+ "$TEST_DEV") fsname="test";;
+ "$SCRATCH_DEV") fsname="scratch";;
+ esac
+
+ case "$qtype" in
+ "any") qtype="any quotas";;
+ "all") qtype="all quotas";;
+ esac
+
+ _notrun "$qtype: accounting not enabled on $fsname filesystem."
+}
+
#
# checks that xfs_quota can operate on foreign (non-xfs) filesystems
# Skips check on xfs filesystems, old xfs_quota is fine there.
_require_setquota_project()
{
setquota --help 2>&1 | \
- grep -q "\-P, \-\-project[[:space:]]*set limits for project"
+ grep -q -- "-P, --project[[:space:]]*set limits for project"
if [ "$?" -ne 0 ];then
_notrun "setquota doesn't support project quota (-P)"
fi
_cat_passwd | grep -q '^nobody'
[ $? -ne 0 ] && _notrun "password file does not contain user nobody."
- _cat_group | egrep -q '^no(body|group)'
+ _cat_group | grep -Eq '^no(body|group)'
[ $? -ne 0 ] && _notrun "group file does not contain nobody/nogroup."
}
_choose_gid()
{
- _cat_group | egrep '^no(body|group)' | perl -ne '@a = split(/:/); END { printf "id=%d name=%s\n", $a[2],$a[0] }'
+ _cat_group | grep -E '^no(body|group)' | perl -ne '@a = split(/:/); END { printf "id=%d name=%s\n", $a[2],$a[0] }'
}
_choose_prid()
quotacheck -ug $SCRATCH_MNT >>$seqres.full 2>&1
quotaon -ug $SCRATCH_MNT >>$seqres.full 2>&1
# try to turn on project quota if it's supported
- if quotaon --help 2>&1 | grep -q '\-\-project'; then
+ if quotaon --help 2>&1 | grep -q -- '--project'; then
quotaon --project $SCRATCH_MNT >>$seqres.full 2>&1
fi
fi
# Report the block usage of root, $qa_user, and nobody
_report_quota_blocks() {
- repquota $1 | egrep "^($qa_user|root|nobody)" | awk '{print $1, $3, $4, $5}' | sort -r
+ repquota $1 | grep -E "^($qa_user|root|nobody)" | awk '{print $1, $3, $4, $5}' | sort -r
}
# Report the inode usage of root, $qa_user, and nobody
_report_quota_inodes() {
- repquota $1 | egrep "^($qa_user|root|nobody)" | awk '{print $1, $6, $7, $8}' | sort -r
+ repquota $1 | grep -E "^($qa_user|root|nobody)" | awk '{print $1, $6, $7, $8}' | sort -r
}
# Determine which type of quota we're using
BC="$(type -P bc)" || BC=
+_wallclock()
+{
+ date "+%s"
+}
+
_require_math()
{
if [ -z "$BC" ]; then
umask 022
# check for correct setup and source the $FSTYP specific functions now
-case "$FSTYP" in
- xfs)
- [ "$XFS_LOGPRINT_PROG" = "" ] && _fatal "xfs_logprint not found"
- [ "$XFS_REPAIR_PROG" = "" ] && _fatal "xfs_repair not found"
- [ "$XFS_DB_PROG" = "" ] && _fatal "xfs_db not found"
- [ "$MKFS_XFS_PROG" = "" ] && _fatal "mkfs_xfs not found"
- [ "$XFS_INFO_PROG" = "" ] && _fatal "xfs_info not found"
-
- . ./common/xfs
- ;;
- udf)
- [ "$MKFS_UDF_PROG" = "" ] && _fatal "mkfs_udf/mkudffs not found"
- ;;
- btrfs)
- [ "$MKFS_BTRFS_PROG" = "" ] && _fatal "mkfs.btrfs not found"
-
- . ./common/btrfs
- ;;
- ext4)
- [ "$MKFS_EXT4_PROG" = "" ] && _fatal "mkfs.ext4 not found"
- ;;
- f2fs)
- [ "$MKFS_F2FS_PROG" = "" ] && _fatal "mkfs.f2fs not found"
- ;;
- nfs)
- . ./common/nfs
- ;;
- cifs)
- ;;
- 9p)
- ;;
- ceph)
- . ./common/ceph
- ;;
- glusterfs)
- ;;
- overlay)
- . ./common/overlay
- ;;
- reiser4)
- [ "$MKFS_REISER4_PROG" = "" ] && _fatal "mkfs.reiser4 not found"
- ;;
- pvfs2)
- ;;
- ubifs)
- [ "$UBIUPDATEVOL_PROG" = "" ] && _fatal "ubiupdatevol not found"
- ;;
-esac
+_source_specific_fs $FSTYP
if [ ! -z "$REPORT_LIST" ]; then
. ./common/report
stat -c %s "$1"
}
+# Does this kernel support huge pages?
+_require_hugepages()
+{
+ awk '/Hugepagesize/ {print $2}' /proc/meminfo | grep -E -q ^[0-9]+$ || \
+ _notrun "Kernel does not report huge page size"
+}
+
# Get hugepagesize in bytes
_get_hugepagesize()
{
- local hugepgsz=$(awk '/Hugepagesize/ {print $2}' /proc/meminfo)
- # Call _notrun if $hugepgsz is not a number
- echo "$hugepgsz" | egrep -q ^[0-9]+$ || \
- _notrun "Cannot get the value of Hugepagesize"
- echo $((hugepgsz * 1024))
+ awk '/Hugepagesize/ {print $2 * 1024}' /proc/meminfo
}
_mount()
{
- $MOUNT_PROG `_mount_ops_filter $*`
+ $MOUNT_PROG $*
}
# Call _mount to do mount operation but also save mountpoint to
_scratch_options()
{
- local type=$1
- local rt_opt=""
- local log_opt=""
SCRATCH_OPTIONS=""
- if [ "$FSTYP" != "xfs" ]; then
- return
- fi
-
- case $type in
- mkfs)
- SCRATCH_OPTIONS="$SCRATCH_OPTIONS -f"
- rt_opt="-r"
- log_opt="-l"
+ case "$FSTYP" in
+ "xfs")
+ _scratch_xfs_options "$@"
;;
- mount)
- rt_opt="-o"
- log_opt="-o"
+ ext2|ext3|ext4|ext4dev)
+ _scratch_ext4_options "$@"
;;
esac
- [ "$USE_EXTERNAL" = yes -a ! -z "$SCRATCH_RTDEV" ] && \
- SCRATCH_OPTIONS="$SCRATCH_OPTIONS ${rt_opt}rtdev=$SCRATCH_RTDEV"
- [ "$USE_EXTERNAL" = yes -a ! -z "$SCRATCH_LOGDEV" ] && \
- SCRATCH_OPTIONS="$SCRATCH_OPTIONS ${log_opt}logdev=$SCRATCH_LOGDEV"
}
_test_options()
TEST_OPTIONS="$TEST_OPTIONS ${log_opt}logdev=$TEST_LOGDEV"
}
-_mount_ops_filter()
-{
- local params="$*"
- local last_index=$(( $# - 1 ))
-
- [ $last_index -gt 0 ] && shift $last_index
- local fs_escaped=$1
-
- echo $params | \
- $PERL_PROG -ne "s#mtpt=[^,|^\n|^\s]*#mtpt=$fs_escaped\1\2#; print;"
-
-}
-
# Used for mounting non-scratch devices (e.g. loop, dm constructs)
# with the safe set of scratch mount options (e.g. loop image may be
# hosted on $SCRATCH_DEV, so can't use external scratch devices).
local fstyp=`$DF_PROG $dir | tail -1 | $AWK_PROG '{print $2}'`
case "$fstyp" in
xfs)
- $XFS_INFO_PROG $dir | grep -q "ftype=1"
+ _xfs_has_feature $dir ftype
;;
ext2|ext3|ext4)
local dev=`$DF_PROG $dir | tail -1 | $AWK_PROG '{print $1}'`
_overlay_scratch_mount $*
return $?
fi
- _mount -t $FSTYP `_scratch_mount_options $*`
+ _mount -t $FSTYP$FUSE_SUBTYP `_scratch_mount_options $*`
mount_ret=$?
[ $mount_ret -ne 0 ] && return $mount_ret
_idmapped_mount $SCRATCH_DEV $SCRATCH_MNT
local tmp=`mktemp -d`
local mount_rec=`findmnt -rncv -S $dev -o OPTIONS`
+ # We create an idmapped mount where {g,u}id 0 writes to disk as
+ # {g,u}id 10000000 and $(id -u fsgqa) + 10000000. We change ownership
+ # of $mnt, provided it's not read-only, so {g,u} id 0 can actually
+ # create objects in there.
+ if [[ "$mount_rec" != *"ro,"* && "$mount_rec" != *",ro"* ]]; then
+ chown 10000000:10000000 $mnt || return 1
+ fi
+ # But if the mount is already idmapped, then there's nothing more to do.
if [[ "$mount_rec" == *"idmapped"* ]]; then
return 0
fi
- # We create an idmapped mount where {g,u}id 0 writes to disk as
- # {g,u}id 10000000 and $(id -u fsgqa) + 10000000. We change ownership
- # of $mnt so {g,u} id 0 can actually create objects in there.
- chown 10000000:10000000 $mnt || return 1
$here/src/vfs/mount-idmapped \
--map-mount b:10000000:0:100000000000 \
$mnt $tmp
fi
_test_options mount
- _mount -t $FSTYP $TEST_OPTIONS $TEST_FS_MOUNT_OPTS $SELINUX_MOUNT_OPTIONS $* $TEST_DEV $TEST_DIR
+ _mount -t $FSTYP$FUSE_SUBTYP $TEST_OPTIONS $TEST_FS_MOUNT_OPTS $SELINUX_MOUNT_OPTIONS $* $TEST_DEV $TEST_DIR
mount_ret=$?
[ $mount_ret -ne 0 ] && return $mount_ret
_idmapped_mount $TEST_DEV $TEST_DIR
return $mkfs_status
}
-_setup_large_ext4_fs()
-{
- local fs_size=$1
- local tmp_dir=/tmp/
-
- [ "$LARGE_SCRATCH_DEV" != yes ] && return 0
- [ -z "$SCRATCH_DEV_EMPTY_SPACE" ] && SCRATCH_DEV_EMPTY_SPACE=0
- [ $SCRATCH_DEV_EMPTY_SPACE -ge $fs_size ] && return 0
-
- # Default free space in the FS is 50GB, but you can specify more via
- # SCRATCH_DEV_EMPTY_SPACE
- local space_to_consume=$(($fs_size - 50*1024*1024*1024 - $SCRATCH_DEV_EMPTY_SPACE))
-
- # mount the filesystem and create 16TB - 4KB files until we consume
- # all the necessary space.
- _try_scratch_mount 2>&1 >$tmp_dir/mnt.err
- local status=$?
- if [ $status -ne 0 ]; then
- echo "mount failed"
- cat $tmp_dir/mnt.err >&2
- rm -f $tmp_dir/mnt.err
- return $status
- fi
- rm -f $tmp_dir/mnt.err
-
- local file_size=$((16*1024*1024*1024*1024 - 4096))
- local nfiles=0
- while [ $space_to_consume -gt $file_size ]; do
-
- xfs_io -F -f \
- -c "truncate $file_size" \
- -c "falloc -k 0 $file_size" \
- $SCRATCH_MNT/.use_space.$nfiles 2>&1
- status=$?
- if [ $status -ne 0 ]; then
- break;
- fi
-
- space_to_consume=$(( $space_to_consume - $file_size ))
- nfiles=$(($nfiles + 1))
- done
-
- # consume the remaining space.
- if [ $space_to_consume -gt 0 ]; then
- xfs_io -F -f \
- -c "truncate $space_to_consume" \
- -c "falloc -k 0 $space_to_consume" \
- $SCRATCH_MNT/.use_space.$nfiles 2>&1
- status=$?
- fi
- export NUM_SPACE_FILES=$nfiles
-
- _scratch_unmount
- if [ $status -ne 0 ]; then
- echo "large file prealloc failed"
- cat $tmp_dir/mnt.err >&2
- return $status
- fi
- return 0
-}
-
-_scratch_mkfs_ext4()
-{
- local mkfs_cmd="$MKFS_EXT4_PROG -F"
- local mkfs_filter="grep -v -e ^Warning: -e \"^mke2fs \" | grep -v \"^$\""
- local tmp=`mktemp -u`
- local mkfs_status
-
- [ "$USE_EXTERNAL" = yes -a ! -z "$SCRATCH_LOGDEV" ] && \
- $mkfs_cmd -O journal_dev $MKFS_OPTIONS $SCRATCH_LOGDEV && \
- mkfs_cmd="$mkfs_cmd -J device=$SCRATCH_LOGDEV"
-
- _scratch_do_mkfs "$mkfs_cmd" "$mkfs_filter" $* 2>$tmp.mkfserr 1>$tmp.mkfsstd
- mkfs_status=$?
-
- if [ $mkfs_status -eq 0 -a "$LARGE_SCRATCH_DEV" = yes ]; then
- # manually parse the mkfs output to get the fs size in bytes
- local fs_size=`cat $tmp.mkfsstd | awk ' \
- /^Block size/ { split($2, a, "="); bs = a[2] ; } \
- / inodes, / { blks = $3 } \
- /reserved for the super user/ { resv = $1 } \
- END { fssize = bs * blks - resv; print fssize }'`
-
- _setup_large_ext4_fs $fs_size
- mkfs_status=$?
- fi
-
- # output mkfs stdout and stderr
- cat $tmp.mkfsstd
- cat $tmp.mkfserr >&2
- rm -f $tmp.mkfserr $tmp.mkfsstd
-
- return $mkfs_status
-}
-
-_ext4_metadump()
-{
- local device="$1"
- local dumpfile="$2"
- local compressopt="$3"
-
- test -n "$E2IMAGE_PROG" || _fail "e2image not installed"
- $E2IMAGE_PROG -Q "$device" "$dumpfile"
- [ "$compressopt" = "compress" ] && [ -n "$DUMP_COMPRESSOR" ] &&
- $DUMP_COMPRESSOR -f "$dumpfile" &>> "$seqres.full"
-}
-
# Capture the metadata of a filesystem in a dump file for offline analysis.
# This is not supported by all filesystem types, so this function should only
# be used after a test has already failed.
nfs*)
# do nothing for nfs
;;
+ afs*)
+ # do nothing for afs
+ ;;
cifs)
# do nothing for cifs
;;
9p)
# do nothing for 9p
;;
+ fuse)
+ # do nothing for fuse
+ ;;
virtiofs)
# do nothing for virtiofs
;;
ext2|ext3|ext4)
$MKFS_PROG -t $FSTYP -- -F $MKFS_OPTIONS $* $TEST_DEV
;;
+ xfs)
+ $MKFS_PROG -t $FSTYP -- -f $MKFS_OPTIONS $* $TEST_DEV
+ ;;
+ bcachefs)
+ $MKFS_BCACHEFS_PROG $MKFS_OPTIONS $* $TEST_DEV > /dev/null
+ ;;
*)
yes | $MKFS_PROG -t $FSTYP -- $MKFS_OPTIONS $* $TEST_DEV
;;
esac
}
-_mkfs_dev()
+_try_mkfs_dev()
{
- local tmp=`mktemp -u`
case $FSTYP in
nfs*)
# do nothing for nfs
;;
+ afs*)
+ # do nothing for afs
+ ;;
9p)
# do nothing for 9p
;;
+ fuse)
+ # do nothing for fuse
+ ;;
virtiofs)
# do nothing for virtiofs
;;
# do nothing for pvfs2
;;
udf)
- $MKFS_UDF_PROG $MKFS_OPTIONS $* 2>$tmp.mkfserr 1>$tmp.mkfsstd
+ $MKFS_UDF_PROG $MKFS_OPTIONS $*
;;
btrfs)
- $MKFS_BTRFS_PROG $MKFS_OPTIONS $* 2>$tmp.mkfserr 1>$tmp.mkfsstd
+ $MKFS_BTRFS_PROG $MKFS_OPTIONS $*
;;
ext2|ext3|ext4)
- $MKFS_PROG -t $FSTYP -- -F $MKFS_OPTIONS $* \
- 2>$tmp.mkfserr 1>$tmp.mkfsstd
+ $MKFS_PROG -t $FSTYP -- -F $MKFS_OPTIONS $*
;;
xfs)
- $MKFS_PROG -t $FSTYP -- -f $MKFS_OPTIONS $* \
- 2>$tmp.mkfserr 1>$tmp.mkfsstd
+ $MKFS_PROG -t $FSTYP -- -f $MKFS_OPTIONS $*
;;
*)
- yes | $MKFS_PROG -t $FSTYP -- $MKFS_OPTIONS $* \
- 2>$tmp.mkfserr 1>$tmp.mkfsstd
+ yes | $MKFS_PROG -t $FSTYP -- $MKFS_OPTIONS $*
;;
esac
+}
- if [ $? -ne 0 ]; then
+_mkfs_dev()
+{
+ local tmp=`mktemp -u`
+ if ! _try_mkfs_dev "$@" 2>$tmp.mkfserr 1>$tmp.mkfsstd; then
# output stored mkfs output
cat $tmp.mkfserr >&2
cat $tmp.mkfsstd
rm -f $tmp.mkfserr $tmp.mkfsstd
}
-# remove all files in $SCRATCH_MNT, useful when testing on NFS/CIFS
+# remove all files in $SCRATCH_MNT, useful when testing on NFS/AFS/CIFS
_scratch_cleanup_files()
{
case $FSTYP in
local mkfs_status
case $FSTYP in
- nfs*|cifs|ceph|overlay|glusterfs|pvfs2|9p|virtiofs)
+ nfs*|afs|cifs|ceph|overlay|glusterfs|pvfs2|9p|fuse|virtiofs)
# unable to re-create this fstyp, just remove all files in
# $SCRATCH_MNT to avoid EEXIST caused by the leftover files
# created in previous runs
mkfs_cmd="yes | $MKFS_PROG -t $FSTYP --"
mkfs_filter="grep -v -e ^mkfs\.ocfs2"
;;
+ bcachefs)
+ mkfs_cmd="$MKFS_BCACHEFS_PROG"
+ mkfs_filter="cat"
+ ;;
*)
mkfs_cmd="yes | $MKFS_PROG -t $FSTYP --"
mkfs_filter="cat"
# to make sure it has the enough scratch devices including
# replace-target and spare device. Now arg1 here is the
# required number of scratch devices by a-test-case excluding
-# the replace-target and spare device. So this function will
-# set SCRATCH_DEV_POOL to the specified number of devices.
+# the replace-target and spare device. So, this function sets
+# SCRATCH_DEV_POOL to the specified number of devices and also
+# sets a SCRATCH_DEV_NAME array with the names of the devices.
#
# Usage:
# _scratch_dev_pool_get() <ndevs>
export SCRATCH_DEV_POOL_SAVED
SCRATCH_DEV_POOL=${devs[@]:0:$test_ndevs}
export SCRATCH_DEV_POOL
+ SCRATCH_DEV_NAME=( $SCRATCH_DEV_POOL )
+ export SCRATCH_DEV_NAME
}
_scratch_dev_pool_put()
{
+ local ret1
+ local ret2
+
typeset -p SCRATCH_DEV_POOL_SAVED >/dev/null 2>&1
- if [ $? -ne 0 ]; then
+ ret1=$?
+ typeset -p SCRATCH_DEV_NAME >/dev/null 2>&1
+ ret2=$?
+ if [[ $ret1 -ne 0 || $ret2 -ne 0 ]]; then
_fail "Bug: unset val, must call _scratch_dev_pool_get before _scratch_dev_pool_put"
fi
- if [ -z "$SCRATCH_DEV_POOL_SAVED" ]; then
+ if [[ -z "$SCRATCH_DEV_POOL_SAVED" || -z "${SCRATCH_DEV_NAME[@]}" ]]; then
_fail "Bug: str empty, must call _scratch_dev_pool_get before _scratch_dev_pool_put"
fi
+ export SCRATCH_DEV_NAME=()
export SCRATCH_DEV_POOL=$SCRATCH_DEV_POOL_SAVED
export SCRATCH_DEV_POOL_SAVED=""
}
fi
}
+# Round a proposed filesystem size up to the minimium supported size. The
+# input is in MB and so is the output.
+_small_fs_size_mb()
+{
+ local size="$1"
+ local runner_min_size=0
+ local fs_min_size=0
+
+ case "$FSTYP" in
+ xfs)
+ # xfs no longer supports filesystems smaller than 600m
+ fs_min_size=600
+ ;;
+ f2fs)
+ # f2fs-utils 1.9.0 needs at least 38 MB space for f2fs image.
+ # However, f2fs-utils 1.14.0 needs at least 52 MB. Not sure if
+ # it will change again. So just set it 128M.
+ fs_min_size=128
+ ;;
+ esac
+ (( size < fs_min_size )) && size="$fs_min_size"
+
+ # If the test runner wanted a minimum size, enforce that here.
+ test -n "$MIN_FSSIZE" && runner_min_size=$((MIN_FSSIZE / 1048576))
+ (( size < runner_min_size)) && size="$runner_min_size"
+
+ echo "$size"
+}
+
# Create fs of certain size on scratch device
# _scratch_mkfs_sized <size in bytes> [optional blocksize]
_scratch_mkfs_sized()
local fssize=$1
local blocksize=$2
local def_blksz
+ local blocksize_opt
case $FSTYP in
xfs)
btrfs)
def_blksz=`echo $MKFS_OPTIONS | sed -rn 's/.*-s ?+([0-9]+).*/\1/p'`
;;
- ext2|ext3|ext4|ext4dev|udf|reiser4|ocfs2|reiserfs)
+ ext2|ext3|ext4|ext4dev|reiser4|ocfs2|reiserfs)
def_blksz=`echo $MKFS_OPTIONS | sed -rn 's/.*-b ?+([0-9]+).*/\1/p'`
;;
+ udf)
+ def_blksz=`echo $MKFS_OPTIONS | sed -rn 's/.*(-b|--blocksize)[ =]?+([0-9]+).*/\2/p'`
+ if [ -z "$def_blksz" ]; then
+ def_blksz=512
+ fi
+ ;;
jfs)
def_blksz=4096
;;
+ bcachefs)
+ def_blksz=`echo $MKFS_OPTIONS | sed -rn 's/.*(--block_size)[ =]?+([0-9]+).*/\2/p'`
+ [ -n "$blocksize" ] && blocksize_opt="--block_size=$blocksize"
+ [ -n "$def_blksize" ] && blocksize_opt="--block_size=$def_blksize"
+ # If no block size is given by local.confg or parameter, blocksize_opt is empty.
+ # Let MKFS_BCACHEFS_PROG decide block size on its own.
+ ;;
esac
[ -n "$def_blksz" ] && blocksize=$def_blksz
case $FSTYP in
xfs)
# don't override MKFS_OPTIONS that set a block size.
- echo $MKFS_OPTIONS |egrep -q "b?size="
+ echo $MKFS_OPTIONS |grep -E -q "b\s*size="
if [ $? -eq 0 ]; then
_scratch_mkfs_xfs -d size=$fssize $rt_ops
else
fi
;;
ext2|ext3|ext4|ext4dev)
+ # Can't use _scratch_mkfs_ext4 here because the block count has
+ # to come after the device path.
+ if [ "$USE_EXTERNAL" = yes -a ! -z "$SCRATCH_LOGDEV" ]; then
+ ${MKFS_PROG} -F -O journal_dev $MKFS_OPTIONS $SCRATCH_LOGDEV || \
+ _notrun "Could not make scratch logdev"
+ MKFS_OPTIONS="$MKFS_OPTIONS -J device=$SCRATCH_LOGDEV"
+ fi
${MKFS_PROG} -t $FSTYP -F $MKFS_OPTIONS -b $blocksize $SCRATCH_DEV $blocks
;;
gfs2)
export MOUNT_OPTIONS="-o size=$fssize $TMPFS_MOUNT_OPTIONS"
;;
bcachefs)
- $MKFS_PROG -t $FSTYP -- $MKFS_OPTIONS --fs_size=$fssize --block_size=$blocksize $SCRATCH_DEV
+ $MKFS_BCACHEFS_PROG $MKFS_OPTIONS --fs_size=$fssize $blocksize_opt $SCRATCH_DEV
;;
*)
_notrun "Filesystem $FSTYP not supported in _scratch_mkfs_sized"
case $FSTYP in
xfs)
- if echo "$MKFS_OPTIONS" | egrep -q "b?size="; then
- MKFS_OPTIONS=$(echo "$MKFS_OPTIONS" | sed -r "s/(b?size=)[0-9]+k?/\1$blocksize/")
+ if echo "$MKFS_OPTIONS" | grep -E -q "b\s*size="; then
+ MKFS_OPTIONS=$(echo "$MKFS_OPTIONS" | sed -r "s/(b\s*size=)[0-9]+k?/\1$blocksize/")
else
MKFS_OPTIONS+=" -b size=$blocksize"
fi
- if echo "$MKFS_OPTIONS" | egrep -q "(su|sunit|sw|swidth)="; then
+ if echo "$MKFS_OPTIONS" | grep -E -q "(su|sunit|sw|swidth)="; then
MKFS_OPTIONS=$(echo "$MKFS_OPTIONS" | sed -r \
-e "s/(su|sunit)=[0-9kmg]+/su=$sunit_bytes/" \
-e "s/(sw|swidth)=[0-9kmg]+/sw=$swidth_mult/")
if ! [[ $blocksize =~ $re ]] ; then
_notrun "error: _scratch_mkfs_sized: block size \"$blocksize\" not an integer."
fi
+ if [ $blocksize -lt $(_get_page_size) ]; then
+ _exclude_scratch_mount_option dax
+ fi
case $FSTYP in
btrfs)
_scratch_mkfs_xfs $MKFS_OPTIONS -b size=$blocksize
;;
ext2|ext3|ext4)
- ${MKFS_PROG} -t $FSTYP -F $MKFS_OPTIONS -b $blocksize $SCRATCH_DEV
+ _scratch_mkfs_ext4 $MKFS_OPTIONS -b $blocksize
;;
gfs2)
${MKFS_PROG} -t $FSTYP $MKFS_OPTIONS -O -b $blocksize $SCRATCH_DEV
-C $blocksize $SCRATCH_DEV
;;
bcachefs)
- ${MKFS_PROG} -t $FSTYP $MKFS_OPTIONS --block_size=$blocksize \
- $SCRATCH_DEV
+ _scratch_mkfs --block_size=$blocksize
+ ;;
+ udf)
+ _scratch_mkfs -b $blocksize
;;
*)
_notrun "Filesystem $FSTYP not supported in _scratch_mkfs_blocksized"
fi
return $res
;;
+ btrfs)
+ echo "yes|$BTRFS_UTIL_PROG check --repair --force $SCRATCH_DEV"
+ yes | $BTRFS_UTIL_PROG check --repair --force $SCRATCH_DEV 2>&1
+ local res=$?
+ if [ $res -ne 0 ]; then
+ _dump_err2 "btrfs repair failed, err=$res"
+ fi
+ return $res
+ ;;
bcachefs)
# With bcachefs, if fsck detects any errors we consider it a bug and we
# want the test to fail:
esac
}
+_repair_test_fs()
+{
+ case $FSTYP in
+ xfs)
+ _test_xfs_repair "$@" >$tmp.repair 2>&1
+ res=$?
+ if [ "$res" -ne 0 ]; then
+ echo "xfs_repair returns $res; replay log?" >>$tmp.repair
+ _test_mount
+ res=$?
+ if [ $res -gt 0 ]; then
+ echo "mount returns $res; zap log?" >>$tmp.repair
+ _test_xfs_repair -L >>$tmp.repair 2>&1
+ echo "log zap returns $?" >> $tmp.repair
+ else
+ umount "$TEST_DEV"
+ fi
+ _test_xfs_repair "$@" >>$tmp.repair 2>&1
+ res=$?
+ fi
+ ;;
+ btrfs)
+ echo 'yes|$BTRFS_UTIL_PROG check --repair --force "$TEST_DEV"' > \
+ $tmp.repair 2>&1
+ yes | $BTRFS_UTIL_PROG check --repair --force "$TEST_DEV" >> \
+ $tmp.repair 2>&1
+ res=$?
+ ;;
+ *)
+ # Let's hope fsck -y suffices...
+ fsck -t $FSTYP -y $TEST_DEV >$tmp.repair 2>&1
+ res=$?
+ if test "$res" -lt 4 ; then
+ res=0
+ fi
+ ;;
+ esac
+ if [ $res -ne 0 ]; then
+ _log_err "_repair_test_fs: failed, err=$res"
+ echo "*** fsck.$FSTYP output ***" >>$seqres.full
+ cat $tmp.repair >>$seqres.full
+ echo "*** end fsck.$FSTYP output" >>$seqres.full
+
+ fi
+ rm -f $tmp.repair
+ return $res
+}
+
_get_pids_by_name()
{
if [ $# -ne 1 ]
_fixed_by_git_commit kernel $*
}
+# Compare with _fixed_by_* helpers, this helper is used for test cases
+# are not regression tests, e.g. functional tests or maintainer tests,
+# this helper suggests git commits that should be applied to source trees
+# to avoid test failures.
+_wants_git_commit()
+{
+ local pkg=$1
+ shift
+
+ echo "This test wants $pkg fix:" >> $seqres.hints
+ echo " $*" >> $seqres.hints
+ echo >> $seqres.hints
+}
+
+# Refer to _wants_git_commit
+_wants_kernel_commit()
+{
+ _wants_git_commit kernel $*
+}
+
_check_if_dev_already_mounted()
{
local dev=$1
if [ -n "$type" -a "`_fs_type $dev`" != "$type" ]; then
echo "$devname=$dev is mounted but not a type $type filesystem"
- # raw $DF_PROG cannot handle NFS/CIFS/overlay correctly
+ # raw $DF_PROG cannot handle NFS/AFS/CIFS/overlay correctly
_df_device $dev
return 3 # 3 = mounted as wrong type
fi
{
case "$FSTYP" in
glusterfs)
- echo $SCRATCH_DEV | egrep -q ":/?" > /dev/null 2>&1
+ echo $SCRATCH_DEV | grep -E -q ":/?" > /dev/null 2>&1
if [ -z "$SCRATCH_DEV" -o "$?" != "0" ]; then
_notrun "this test requires a valid \$SCRATCH_DEV"
fi
_notrun "this test requires a valid \$SCRATCH_MNT"
fi
;;
- 9p|virtiofs)
+ 9p|fuse|virtiofs)
if [ -z "$SCRATCH_DEV" ]; then
_notrun "this test requires a valid \$SCRATCH_DEV"
fi
_notrun "this test requires a valid \$SCRATCH_MNT"
fi
;;
+ afs)
+ # We only support RW volumes (marked with a '%')
+ # We don't support RO volumes (marked with a '#')
+ echo $SCRATCH_DEV | grep -q "^%" > /dev/null 2>&1
+ if [ -z "$SCRATCH_DEV" -o "$?" != "0" ]; then
+ _notrun "this test requires a valid \$SCRATCH_DEV (must be a RW volume)"
+ fi
+ if [ ! -d "$SCRATCH_MNT" ]; then
+ _notrun "this test requires a valid \$SCRATCH_MNT"
+ fi
+ ;;
ceph)
echo $SCRATCH_DEV | grep -qE "=/|:/" > /dev/null 2>&1
if [ -z "$SCRATCH_DEV" -o "$?" != "0" ]; then
exit 1
fi
fi
- rm -f ${RESULT_DIR}/require_scratch
+ rm -f ${RESULT_DIR}/require_scratch "$RESULT_DIR/.skip_orebuild" "$RESULT_DIR/.skip_rebuild"
}
# we need the scratch device and it needs to not be an lvm device
_require_scratch
local devsize=`_get_device_size $SCRATCH_DEV`
- [ $devsize -lt $1 ] && _notrun "scratch dev too small"
+ [ $devsize -lt $1 ] && _notrun "scratch device too small, $devsize < $1"
}
# require a scratch dev of a minimum size (in kb) and should not be checked
_require_scratch_nocheck
local devsize=`_get_device_size $SCRATCH_DEV`
- [ $devsize -lt $1 ] && _notrun "scratch dev too small"
+ [ $devsize -lt $1 ] && _notrun "scratch device size too small, $devsize < $1"
}
# Require scratch fs which supports >16T of filesystem size.
_scratch_unmount
}
+# Require test fs supports delay allocation.
+_require_test_delalloc()
+{
+ _require_command "$FILEFRAG_PROG" filefrag
+
+ rm -f $TEST_DIR/testy
+ $XFS_IO_PROG -f -c 'pwrite 0 64k' $TEST_DIR/testy &> /dev/null
+ $FILEFRAG_PROG -v $TEST_DIR/testy 2>&1 | grep -q delalloc
+ res=$?
+ rm -f $TEST_DIR/testy
+ test $res -eq 0 || \
+ _notrun "test requires delayed allocation buffered writes"
+}
+
# this test needs a test partition - check we're ok & mount it
#
_require_test()
{
case "$FSTYP" in
glusterfs)
- echo $TEST_DEV | egrep -q ":/?" > /dev/null 2>&1
+ echo $TEST_DEV | grep -E -q ":/?" > /dev/null 2>&1
if [ -z "$TEST_DEV" -o "$?" != "0" ]; then
_notrun "this test requires a valid \$TEST_DEV"
fi
_notrun "this test requires a valid \$TEST_DIR"
fi
;;
- 9p|virtiofs)
+ 9p|fuse|virtiofs)
if [ -z "$TEST_DEV" ]; then
_notrun "this test requires a valid \$TEST_DEV"
fi
_notrun "this test requires a valid \$TEST_DIR"
fi
;;
+ afs)
+ # We only support RW volumes (marked with a '%')
+ # We don't support RO volumes (marked with a '#')
+ echo $TEST_DEV | grep -q "^%" > /dev/null 2>&1
+ if [ -z "$TEST_DEV" -o "$?" != "0" ]; then
+ _notrun "this test requires a valid \$TEST_DEV"
+ fi
+ if [ ! -d "$TEST_DIR" ]; then
+ _notrun "this test requires a valid \$TEST_DIR"
+ fi
+ ;;
ceph)
echo $TEST_DEV | grep -qE "=/|:/" > /dev/null 2>&1
if [ -z "$TEST_DEV" -o "$?" != "0" ]; then
$UMOUNT_PROG $SCRATCH_LOGDEV 2>/dev/null
}
+# This test requires that an external log device is not in use
+#
+_require_no_logdev()
+{
+ [ "$USE_EXTERNAL" = "yes" ] && [ -n "$SCRATCH_LOGDEV" ] && \
+ _notrun "Test not compatible with external logs, skipped this test"
+}
+
# this test requires loopback device support
#
_require_loop()
# Decide if the scratch filesystem is likely to be mounted in fsdax mode.
# It goes 3 ways based on mount options::
# 1. "dax" or "dax=always" means always test using DAX
-# 2. "dax=never" means we'll never use DAX
+# 2. "dax=never" means we'll never use DAX.
# 3. "dax=inode" or nothing means "use scratch dev capability" to
# determine whether DAX is going to be used.
#
-# Returns 0 if DAX will be used, 1 if DAX is not going to be used.
+# Case 2 and 3 basically mean the same thing for the purpose of
+# _require_dm_target(). If the fs is not forcing the use of DAX, then DAX
+# can only be enabled if the underlying block device supports it.
+#
+# Returns 0 if the filesytem will use DAX, 1 if it won't.
__scratch_uses_fsdax()
{
local ops=$(_normalize_mount_options "$MOUNT_OPTIONS")
- echo $ops | egrep -qw "dax(=always| |$)" && return 0
- echo $ops | grep -qw "dax=never" && return 1
+ echo $ops | grep -E -qw "dax(=always| |$)" && return 0
+ return 1
+}
+# Determine if the scratch device is DAX capable. Even if the fs is not
+# using DAX, we still can't use certain device mapper targets if the block
+# device is DAX capable. Hence the check needs to be separat from the FS
+# capability.
+__scratch_dev_has_dax()
+{
local sysfs="/sys/block/$(_short_dev $SCRATCH_DEV)"
test -e "${sysfs}/dax" && return 0
test "$(cat "${sysfs}/queue/dax" 2>/dev/null)" = "1" && return 0
+
return 1
}
_require_sane_bdev_flush $SCRATCH_DEV
_require_command "$DMSETUP_PROG" dmsetup
- if __scratch_uses_fsdax; then
- case $target in
- stripe|linear|log-writes)
- ;;
- *)
- _notrun "Cannot run tests with DAX on $target devices."
- ;;
- esac
- fi
+ case $target in
+ stripe|linear|log-writes)
+ ;;
+ *)
+ if __scratch_uses_fsdax; then
+ _notrun "Cannot run tests with fsdax on $target devices."
+ fi
+ if __scratch_dev_has_dax; then
+ _notrun "Cannot use $target devices on DAX capable block devices."
+ fi
+ ;;
+ esac
modprobe dm-$target >/dev/null 2>&1
_notrun "This test requires dm $target support"
fi
- # dm-error cannot handle the zone information
- #
# dm-snapshot and dm-thin-pool cannot ensure sequential writes on
# the backing device
case $target in
- error|snapshot|thin-pool)
+ snapshot|thin-pool)
_require_non_zoned_device ${SCRATCH_DEV}
;;
esac
fi
}
-# this test requires the ext4 kernel support crc feature on scratch device
-#
-_require_scratch_ext4_crc()
-{
- _scratch_mkfs_ext4 >/dev/null 2>&1
- dumpe2fs -h $SCRATCH_DEV 2> /dev/null | grep -q metadata_csum || _notrun "metadata_csum not supported by this filesystem"
- _try_scratch_mount >/dev/null 2>&1 \
- || _notrun "Kernel doesn't support metadata_csum feature"
- _scratch_unmount
-}
-
-# Check whether the specified feature whether it is supported by
-# mkfs.ext4 and the kernel.
-_require_scratch_ext4_feature()
-{
- if [ -z "$1" ]; then
- echo "Usage: _require_scratch_ext4_feature feature"
- exit 1
- fi
- $MKFS_EXT4_PROG -F $MKFS_OPTIONS -O "$1" \
- $SCRATCH_DEV 512m >/dev/null 2>&1 \
- || _notrun "mkfs.ext4 doesn't support $1 feature"
- _try_scratch_mount >/dev/null 2>&1 \
- || _notrun "Kernel doesn't support the ext4 feature(s): $1"
- _scratch_unmount
-}
-
# this test requires that external log/realtime devices are not in use
#
_require_nonexternal()
# this test requires that the kernel supports IO_URING
_require_io_uring()
{
+ local n
+
$here/src/feature -R
case $? in
0)
1)
_notrun "kernel does not support IO_URING"
;;
+ 2)
+ n=$(sysctl -n kernel.io_uring_disabled 2>/dev/null)
+ if [ "$n" != "0" ];then
+ _notrun "io_uring isn't enabled totally by admin"
+ else
+ _fail "unexpected EPERM error, please check selinux or something else"
+ fi
+ ;;
*)
_fail "unexpected error testing for IO_URING support"
;;
# Test xfs_io chattr support AND
# filesystem FS_IOC_FSSETXATTR support
# 'tPnE' flags are only valid for a directory so check them on a directory.
- if echo "$param" | egrep -q 't|P|n|E'; then
+ if echo "$param" | grep -E -q 't|P|n|E'; then
testio=`$XFS_IO_PROG -F -c "chattr +$param" $testdir 2>&1`
attr_info=`$XFS_IO_PROG -F -r -c "lsattr" $testdir | awk '{print $1}'`
$XFS_IO_PROG -F -r -c "chattr -$param" $testdir 2>&1
param_checked="$param"
;;
"fpunch" | "fcollapse" | "zero" | "fzero" | "finsert" | "funshare")
- local blocksize=$(_get_block_size $TEST_DIR)
+ local blocksize=$(_get_file_block_size $TEST_DIR)
testio=`$XFS_IO_PROG -F -f -c "pwrite 0 $((5 * $blocksize))" \
-c "fsync" -c "$command $blocksize $((2 * $blocksize))" \
$testfile 2>&1`
"-T")
# Check O_TMPFILE support in xfs_io, kernel and fs
testio=`$XFS_IO_PROG -T -c quit $TEST_DIR 2>&1`
- echo $testio | egrep -q "invalid option|Is a directory" && \
+ echo $testio | grep -E -q "invalid option|Is a directory" && \
_notrun "xfs_io $command support is missing"
echo $testio | grep -q "Operation not supported" && \
_notrun "O_TMPFILE is not supported"
param_checked="$pwrite_opts $param"
;;
"scrub"|"repair")
- testio=`$XFS_IO_PROG -x -c "$command probe" $TEST_DIR 2>&1`
+ test -z "$param" && param="probe"
+ testio=`$XFS_IO_PROG -x -c "$command $param" $TEST_DIR 2>&1`
echo $testio | grep -q "Inappropriate ioctl" && \
_notrun "xfs_io $command support is missing"
+ param_checked="$param"
+ ;;
+ "startupdate"|"commitupdate"|"cancelupdate")
+ $XFS_IO_PROG -f -c 'pwrite -S 0x58 0 128k -b 128k' $testfile > /dev/null
+ testio=$($XFS_IO_PROG -c "startupdate $param" \
+ -c 'pwrite -S 0x59 0 192k' \
+ -c 'commitupdate' $testfile 2>&1)
+ echo $testio | grep -q "Inappropriate ioctl" && \
+ _notrun "xfs_io $command $param support is missing"
+ echo $testio | grep -q "Operation not supported" && \
+ _notrun "xfs_io $command $param kernel support is missing"
+ param_checked="$param"
+ ;;
+ "swapext")
+ $XFS_IO_PROG -f -c 'pwrite -S 0x58 0 128k -b 128k' $testfile > /dev/null
+ $XFS_IO_PROG -f -c 'truncate 128k' $testfile.1 > /dev/null
+ testio=`$XFS_IO_PROG -c "$command $param $testfile.1" $testfile 2>&1`
+ echo $testio | grep -q "bad argument count" && \
+ _notrun "xfs_io $command $param support is missing"
+ echo $testio | grep -q "Inappropriate ioctl" && \
+ _notrun "xfs_io $command $param ioctl support is missing"
+ echo $testio | grep -q "Operation not supported" && \
+ _notrun "xfs_io $command $param kernel support is missing"
+ rm -f $testfile.1
+ param_checked="$param"
;;
"utimes" )
testio=`$XFS_IO_PROG -f -c "utimes 0 0 0 0" $testfile 2>&1`
[ -n "$param" ] || return
if [ -z "$param_checked" ]; then
- $XFS_IO_PROG -c "help $command" | grep -E -q "^ $param ([a-zA-Z_]+ )?--" || \
+ $XFS_IO_PROG -c "help $command" | grep -E -q "^ $param ([a-zA-Z0-9_]+ )?--" || \
_notrun "xfs_io $command doesn't support $param"
else
# xfs_io could result in "command %c not supported" if it was
fi
}
-# check that kernel and filesystem support direct I/O
+# check that kernel and filesystem support direct I/O, and check if "$1" size
+# aligned (optional) is supported
_require_odirect()
{
+ local blocksize=$1
+ local align_args=${1:+"-b $1"}
+
if [ $FSTYP = "ext4" ] || [ $FSTYP = "f2fs" ] ; then
if echo "$MOUNT_OPTIONS" | grep -q "test_dummy_encryption"; then
_notrun "$FSTYP encryption doesn't support O_DIRECT"
fi
fi
local testfile=$TEST_DIR/$$.direct
- $XFS_IO_PROG -F -f -d -c "pwrite 0 20k" $testfile > /dev/null 2>&1
+ $XFS_IO_PROG -F -f -d -c "pwrite ${align_args} 0 20k" $testfile > /dev/null 2>&1
if [ $? -ne 0 ]; then
- _notrun "O_DIRECT is not supported"
+ if [ -n "$blocksize" ]; then
+ _notrun "O_DIRECT aligned to $blocksize bytes is not supported"
+ else
+ _notrun "O_DIRECT is not supported"
+ fi
fi
rm -f $testfile 2>&1 > /dev/null
}
_scratch_mount
# Minimum size for mkswap is 10 pages
- _format_swapfile "$SCRATCH_MNT/swap" $(($(get_page_size) * 10)) > /dev/null
+ _format_swapfile "$SCRATCH_MNT/swap" $(($(_get_page_size) * 10)) > /dev/null
# ext* has supported all variants of swap files since their
# introduction, so swapon should not fail.
#
# Check if the filesystem supports sparse files.
#
-# Unfortunately there is no better way to do this than a manual black list.
+# Filesystems (such as CIFS mounted from a Windows server) that generally
+# support sparse files but are tricked into creating a non-sparse file by one
+# of the tests are treated here as not supporting sparse files. This special
+# treatment is done due to media wear-out concerns -- e.g., generic/129 would
+# write multiple terabytes of zeros if allowed to run on a filesystem that
+# ignores the request to make a file sparse.
#
_require_sparse_files()
{
- case $FSTYP in
- hfsplus|exfat)
- _notrun "Sparse files not supported by this filesystem type: $FSTYP"
- ;;
- *)
- ;;
- esac
+ local testfile="$TEST_DIR/$$.sparsefiletest"
+ rm -f "$testfile"
+
+ # The write size and offset are specifically chosen to trick the Windows
+ # SMB server implementation into dishonoring the request to create a sparse
+ # file, while still fitting into the 64 kb SMB1 maximum request size.
+ # This also creates a non-sparse file on vfat, exfat, and hfsplus.
+ $XFS_IO_PROG -f -c 'pwrite -b 51200 -S 0x61 1638400 51200' "$testfile" >/dev/null
+
+ resulting_file_size_kb=$( du -sk "$testfile" | cut -f 1 )
+ rm -f "$testfile"
+
+ # The threshold of 1 MB allows for filesystems with such large clusters.
+ [ $resulting_file_size_kb -gt 1024 ] && \
+ _notrun "Sparse files are not supported or do not work as expected"
}
_require_debugfs()
[ -d "$DEBUGFS_MNT/boot_params" ] || _notrun "Debugfs not mounted"
}
-_require_fail_make_request()
+#
+# Return the version of NFS in use on the mount on $1. Returns 0
+# if it's not NFS.
+#
+_nfs_version()
{
- [ -f "$DEBUGFS_MNT/fail_make_request/probability" ] \
- || _notrun "$DEBUGFS_MNT/fail_make_request \
- not found. Seems that CONFIG_FAULT_INJECTION_DEBUG_FS kernel config option not enabled"
-}
+ local mountpoint=$1
+ local nfsvers=""
-# Disable extent zeroing for ext4 on the given device
-_ext4_disable_extent_zeroout()
-{
- local dev=${1:-$TEST_DEV}
- local sdev=`_short_dev $dev`
+ case "$FSTYP" in
+ nfs*)
+ nfsvers=`_mount | grep $1 | sed -n 's/^.*vers=\([0-9.]*\).*$/\1/p'`
+ ;;
+ *)
+ nfsvers="0"
+ ;;
+ esac
- [ -f /sys/fs/ext4/$sdev/extent_max_zeroout_kb ] && \
- echo 0 >/sys/fs/ext4/$sdev/extent_max_zeroout_kb
+ echo "$nfsvers"
}
# The default behavior of SEEK_HOLE is to always return EOF.
nfs*)
# NFSv2, NFSv3, and NFSv4.0/4.1 only support default behavior of SEEK_HOLE,
# while NFSv4.2 supports non-default behavior
- local nfsvers=`_mount() | grep $TEST_DEV | sed -n 's/^.*vers=\([0-9.]*\).*$/\1/p'`
+ local nfsvers=$( _nfs_version "$TEST_DIR" )
[ "$nfsvers" = "4.2" ]
return $?
;;
_scratch_unmount
}
-_require_scratch_richacl_ext4()
-{
- _scratch_mkfs -O richacl >/dev/null 2>&1 \
- || _notrun "can't mkfs $FSTYP with option -O richacl"
- _try_scratch_mount >/dev/null 2>&1 \
- || _notrun "kernel doesn't support richacl feature on $FSTYP"
- _scratch_unmount
-}
-
_require_scratch_richacl_support()
{
_scratch_mount
;;
ext4) _scratch_mkfs -O richacl
;;
- nfs*|cifs|overlay)
+ nfs*|afs|cifs|overlay)
_scratch_mkfs
;;
esac
if [ $USE_REMOUNT -eq 0 ]; then
if [ "$FSTYP" != "overlay" ]; then
- _mount -t $FSTYP $mount_opts $device $mountpoint
+ _mount -t $FSTYP$FUSE_SUBTYP $mount_opts $device $mountpoint
_idmapped_mount $device $mountpoint
else
_overlay_mount $device $mountpoint
# Filter the knowen errors the UDF Verifier reports.
_udf_test_known_error_filter()
{
- egrep -v "PVD 60 Error: Interchange Level: 1, Maximum Interchange Level: 0|FSD 28 Error: Interchange Level: 1, Maximum Interchange Level: 1,|PVD 72 Warning: Volume Set Identifier: \"\*IRIX UDF\",|Warning: [0-9]+ unused blocks NOT marked as unallocated."
+ grep -E -v "PVD 60 Error: Interchange Level: 1, Maximum Interchange Level: 0|FSD 28 Error: Interchange Level: 1, Maximum Interchange Level: 1,|PVD 72 Warning: Volume Set Identifier: \"\*IRIX UDF\",|Warning: [0-9]+ unused blocks NOT marked as unallocated."
}
fi
local device=$1
- local opt_arg=""
+ local blksz=`echo $MKFS_OPTIONS | sed -rn 's/.*(-b|--blocksize)[ =]?+([0-9]+).*/\2/p'`
+ if [ -z "$blksz" ]; then
+ blksz=512
+ fi
+ # Is the filesystem mounted?
+ local type=`_fs_type $device`
+ if [ "$type" = "$FSTYP" ]; then
+ blksz=`blockdev --getbsz $device`
+ local mountpoint=`_umount_or_remount_ro $device`
+ fi
+
+ local opt_arg="-ecclength 1 -blocksize $blksz"
if [ $# -eq 2 ]; then
- opt_arg="-lastvalidblock $(( $2 - 1 ))"
+ opt_arg+=" -lastvalidblock $(( $2 - 1 ))"
fi
rm -f $seqres.checkfs
sleep 1 # Due to a problem with time stamps in udf_test
- $here/src/udf_test $opt_arg $device | tee $seqres.checkfs | egrep "Error|Warning" | \
+ $here/src/udf_test $opt_arg $device | tee $seqres.checkfs | grep -E "Error|Warning" | \
_udf_test_known_error_filter | \
- egrep -iv "Error count:.*[0-9]+.*total occurrences:.*[0-9]+|Warning count:.*[0-9]+.*total occurrences:.*[0-9]+" && \
+ grep -E -iv "Error count:.*[0-9]+.*total occurrences:.*[0-9]+|Warning count:.*[0-9]+.*total occurrences:.*[0-9]+" && \
echo "Warning UDF Verifier reported errors see $seqres.checkfs." && return 1
+ # Remount the filesystem
+ if [ "$type" = "$FSTYP" ]; then
+ _mount_or_remount_rw "$MOUNT_OPTIONS" $device $mountpoint
+ fi
return 0
}
nfs)
# no way to check consistency for nfs
;;
+ afs)
+ # no way to check consistency for afs
+ ;;
cifs)
# no way to check consistency for cifs
;;
9p)
# no way to check consistency for 9p
;;
+ fuse)
+ # no way to check consistency for fuse
+ ;;
virtiofs)
# no way to check consistency for virtiofs
;;
case $FSTYP in
xfs)
- local scratch_log="none"
- local scratch_rt="none"
- [ "$USE_EXTERNAL" = yes -a ! -z "$SCRATCH_LOGDEV" ] && \
- scratch_log="$SCRATCH_LOGDEV"
-
- [ "$USE_EXTERNAL" = yes -a ! -z "$SCRATCH_RTDEV" ] && \
- scratch_rt="$SCRATCH_RTDEV"
-
- _check_xfs_filesystem $device $scratch_log $scratch_rt
+ _check_xfs_scratch_fs $device
;;
udf)
_check_udf_filesystem $device $udf_fsize
nfs*)
# Don't know how to check an NFS filesystem, yet.
;;
+ afs*)
+ # Don't know how to check an AFS filesystem, yet.
+ ;;
cifs)
# Don't know how to check a CIFS filesystem, yet.
;;
9p)
# no way to check consistency for 9p
;;
+ fuse)
+ # no way to check consistency for fuse
+ ;;
virtiofs)
# no way to check consistency for virtiofs
;;
}
print $result
' <$seqfull.cfg)
- rm -f $1
- ln -fs $(basename $1).$suffix $1
+ rm -f $1 || _fail "_link_out_file_named: failed to remove existing output file"
+ ln -fs $(basename $1).$suffix $1 || _fail "$(basename $1).$suffix: could not setup output file"
}
_link_out_file()
exit 1
fi
fi
- # to help better debug when something fails, we remove
- # traces of previous btrfs FS on the dev.
- dd if=/dev/zero of=$i bs=4096 count=100 > /dev/null 2>&1
+ # To help better debug when something fails, we remove
+ # traces of previous btrfs FS on the dev. For zoned devices we
+ # can't use dd as it'll lead to unaligned writes so simply
+ # reset the first two zones.
+ if [ "`_zone_type "$i"`" = "none" ]; then
+ dd if=/dev/zero of=$i bs=4096 count=100 > /dev/null 2>&1
+ else
+ $BLKZONE_PROG reset -c 2 $i
+ fi
done
}
fi
}
+# Make sure the given file access mode is set to use the pagecache. If
+# userspace or kernel don't support statx or STATX_ATTR_DAX, we assume that
+# means pagecache. The sole parameter must be a directory.
+_require_pagecache_access() {
+ local testfile="$1/testfile"
+
+ touch "$testfile"
+ if ! _check_s_dax "$testfile" 0 &>> $seqres.full; then
+ rm -f "$testfile"
+ _notrun 'test requires pagecache access'
+ fi
+
+ rm -f "$testfile"
+}
+
# Check if dax mount options are supported
#
# $1 can be either 'dax=always' or 'dax'
_try_scratch_mount "-o $option" > /dev/null 2>&1 || return 1
- if _fs_options $SCRATCH_DEV | egrep -q "dax(=always|,|$)"; then
+ if _fs_options $SCRATCH_DEV | grep -E -q "dax(=always|,|$)"; then
_scratch_unmount
return 0
else
fi
_require_fstrim
- grep -q "not supported" <($FSTRIM_PROG $1 2>&1)
+ grep -q -E "not supported|not implemented" <($FSTRIM_PROG $1 2>&1)
if [ "$?" = "0" ]
then
_notrun "FITRIM not supported on $1"
fi
}
+# Given a mountpoint and the device associated with that mountpoint, return the
+# maximum start offset that the FITRIM command will accept, in units of 1024
+# byte blocks.
+_discard_max_offset_kb()
+{
+ case "$FSTYP" in
+ xfs)
+ _xfs_discard_max_offset_kb "$1"
+ ;;
+ *)
+ $DF_PROG -k | awk -v dev="$2" -v mnt="$1" '$1 == dev && $7 == mnt { print $3 }'
+ ;;
+ esac
+}
+
_require_dumpe2fs()
{
if [ -z "$DUMPE2FS_PROG" ]; then
shift
while [ $# -gt 0 ]; do
local pattern=$1
- echo "$pattern" | egrep -q "dax(=always|$)" && \
+ echo "$pattern" | grep -E -q "dax(=always|$)" && \
pattern="dax(=always| |$)"
- if echo $mnt_opts | egrep -q "$pattern"; then
+ if echo $mnt_opts | grep -E -q "$pattern"; then
_notrun "mount option \"$1\" not allowed in this test"
fi
shift
{
_exclude_scratch_mount_option "noatime"
case $FSTYP in
- nfs|cifs|virtiofs)
+ nfs|afs|cifs|virtiofs)
_notrun "atime related mount options have no effect on $FSTYP"
;;
+ ceph|ceph-fuse)
+ _notrun "atime not maintained by $FSTYP"
+ ;;
esac
}
echo "Usage: _get_available_space <mnt>"
exit 1
fi
- local avail_kb;
- avail_kb=`$DF_PROG $1 | tail -n1 | awk '{ print $5 }'`
- echo $((avail_kb * 1024))
+ $DF_PROG -B 1 $1 | tail -n1 | awk '{ print $5 }'
+}
+
+# get the total space in bytes
+#
+_get_total_space()
+{
+ if [ -z "$1" ]; then
+ echo "Usage: _get_total_space <mnt>"
+ exit 1
+ fi
+ $DF_PROG -B 1 $1 | tail -n1 | awk '{ print $3 }'
}
# return device size in kb
# _dmesg_since_test_start.
_check_dmesg_for()
{
- _dmesg_since_test_start | egrep -q "$1"
+ _dmesg_since_test_start | grep -E -q "$1"
}
# Default filter for dmesg scanning.
# lockdep.
_check_dmesg_filter()
{
- egrep -v -e "BUG: MAX_LOCKDEP_CHAIN_HLOCKS too low" \
- -e "BUG: MAX_STACK_TRACE_ENTRIES too low"
+ local extra_filter=
+ local filter_file="${RESULT_DIR}/dmesg_filter"
+
+ test -e "$filter_file" && extra_filter="-f $filter_file"
+
+ grep -E -v -e "BUG: MAX_LOCKDEP_CHAIN_HLOCKS too low" \
+ -e "BUG: MAX_STACK_TRACE_ENTRIES too low" \
+ $extra_filter
+}
+
+# Add a simple expression to the default dmesg filter
+_add_dmesg_filter()
+{
+ local regexp="$1"
+ local filter_file="${RESULT_DIR}/dmesg_filter"
+
+ if [ ! -e "$filter_file" ] || ! grep -q "$regexp" "$filter_file"; then
+ echo "$regexp" >> "${RESULT_DIR}/dmesg_filter"
+ fi
}
# check dmesg log for WARNING/Oops/etc.
local filter=${1:-_check_dmesg_filter}
_dmesg_since_test_start | $filter >$seqres.dmesg
- egrep -q -e "kernel BUG at" \
+ grep -E -q -e "kernel BUG at" \
-e "WARNING:" \
-e "\bBUG:" \
-e "Oops:" \
fi
}
+# Log the arguments to the kernel log with userspace annotation, if possible.
+# Output is not sent to stdout.
+_kernlog()
+{
+ if [ -w /dev/ttyprintk ]; then
+ echo "$*" >> /dev/ttyprintk
+ return
+ fi
+
+ if [ -w /dev/kmsg ]; then
+ echo "[U] $*" >> /dev/kmsg
+ return
+ fi
+}
+
+# Convey stdin to the kernel log with userspace annotation, if possible.
+# Output will be appended to any file paths provided as arguments.
+_tee_kernlog()
+{
+ if [ -w /dev/ttyprintk ]; then
+ tee -a /dev/ttyprintk "$@"
+ return
+ fi
+
+ if [ -w /dev/kmsg ]; then
+ awk '{printf("[U] %s\n", $0) >> "/dev/kmsg"; printf("%s\n", $0);}' | tee -a "$@"
+ return
+ fi
+
+ tee -a "$@"
+}
+
+# Make whatever configuration changes we need ahead of testing fs shutdowns due
+# to unexpected IO errors while updating metadata. The sole parameter should
+# be the fs device, e.g. $SCRATCH_DEV.
+_prepare_for_eio_shutdown()
+{
+ local dev="$1"
+
+ case "$FSTYP" in
+ "xfs")
+ _xfs_prepare_for_eio_shutdown "$dev"
+ ;;
+ esac
+}
+
# capture the kmemleak report
_capture_kmemleak()
{
local kern_knob="$DEBUGFS_MNT/kmemleak"
local leak_file="$1"
+ # Some callers pass in /dev/null when they want to clear the
+ # kernel's leak report file and do not care what was in that.
+ [ -f "$leak_file" ] && rm -f "$leak_file"
+
# Tell the kernel to scan for memory leaks. Apparently the write
# returns before the scan is complete, so do it twice in the hopes
# that twice is enough to capture all the leaks.
esac
}
+# Given a file path and a byte length of a file operation under test, ensure
+# that the length is an integer multiple of the file's allocation unit size.
+# In other words, skip the test unless (oplen ≡ alloc_unit mod 0). This is
+# intended for file remapping operations.
+_require_congruent_file_oplen()
+{
+ local file="$1"
+ local alloc_unit=$(_get_file_block_size "$file")
+ local oplen="$2"
+
+ case $FSTYP in
+ nfs*|afs|cifs|9p|virtiofs|ceph|glusterfs|overlay|pvfs2)
+ # Network filesystems don't know about (or tell the client
+ # about) the underlying file allocation unit and they generally
+ # pass the file IO request to the underlying filesystem, so we
+ # don't have anything to check here.
+ return
+ ;;
+ esac
+
+ test $alloc_unit -gt $oplen && \
+ _notrun "$1: file alloc unit $alloc_unit larger than op length $oplen"
+ test $((oplen % alloc_unit)) -eq 0 || \
+ _notrun "$1: file alloc unit $alloc_unit not congruent with op length $oplen"
+}
+
# Get the minimum block size of an fs.
_get_block_size()
{
stat -f -c %S $1
}
+# Obtain the directory block size of an fs.
+_get_dir_block_size()
+{
+ case "$FSTYP" in
+ xfs)
+ _xfs_get_dir_blocksize $1 ;;
+ *)
+ _get_block_size $1 ;;
+ esac
+}
+
# Require that the fundamental allocation unit of a file is the same as the
# filesystem block size. The sole parameter must be the root dir of a
# filesystem.
_notrun "File allocation unit is larger than a filesystem block"
}
-get_page_size()
+_get_page_size()
{
echo $(getconf PAGE_SIZE)
}
rm -f $tmp.fsx
}
-# Test for the existence of a sysfs entry at /sys/fs/$FSTYP/DEV/$ATTR
+_require_statx()
+{
+ $here/src/stat_test --check-statx ||
+ _notrun "This test requires the statx system call"
+}
+
+# Get the path to the sysfs directory for the fs on a device
#
# Only one argument is needed:
-# - attr: path name under /sys/fs/$FSTYP/DEV
+# - dev: mounted block device for the fs
#
# Usage example:
-# _require_fs_sysfs error/fail_at_unmount
-_require_fs_sysfs()
+# _fs_sysfs_dname /dev/mapper/scratch-dev
+_fs_sysfs_dname()
{
- local attr=$1
- local dname
+ local dev=$1
+
+ if [ ! -b "$dev" ]; then
+ _fail "Usage: _fs_sysfs_dname <mounted_device>"
+ fi
case "$FSTYP" in
btrfs)
- dname=$(findmnt -n -o UUID $TEST_DEV) ;;
+ _btrfs_get_fsid $dev ;;
*)
- dname=$(_short_dev $TEST_DEV) ;;
+ _short_dev $dev ;;
esac
-
- if [ -z "$attr" -o -z "$dname" ];then
- _fail "Usage: _require_fs_sysfs <sysfs_attr_path>"
- fi
-
- if [ ! -e /sys/fs/${FSTYP}/${dname}/${attr} ];then
- _notrun "This test requires /sys/fs/${FSTYP}/${dname}/${attr}"
- fi
-}
-
-_require_statx()
-{
- $here/src/stat_test --check-statx ||
- _notrun "This test requires the statx system call"
}
# Write "content" into /sys/fs/$FSTYP/$DEV/$ATTR
_fail "Usage: _set_fs_sysfs_attr <mounted_device> <attr> <content>"
fi
- local dname
- case "$FSTYP" in
- btrfs)
- dname=$(findmnt -n -o UUID ${dev}) ;;
- *)
- dname=$(_short_dev $dev) ;;
- esac
+ local dname=$(_fs_sysfs_dname $dev)
echo "$content" > /sys/fs/${FSTYP}/${dname}/${attr}
}
_fail "Usage: _get_fs_sysfs_attr <mounted_device> <attr>"
fi
- local dname
- case "$FSTYP" in
- btrfs)
- dname=$(findmnt -n -o UUID ${dev}) ;;
- *)
- dname=$(_short_dev $dev) ;;
- esac
+ local dname=$(_fs_sysfs_dname $dev)
cat /sys/fs/${FSTYP}/${dname}/${attr}
}
+# Test for the existence of a sysfs entry at /sys/fs/$FSTYP/$DEV/$ATTR
+#
+# All arguments are necessary, and in this order:
+# - dev: device name, e.g. $SCRATCH_DEV
+# - attr: path name under /sys/fs/$FSTYP/$dev
+#
+# Usage example:
+# _has_fs_sysfs_attr /dev/mapper/scratch-dev error/fail_at_unmount
+_has_fs_sysfs_attr()
+{
+ local dev=$1
+ local attr=$2
+
+ if [ ! -b "$dev" -o -z "$attr" ];then
+ _fail "Usage: _has_fs_sysfs_attr <mounted_device> <attr>"
+ fi
+
+ local dname=$(_fs_sysfs_dname $dev)
+
+ test -e /sys/fs/${FSTYP}/${dname}/${attr}
+}
+
+# Require the existence of a sysfs entry at /sys/fs/$FSTYP/$DEV/$ATTR
+# All arguments are necessary, and in this order:
+# - dev: device name, e.g. $SCRATCH_DEV
+# - attr: path name under /sys/fs/$FSTYP/$dev
+#
+# Usage example:
+# _require_fs_sysfs_attr /dev/mapper/scratch-dev error/fail_at_unmount
+_require_fs_sysfs_attr()
+{
+ _has_fs_sysfs_attr "$@" && return
+
+ local dev=$1
+ local attr=$2
+ local dname=$(_fs_sysfs_dname $dev)
+
+ _notrun "This test requires /sys/fs/${FSTYP}/${dname}/${attr}"
+}
+
+# Test for the existence of a sysfs entry at /sys/fs/$FSTYP/DEV/$ATTR
+#
+# Only one argument is needed:
+# - attr: path name under /sys/fs/$FSTYP/DEV
+#
+# Usage example:
+# _has_fs_sysfs error/fail_at_unmount
+_has_fs_sysfs()
+{
+ _has_fs_sysfs_attr $TEST_DEV "$@"
+}
+
+# Require the existence of a sysfs entry at /sys/fs/$FSTYP/DEV/$ATTR
+_require_fs_sysfs()
+{
+ _has_fs_sysfs "$@" && return
+
+ local attr=$1
+ local dname=$(_short_dev $TEST_DEV)
+
+ _notrun "This test requires /sys/fs/${FSTYP}/${dname}/${attr}"
+}
+
# Generic test for specific filesystem feature.
# Currently only implemented to test overlayfs features.
_require_scratch_feature()
# be UINT32_MAX * block_size, but other filesystems may allow up to LLONG_MAX.
_get_max_file_size()
{
- local testfile=$TEST_DIR/maxfilesize.$seq
+ if [ -z $1 ] || [ ! -d $1 ]; then
+ echo "Missing mount point argument for _get_max_file_size"
+ exit 1
+ fi
+
+ local mnt=$1
+ local testfile=$mnt/maxfilesize.$seq
local l=0
local r=9223372036854775807 # LLONG_MAX
l=$m
fi
done
+ rm -f $testfile
echo $l
}
_dmsetup_create()
{
+ # Wait for udev to settle so that the dm creation doesn't fail because
+ # some udev subprogram opened one of the block devices mentioned in the
+ # table string w/ O_EXCL. Do it again at the end so that an immediate
+ # device open won't also fail.
+ $UDEV_SETTLE_PROG >/dev/null 2>&1
$DMSETUP_PROG create "$@" >>$seqres.full 2>&1 || return 1
$DMSETUP_PROG mknodes >/dev/null 2>&1
$UDEV_SETTLE_PROG >/dev/null 2>&1
ceph|exfat)
_notrun "$FSTYP does not support negative timestamps"
;;
+ nfs*)
+ #
+ # NFSv2/3 timestamps use 32-bit unsigned values, and so
+ # cannot represent values prior to the epoch
+ #
+ local nfsvers=$( _nfs_version "$TEST_DIR" )
+ if [ "$nfsvers" = "2" -o "$nfsvers" = "3" ]; then
+ _notrun "$FSTYP version $nfsvers does not support negative timestamps"
+ fi
+ ;;
esac
}
_fail "Use _hexdump(), please!"
}
+# Try to create a file with inode->i_blocks >= (length / blocksize).
+# There may be some small overhead, e.g. ext2 filesystem allocates a
+# substantial number of blocks to store block mappings. Those are accounted
+# to i_blocks.
+_create_file_sized()
+{
+ local length=$1
+ local file=$2
+ local tmp=`mktemp -u`
+ local ret=0
+
+ $XFS_IO_PROG -ft -c "falloc 0 $length" $file >$tmp.out 2>&1
+ ret=$?
+ if (grep -Eq "Operation not supported|command .* not found" $tmp.out);then
+ # fallocate isn't supported, fallback to general buffer write
+ $XFS_IO_PROG -ft -c "pwrite 0 $length" $file >$tmp.out 2>&1
+ ret=$?
+ fi
+ [ $ret -ne 0 ] && cat $tmp.out
+ rm -f $tmp.out
+ return $ret
+}
+
+# Save an arbitrary coredump to the report directory.
+_save_coredump()
+{
+ local path="$1"
+
+ if [ -z "$seqres" ]; then
+ echo "$path: seqres is not defined; ignoring coredump!"
+ return 1
+ fi
+
+ local core_hash="$(_md5_checksum "$path")"
+ local out_file="${seqres}.core.${core_hash}"
+
+ for dump in "$out_file"*; do
+ if [ -s "$dump" ]; then
+ rm -f "$path"
+ return 0
+ fi
+ done
+
+ mv "$path" "$out_file"
+ test -z "$COREDUMP_COMPRESSOR" && return 0
+
+ $COREDUMP_COMPRESSOR -f "$out_file"
+}
+
+_require_sgid_inheritance()
+{
+ case $FSTYP in
+ afs)
+ _notrun "SGID-based group ID inheritance is not supported on $FSTYP"
+ ;;
+ esac
+}
+
+_require_use_local_uidgid()
+{
+ case $FSTYP in
+ afs)
+ _notrun "$FSTYP doesn't honour local uid and gid"
+ ;;
+ esac
+}
+
+_require_unix_perm_checking()
+{
+ case $FSTYP in
+ afs)
+ _notrun "$FSTYP doesn't perform traditional UNIX perm checking"
+ ;;
+ esac
+}
+
+# Decide if a soak test should continue looping. The sole parameter is the
+# number of soak loops that the test wants to run by default. The actual
+# loop iteration number is stored in SOAK_LOOPIDX until the loop completes.
+#
+# If the test runner set a SOAK_DURATION value, this predicate will keep
+# looping until it has run for at least that long.
+_soak_loop_running() {
+ local max_soak_loops="$1"
+
+ test -z "$SOAK_LOOPIDX" && SOAK_LOOPIDX=1
+
+ if [ -n "$SOAK_DURATION" ]; then
+ if [ -z "$SOAK_DEADLINE" ]; then
+ SOAK_DEADLINE="$(( $(date +%s) + SOAK_DURATION))"
+ fi
+
+ local now="$(date +%s)"
+ if [ "$now" -gt "$SOAK_DEADLINE" ]; then
+ unset SOAK_DEADLINE
+ unset SOAK_LOOPIDX
+ return 1
+ fi
+ SOAK_LOOPIDX=$((SOAK_LOOPIDX + 1))
+ return 0
+ fi
+
+ if [ "$SOAK_LOOPIDX" -gt "$max_soak_loops" ]; then
+ unset SOAK_LOOPIDX
+ return 1
+ fi
+ SOAK_LOOPIDX=$((SOAK_LOOPIDX + 1))
+ return 0
+}
+
+_require_unshare() {
+ unshare -f -r -m -p -U $@ true &>/dev/null || \
+ _notrun "unshare $*: command not found, should be in util-linux"
+}
+
+# Return a random file in a directory. A directory is *not* followed
+# recursively.
+_random_file() {
+ local basedir=$1
+ echo "$basedir/$(ls -U $basedir | shuf -n 1)"
+}
+
init_rc
################################################################################
done
}
+# Create a file of interleaved holes, unwritten blocks, and regular blocks.
+_weave_file_rainbow() {
+ blksz=$1
+ nr=$2
+ dfile=$3
+
+ $XFS_IO_PROG -f -c "truncate $((blksz * nr))" $dfile
+ _pwrite_byte 0x00 0 $((blksz * nr)) $dfile.chk
+ # 0 blocks are unwritten
+ seq 1 5 $((nr - 1)) | while read i; do
+ $XFS_IO_PROG -f -c "falloc $((blksz * i)) $blksz" $dfile
+ _pwrite_byte 0x00 $((blksz * i)) $blksz $dfile.chk
+ done
+ # 1 blocks are holes
+ seq 2 5 $((nr - 1)) | while read i; do
+ _pwrite_byte 0x00 $((blksz * i)) $blksz $dfile.chk
+ done
+ # 2 blocks are regular
+ seq 3 5 $((nr - 1)) | while read i; do
+ _pwrite_byte 0x71 $((blksz * i)) $blksz $dfile
+ _pwrite_byte 0x71 $((blksz * i)) $blksz $dfile.chk
+ done
+ # 3 blocks are holes
+ seq 2 5 $((nr - 1)) | while read i; do
+ _pwrite_byte 0x00 $((blksz * i)) $blksz $dfile.chk
+ done
+ # 4 blocks are delalloc
+ seq 4 5 $((nr - 1)) | while read i; do
+ _pwrite_byte 0x62 $((blksz * i)) $blksz $dfile
+ _pwrite_byte 0x62 $((blksz * i)) $blksz $dfile.chk
+ done
+}
+
# Create a file of interleaved holes, unwritten blocks, regular blocks, and
# reflinked blocks
_weave_reflink_rainbow() {
s/(AG \#)(\d+)/\1AGNO/;
s/(reset bad sb for ag) (\d+)/\1 AGNO/;
s/(unknown block state, ag )(\d+)(, blocks? )(\d+)/\1AGNO\3AGBNO/;
+s/^Superblock has (bad magic number) 0x.*/\1/;
/^Note - quota info will be regenerated on next quota mount.$/ && next;
print;'
}
_filter_dd()
{
- fgrep -v records # lose records in/out lines
+ grep -F -v records # lose records in/out lines
}
# do some controlled corrupting & ensure repair recovers us
# List of xfstests's enviroment variables to include reports
## TODO automate list population inside common/conf
-REPORT_ENV_LIST="$REPORT_ENV_LIST SECTION"
-REPORT_ENV_LIST="$REPORT_ENV_LIST FSTYP"
-REPORT_ENV_LIST="$REPORT_ENV_LIST PLATFORM"
-REPORT_ENV_LIST="$REPORT_ENV_LIST MKFS_OPTIONS"
-REPORT_ENV_LIST="$REPORT_ENV_LIST MOUNT_OPTIONS"
-
-REPORT_ENV_LIST="$REPORT_ENV_LIST HOST_OPTIONS"
-REPORT_ENV_LIST="$REPORT_ENV_LIST CHECK_OPTIONS"
-REPORT_ENV_LIST="$REPORT_ENV_LIST XFS_MKFS_OPTIONS"
-REPORT_ENV_LIST="$REPORT_ENV_LIST TIME_FACTOR"
-REPORT_ENV_LIST="$REPORT_ENV_LIST LOAD_FACTOR"
-
-REPORT_ENV_LIST="$REPORT_ENV_LIST TEST_DIR"
-REPORT_ENV_LIST="$REPORT_ENV_LIST TEST_DEV"
-REPORT_ENV_LIST="$REPORT_ENV_LIST SCRATCH_DEV"
-REPORT_ENV_LIST="$REPORT_ENV_LIST SCRATCH_MNT"
-
-REPORT_ENV_LIST="$REPORT_ENV_LIST OVL_UPPER"
-REPORT_ENV_LIST="$REPORT_ENV_LIST OVL_LOWER"
-REPORT_ENV_LIST="$REPORT_ENV_LIST OVL_WORK"
+REPORT_ENV_LIST=("SECTION" "FSTYP" "PLATFORM" "MKFS_OPTIONS" "MOUNT_OPTIONS" \
+ "HOST_OPTIONS" "CHECK_OPTIONS" "XFS_MKFS_OPTIONS" \
+ "TIME_FACTOR" "LOAD_FACTOR" "TEST_DIR" "TEST_DEV" \
+ "SCRATCH_DEV" "SCRATCH_MNT" "OVL_UPPER" "OVL_LOWER" "OVL_WORK")
+
+# Variables that are captured in the report /if/ they are set.
+REPORT_ENV_LIST_OPT=("TAPE_DEV" "RMT_TAPE_DEV" "FSSTRESS_AVOID" "FSX_AVOID"
+ "KCONFIG_PATH" "PERF_CONFIGNAME" "MIN_FSSIZE"
+ "IDMAPPED_MOUNTS")
encode_xml()
{
-e 's/"/\"/g'
}
+encode_cdata()
+{
+ cat -v | sed -e 's/]]>/]]]]><![CDATA[>/g'
+}
+
+# Fill out REPORT_VARS with information about the block device referred to by
+# the passed-in bash variable.
+__generate_blockdev_report_vars() {
+ local bdev_var="$1"
+ local bdev="${!bdev_var}"
+
+ test -z "$bdev" && return
+ test -b "$bdev" || return
+ local sysfs_bdev="$(_sysfs_dev "$bdev")"
+
+ REPORT_VARS["${bdev_var}_SIZE_KB"]="$(( "$(blockdev --getsz "$bdev")" / 2 ))"
+ REPORT_VARS["${bdev_var}_ROTATIONAL"]="$(cat "$sysfs_bdev/queue/rotational" 2>/dev/null)"
+ REPORT_VARS["${bdev_var}_DAX"]="$(cat "$sysfs_bdev/queue/dax" 2>/dev/null)"
+ REPORT_VARS["${bdev_var}_DISCARD"]="$(sed -e 's/[1-9][0-9]*/1/g' "$sysfs_bdev/queue/discard_max_bytes" 2>/dev/null)"
+ REPORT_VARS["${bdev_var}_WRITE_ZEROES"]="$(sed -e 's/[1-9][0-9]*/1/g' "$sysfs_bdev/queue/write_zeroes_max_bytes" 2>/dev/null)"
+ REPORT_VARS["${bdev_var}_PHYS_BLOCK_BYTES"]="$(cat "$sysfs_bdev/queue/physical_block_size" 2>/dev/null)"
+ REPORT_VARS["${bdev_var}_LBA_BYTES"]="$(cat "$sysfs_bdev/queue/logical_block_size" 2>/dev/null)"
+ REPORT_VARS["${bdev_var}_ZONES"]="$(cat "$sysfs_bdev/queue/nr_zones" 2>/dev/null)"
+}
+
+__import_report_vars() {
+ local fname="$1"
+
+ while IFS=':' read key value; do
+ REPORT_VARS["${key%% }"]="${value## }"
+ done < "$1"
+}
+
+# Fill out REPORT_VARS with tidbits about our test runner configuration.
+# Caller is required to declare REPORT_VARS to be an associative array.
+__generate_report_vars() {
+ test "$REPORT_VARS_FILE" && __import_report_vars "$REPORT_VARS_FILE"
+
+ REPORT_VARS["ARCH"]="$(uname -m)"
+ REPORT_VARS["KERNEL"]="$(uname -r)"
+ REPORT_VARS["CPUS"]="$(getconf _NPROCESSORS_ONLN 2>/dev/null)"
+ REPORT_VARS["MEM_KB"]="$(grep MemTotal: /proc/meminfo | awk '{print $2}')"
+ REPORT_VARS["SWAP_KB"]="$(grep SwapTotal: /proc/meminfo | awk '{print $2}')"
+ test -n "$SOAK_DURATION" && REPORT_VARS["SOAK_DURATION"]="$SOAK_DURATION"
+
+ test -e /sys/devices/system/node/possible && \
+ REPORT_VARS["NUMA_NODES"]="$(cat /sys/devices/system/node/possible 2>/dev/null)"
+
+ __generate_blockdev_report_vars "TEST_DEV"
+ __generate_blockdev_report_vars "SCRATCH_DEV"
+
+ # Add per-filesystem variables to the report variable list
+ test "$FSTYP" = "xfs" && __generate_xfs_report_vars
+ [[ "$FSTYP" == ext[0-9]* ]] && __generate_ext4_report_vars
+
+ # Optional environmental variables
+ for varname in "${REPORT_ENV_LIST_OPT[@]}"; do
+ test -n "${!varname}" && REPORT_VARS["${varname}"]="${!varname}"
+ done
+}
+
#
# Xunit format report functions
_xunit_add_property()
{
local name="$1"
- local value="${!name}"
+ local value="$2"
- if [ ! -z "$value" ]; then
- echo -e "\t\t<property name=\"$name\" value=\"$value\"/>" >> $REPORT_DIR/result.xml
- fi
+ test -z "$value" && return
+
+ local xname="$(echo "$name" | encode_xml)"
+ local xvalue="$(echo "$value" | encode_xml)"
+
+ echo -e "\t\t<property name=\"$xname\" value=\"$xvalue\"/>"
}
+
_xunit_make_section_report()
{
# xfstest:section ==> xunit:testsuite
- local sect_name=$section
- local sect_time=`expr $sect_stop - $sect_start`
- local n_total=`expr $n_try + $n_notrun`
+ local sect_name="$1"
+ local tests_count="$2"
+ local bad_count="$3"
+ local notrun_count="$4"
+ local sect_time="$5"
+ local timestamp
+ local tmp_fn="$REPORT_DIR/result.xml.new"
+ local out_fn="$REPORT_DIR/result.xml"
if [ $sect_name == '-no-sections-' ]; then
sect_name='global'
fi
local report=$tmp.report.xunit.$sect_name.xml
+ rm -f "$tmp_fn"
# Header
- echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" > $REPORT_DIR/result.xml
- if [ -z "$date_time" ]; then
- date_time=$(date +"%F %T")
+ echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" > "$tmp_fn"
+ if [ -n "$test_start_time" ]; then
+ timestamp="$(date -Iseconds --date="$test_start_time")"
+ else
+ timestamp="$(date -Iseconds)"
fi
- local dtime=`echo $date_time| tr " " 'T'`
- local stats="failures=\"$n_bad\" skipped=\"$n_notrun\" tests=\"$n_total\" time=\"$sect_time\""
- local hw_info="hostname=\"$HOST\" timestamp=\"$dtime\" "
- echo "<testsuite name=\"xfstests\" $stats $hw_info >" >> $REPORT_DIR/result.xml
+
+ local fstests_ns="https://git.kernel.org/pub/scm/fs/xfs/xfstests-dev.git"
+ cat >> "$tmp_fn" << ENDL
+<testsuite
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="$fstests_ns $fstests_ns/tree/doc/xunit.xsd"
+
+ name="xfstests"
+ failures="$bad_count" skipped="$notrun_count" tests="$tests_count" time="$sect_time"
+ hostname="$HOST"
+ start_timestamp="$(date -Iseconds --date="$fstests_start_time")"
+ timestamp="$timestamp"
+ report_timestamp="$(date -Iseconds)"
+>
+ENDL
+
+ declare -A REPORT_VARS
+ __generate_report_vars
# Properties
- echo -e "\t<properties>" >> $REPORT_DIR/result.xml
- for p in $REPORT_ENV_LIST;do
- _xunit_add_property "$p"
- done
- echo -e "\t</properties>" >> $REPORT_DIR/result.xml
+ echo -e "\t<properties>" >> "$tmp_fn"
+ (for key in "${!REPORT_VARS[@]}"; do
+ _xunit_add_property "$key" "${REPORT_VARS["$key"]}"
+ done;
+ for p in "${REPORT_ENV_LIST[@]}"; do
+ _xunit_add_property "$p" "${!p}"
+ done) | sort >> "$tmp_fn"
+ echo -e "\t</properties>" >> "$tmp_fn"
if [ -f $report ]; then
- cat $report >> $REPORT_DIR/result.xml
+ cat $report >> "$tmp_fn"
fi
- echo "</testsuite>" >> $REPORT_DIR/result.xml
- echo "Xunit report: $REPORT_DIR/result.xml"
+ echo "</testsuite>" >> "$tmp_fn"
+ sync "$tmp_fn" && mv "$tmp_fn" "$out_fn"
+ echo "Xunit report: $out_fn"
}
_xunit_make_testcase_report()
{
- local test_seq="$1"
- local test_status="$2"
- local test_time=`expr $stop - $start`
- local strip="$SRC_DIR/"
- local test_name=${test_seq#$strip}
- local sect_name=$section
+ local sect_name="$1"
+ local test_name="$2"
+ local test_status="$3"
+ local test_time="$4"
+ local report_format="$5"
+ local quiet
+
+ if [ "$report_format" = xunit-quiet ]; then
+ quiet=yes
+ fi
# TODO: other places may also win if no-section mode will be named like 'default/global'
if [ $sect_name == '-no-sections-' ]; then
sect_name='global'
-
fi
local report=$tmp.report.xunit.$sect_name.xml
"pass")
;;
"notrun")
- if [ -f $seqres.notrun ]; then
- local msg=`cat $seqres.notrun | encode_xml`
+ local notrun_file="${REPORT_DIR}/${test_name}.notrun"
+ if [ -f "$notrun_file" ]; then
+ local msg=`cat "$notrun_file" | encode_xml`
echo -e "\t\t<skipped message=\"$msg\" />" >> $report
else
echo -e "\t\t<skipped/>" >> $report
echo -e "\t\t<skipped/>" >> $report
;;
"fail")
+ local out_src="${SRC_DIR}/${test_name}.out"
+ local full_file="${REPORT_DIR}/${test_name}.full"
+ local dmesg_file="${REPORT_DIR}/${test_name}.dmesg"
+ local outbad_file="${REPORT_DIR}/${test_name}.out.bad"
if [ -z "$_err_msg" ]; then
- _err_msg="Test $sequm failed, reason unknown"
+ _err_msg="Test $test_name failed, reason unknown"
fi
echo -e "\t\t<failure message=\"$_err_msg\" type=\"TestFail\" />" >> $report
- if [ -s $seqres.full ]; then
+ if [ -z "$quiet" -a -s "$full_file" ]; then
echo -e "\t\t<system-out>" >> $report
printf '<![CDATA[\n' >>$report
- cat $seqres.full | tr -dc '[:print:][:space:]' | encode_xml >>$report
+ cat "$full_file" | tr -dc '[:print:][:space:]' | encode_cdata >>$report
printf ']]>\n' >>$report
echo -e "\t\t</system-out>" >> $report
fi
- if [ -f $seqres.dmesg ]; then
- echo -e "\t\t<system-err>" >> $report
+ if [ -z "$quiet" -a -f "$dmesg_file" ]; then
+ echo -e "\t\t<kernel-log>" >> $report
printf '<![CDATA[\n' >>$report
- cat $seqres.dmesg | tr -dc '[:print:][:space:]' | encode_xml >>$report
+ cat "$dmesg_file" | tr -dc '[:print:][:space:]' | encode_cdata >>$report
printf ']]>\n' >>$report
- echo -e "\t\t</system-err>" >> $report
- elif [ -s $seqres.out.bad ]; then
+ echo -e "\t\t</kernel-log>" >> $report
+ fi
+ if [ -z "$quiet" -a -s "$outbad_file" ]; then
echo -e "\t\t<system-err>" >> $report
printf '<![CDATA[\n' >>$report
- $diff $test_seq.out $seqres.out.bad | encode_xml >>$report
+ $diff "$out_src" "$outbad_file" | encode_cdata >>$report
printf ']]>\n' >>$report
echo -e "\t\t</system-err>" >> $report
fi
# Common report generator entry points
_make_section_report()
{
+ local sect_name="$1"
+ local tests_count="$2"
+ local bad_count="$3"
+ local notrun_count="$4"
+ local sect_time="$5"
for report in $REPORT_LIST; do
case "$report" in
- "xunit")
- _xunit_make_section_report "$test_status"
+ "xunit"|"xunit-quiet")
+ _xunit_make_section_report "$sect_name" "$tests_count" \
+ "$bad_count" "$notrun_count" \
+ "$sect_time"
;;
*)
_dump_err "format '$report' is not supported"
_make_testcase_report()
{
- local test_seq="$1"
- local test_status="$2"
+ local sect_name="$1"
+ local test_seq="$2"
+ local test_status="$3"
+ local test_time="$4"
for report in $REPORT_LIST; do
case "$report" in
- "xunit")
- _xunit_make_testcase_report "$test_seq" "$test_status"
+ "xunit"|"xunit-quiet")
+ _xunit_make_testcase_report "$sect_name" "$test_seq" \
+ "$test_status" "$test_time" "$report"
;;
*)
_dump_err "report format '$report' is not supported"
_assert_report_list() {
for report in $REPORT_LIST; do
case "$report" in
- "xunit")
+ "xunit"|"xunit-quiet")
;;
*)
_fatal "report format '$report' is not supported"
--- /dev/null
+##/bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# Routines for dealing with ftrace (or any other tracing).
+
+FTRACE_INSTANCES_DIR="/sys/kernel/debug/tracing/instances/"
+
+_require_ftrace() {
+ test -d "$FTRACE_INSTANCES_DIR" || \
+ _notrun "kernel does not support ftrace"
+}
+
+_ftrace_cleanup() {
+ if [ -d "$FTRACE_DIR" ]; then
+ _ftrace_ignore_events
+ # Removing an ftrace buffer requires rmdir, even though the
+ # virtual directory contains children.
+ rmdir "$FTRACE_DIR"
+ fi
+}
+
+_ftrace_setup() {
+ test -n "$FTRACE_DIR" && _fail "_ftrace_setup already run?"
+
+ # Give this fstest its own ftrace buffer so that we don't mess up
+ # any other tracers that might be running.
+ FTRACE_DIR="$FTRACE_INSTANCES_DIR/fstests.$seq"
+
+ test -d "$FTRACE_DIR" && rmdir "$FTRACE_DIR"
+ mkdir "$FTRACE_DIR"
+}
+
+# Intercept the given events. Arguments may be regular expressions.
+_ftrace_record_events() {
+ test -d "$FTRACE_DIR" || _fail "_ftrace_setup not run?"
+
+ for arg in "$@"; do
+ # Replace slashes with semicolons per ftrace convention
+ find "$FTRACE_DIR/events/" -type d -name "$arg" -printf '%P\n' | \
+ tr '/' ':' >> "$FTRACE_DIR/set_event"
+ done
+}
+
+# Stop intercepting the given events. If no arguments, stops all events.
+_ftrace_ignore_events() {
+ test -d "$FTRACE_DIR" || _fail "_ftrace_setup not run?"
+
+ if [ "$#" -eq 0 ]; then
+ echo > "$FTRACE_DIR/set_event"
+ else
+ for arg in "$@"; do
+ # Replace slashes with semicolons per ftrace convention
+ find "$FTRACE_DIR/events/" -type d -name "$arg" -printf '!%P\n' | \
+ tr '/' ':' >> "$FTRACE_DIR/set_event"
+ done
+ fi
+}
+
+# Dump whatever was written to the ftrace buffer since the last time this
+# helper was called.
+_ftrace_dump() {
+ test -d "$FTRACE_DIR" || _fail "_ftrace_setup not run?"
+
+ cat "$FTRACE_DIR/trace"
+}
--- /dev/null
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0-only
+
+_get_leb_size()
+{
+ local ubivol=$1
+
+ cat /sys/class/ubi/`basename $ubivol`/usable_eb_size
+}
#
# Functions for setting up and testing fs-verity
+# btrfs will return IO errors on corrupted data with or without fs-verity.
+# to really test fs-verity, use nodatasum.
+if [ "$FSTYP" == "btrfs" ]; then
+ if [ -z "$MOUNT_OPTIONS" ]; then
+ export MOUNT_OPTIONS="-o nodatasum"
+ else
+ export MOUNT_OPTIONS+=" -o nodatasum"
+ fi
+fi
+
+# Require fs-verity support on the scratch filesystem.
+#
+# FSV_BLOCK_SIZE will be set to a Merkle tree block size that is supported by
+# the filesystem. Other sizes may be supported too, but FSV_BLOCK_SIZE is the
+# only size that is guaranteed to work without any additional checks.
_require_scratch_verity()
{
_require_scratch
# Try to mount the filesystem. If this fails then either the kernel
# isn't aware of fs-verity, or the mkfs options were not compatible with
- # verity (e.g. ext4 with block size != PAGE_SIZE).
+ # verity (e.g. ext4 with block size != PAGE_SIZE on old kernels).
if ! _try_scratch_mount &>>$seqres.full; then
_notrun "kernel is unaware of $FSTYP verity feature," \
"or mkfs options are not compatible with verity"
fi
+ local fstyp=${1:-$FSTYP}
+ local scratch_mnt=${2:-$SCRATCH_MNT}
+
# The filesystem may be aware of fs-verity but have it disabled by
# CONFIG_FS_VERITY=n. Detect support via sysfs.
- if [ ! -e /sys/fs/$FSTYP/features/verity ]; then
- _notrun "kernel $FSTYP isn't configured with verity support"
+ if [ ! -e /sys/fs/$fstyp/features/verity ]; then
+ _notrun "kernel $fstyp isn't configured with verity support"
+ fi
+
+ # Select a default Merkle tree block size for when tests don't
+ # explicitly specify one.
+ #
+ # For consistency reasons, all 'fsverity' subcommands, including
+ # 'fsverity enable', default to 4K Merkle tree blocks. That's generally
+ # not ideal for tests, since it's possible that the filesystem doesn't
+ # support 4K blocks but does support another size. Specifically, the
+ # kernel originally supported only merkle_tree_block_size ==
+ # fs_block_size == page_size, and later it was updated to support
+ # merkle_tree_block_size <= min(fs_block_size, page_size).
+ #
+ # Therefore, we default to merkle_tree_block_size == min(fs_block_size,
+ # page_size). That maximizes the chance of verity actually working.
+ local fs_block_size=$(_get_block_size $scratch_mnt)
+ local page_size=$(_get_page_size)
+ if (( fs_block_size <= page_size )); then
+ FSV_BLOCK_SIZE=$fs_block_size
+ else
+ FSV_BLOCK_SIZE=$page_size
fi
# The filesystem may have fs-verity enabled but not actually usable by
# default. E.g., ext4 only supports verity on extent-based files, so it
# doesn't work on ext3-style filesystems. So, try actually using it.
- echo foo > $SCRATCH_MNT/tmpfile
- _disable_fsverity_signatures
- if ! _fsv_enable $SCRATCH_MNT/tmpfile; then
- _restore_fsverity_signatures
- _notrun "$FSTYP verity isn't usable by default with these mkfs options"
+ if ! _fsv_can_enable $scratch_mnt/tmpfile; then
+ _notrun "$fstyp verity isn't usable by default with these mkfs options"
fi
- _restore_fsverity_signatures
- rm -f $SCRATCH_MNT/tmpfile
_scratch_unmount
-
- # Merkle tree block size. Currently all filesystems only support
- # PAGE_SIZE for this. This is also the default for 'fsverity enable'.
- FSV_BLOCK_SIZE=$(get_page_size)
}
# Check for CONFIG_FS_VERITY_BUILTIN_SIGNATURES=y, as well as the userspace
_disable_fsverity_signatures()
{
if [ -e /proc/sys/fs/verity/require_signatures ]; then
- if [ -z "$FSVERITY_SIG_CTL_ORIG" ]; then
- FSVERITY_SIG_CTL_ORIG=$(</proc/sys/fs/verity/require_signatures)
- fi
- echo 0 > /proc/sys/fs/verity/require_signatures
+ _set_fsverity_require_signatures 0
fi
}
# This assumes that _require_fsverity_builtin_signatures() was called.
_enable_fsverity_signatures()
{
- if [ -z "$FSVERITY_SIG_CTL_ORIG" ]; then
- FSVERITY_SIG_CTL_ORIG=$(</proc/sys/fs/verity/require_signatures)
- fi
- echo 1 > /proc/sys/fs/verity/require_signatures
+ _set_fsverity_require_signatures 1
}
-# Restore the original signature verification setting.
+# Restore the original value of fs.verity.require_signatures, i.e. the value it
+# had at the beginning of the test.
_restore_fsverity_signatures()
{
- if [ -n "$FSVERITY_SIG_CTL_ORIG" ]; then
- echo "$FSVERITY_SIG_CTL_ORIG" > /proc/sys/fs/verity/require_signatures
- fi
+ if [ -n "$FSVERITY_SIG_CTL_ORIG" ]; then
+ _set_fsverity_require_signatures "$FSVERITY_SIG_CTL_ORIG"
+ fi
+}
+
+# Restore the previous value of fs.verity.require_signatures, i.e. the value it
+# had just before it was last written to.
+_restore_prev_fsverity_signatures()
+{
+ if [ -n "$FSVERITY_SIG_CTL_PREV" ]; then
+ _set_fsverity_require_signatures "$FSVERITY_SIG_CTL_PREV"
+ fi
+}
+
+_set_fsverity_require_signatures()
+{
+ local newval=$1
+ local oldval=$(</proc/sys/fs/verity/require_signatures)
+ FSVERITY_SIG_CTL_PREV=$oldval
+ if [ -z "$FSVERITY_SIG_CTL_ORIG" ]; then
+ FSVERITY_SIG_CTL_ORIG=$oldval
+ fi
+ echo "$newval" > /proc/sys/fs/verity/require_signatures
}
# Require userspace and kernel support for 'fsverity dump_metadata'.
_fail "Unexpected output from 'fsverity dump_metadata': $(<"$tmpfile")"
}
+# Check for userspace tools needed to corrupt verity data or metadata.
+_require_fsverity_corruption()
+{
+ _require_xfs_io_command "fiemap"
+ if [ $FSTYP == "btrfs" ]; then
+ _require_btrfs_corrupt_block
+ fi
+}
+
_scratch_mkfs_verity()
{
case $FSTYP in
ext4|f2fs)
_scratch_mkfs -O verity
;;
+ btrfs)
+ _scratch_mkfs
+ ;;
+ overlay)
+ _scratch_mkfs # This relies on the scratch fs supporting verity
+ ;;
*)
_notrun "No verity support for $FSTYP"
;;
_fsv_enable()
{
- $FSVERITY_PROG enable "$@"
+ local args=("$@")
+ # If the caller didn't explicitly specify a Merkle tree block size, then
+ # use FSV_BLOCK_SIZE.
+ if ! [[ " $*" =~ " --block-size" ]]; then
+ args+=("--block-size=$FSV_BLOCK_SIZE")
+ fi
+ $FSVERITY_PROG enable "${args[@]}"
}
_fsv_measure()
$FSVERITY_PROG measure "$@" | awk '{print $1}'
}
+_fsv_digest()
+{
+ local args=("$@")
+ # If the caller didn't explicitly specify a Merkle tree block size, then
+ # use FSV_BLOCK_SIZE.
+ if ! [[ " $*" =~ " --block-size" ]]; then
+ args+=("--block-size=$FSV_BLOCK_SIZE")
+ fi
+ $FSVERITY_PROG digest "${args[@]}" | awk '{print $1}'
+}
+
_fsv_sign()
{
- $FSVERITY_PROG sign "$@"
+ local args=("$@")
+ # If the caller didn't explicitly specify a Merkle tree block size, then
+ # use FSV_BLOCK_SIZE.
+ if ! [[ " $*" =~ " --block-size" ]]; then
+ args+=("--block-size=$FSV_BLOCK_SIZE")
+ fi
+ $FSVERITY_PROG sign "${args[@]}"
}
# Generate a file, then enable verity on it.
_fsv_enable "$file" "$@"
}
-_fsv_have_hash_algorithm()
+_fsv_can_enable()
{
- local hash_alg=$1
- local test_file=$2
+ local test_file=$1
+ shift
+ local params=("$@")
+ _disable_fsverity_signatures
rm -f $test_file
head -c 4096 /dev/zero > $test_file
- if ! _fsv_enable --hash-alg=$hash_alg $test_file &>> $seqres.full; then
- # no kernel support
- return 1
- fi
+ _fsv_enable $test_file "${params[@]}" &>> $seqres.full
+ local status=$?
+ _restore_prev_fsverity_signatures
rm -f $test_file
- return 0
+ return $status
}
#
(( offset += ($(_get_filesize $file) + 65535) & ~65535 ))
_fsv_scratch_corrupt_bytes $file $offset
;;
+ btrfs)
+ local ino=$(stat -c '%i' $file)
+ _scratch_unmount
+ local byte=""
+ while read -n 1 byte; do
+ local ascii=$(printf "%d" "'$byte'")
+ # This command will find a Merkle tree item for the inode (-I $ino,37,0)
+ # in the default filesystem tree (-r 5) and corrupt one byte (-b 1) at
+ # $offset (-o $offset) with the ascii representation of the byte we read
+ # (-v $ascii)
+ $BTRFS_CORRUPT_BLOCK_PROG -r 5 -I $ino,37,0 -v $ascii -o $offset -b 1 $SCRATCH_DEV
+ (( offset += 1 ))
+ done
+ _scratch_mount
+ ;;
*)
_fail "_fsv_scratch_corrupt_merkle_tree() unimplemented on $FSTYP"
;;
esac
}
+
+_require_fsverity_max_file_size_limit()
+{
+ case $FSTYP in
+ btrfs|ext4|f2fs)
+ ;;
+ *)
+ _notrun "$FSTYP does not store verity data past EOF; no special file size limit"
+ ;;
+ esac
+}
+
+# Replace fs-verity digests, as formatted by the 'fsverity' tool, with <digest>.
+# This function can be used by tests where fs-verity digests depend on the
+# default Merkle tree block size (FSV_BLOCK_SIZE).
+_filter_fsverity_digest()
+{
+ sed -E 's/\b(sha(256|512)):[a-f0-9]{64,}\b/\1:<digest>/'
+}
# XFS specific common functions.
#
+__generate_xfs_report_vars() {
+ __generate_blockdev_report_vars TEST_RTDEV
+ __generate_blockdev_report_vars TEST_LOGDEV
+ __generate_blockdev_report_vars SCRATCH_RTDEV
+ __generate_blockdev_report_vars SCRATCH_LOGDEV
+
+ REPORT_VARS["XFS_ALWAYS_COW"]="$(cat /sys/fs/xfs/debug/always_cow 2>/dev/null)"
+ REPORT_VARS["XFS_LARP"]="$(cat /sys/fs/xfs/debug/larp 2>/dev/null)"
+ REPORT_ENV_LIST_OPT+=("TEST_XFS_REPAIR_REBUILD" "TEST_XFS_SCRUB_REBUILD")
+}
+
_setup_large_xfs_fs()
{
fs_size=$1
# try again without MKFS_OPTIONS because that's what _scratch_do_mkfs
# will do if we pass in the log size option.
if [ $mkfs_status -ne 0 ] &&
- ! egrep -q '(log size.*too small, minimum|external log device.*too small, must be)' $tmp.mkfserr; then
+ ! grep -E -q '(log size.*too small, minimum|external log device.*too small, must be)' $tmp.mkfserr; then
eval "$mkfs_cmd $extra_mkfs_options $SCRATCH_DEV" \
2>$tmp.mkfserr 1>$tmp.mkfsstd
mkfs_status=$?
return $mkfs_status
}
+# Get the number of realtime extents of a mounted filesystem.
+_xfs_get_rtextents()
+{
+ local path="$1"
+
+ $XFS_INFO_PROG "$path" | sed -n "s/^.*rtextents=\([[:digit:]]*\).*/\1/p"
+}
+
+# Get the realtime extent size of a mounted filesystem.
+_xfs_get_rtextsize()
+{
+ local path="$1"
+
+ $XFS_INFO_PROG "$path" | sed -n "s/^.*realtime.*extsz=\([[:digit:]]*\).*/\1/p"
+}
+
# Get the size of an allocation unit of a file. Normally this is just the
# block size of the file, but for realtime files, this is the realtime extent
# size.
{
local path="$1"
- if ! ($XFS_IO_PROG -c "stat -v" "$path" 2>&1 | egrep -q '(rt-inherit|realtime)'); then
+ if ! ($XFS_IO_PROG -c "stat -v" "$path" 2>&1 | grep -E -q '(rt-inherit|realtime)'); then
_get_block_size "$path"
return
fi
while ! $XFS_INFO_PROG "$path" &>/dev/null && [ "$path" != "/" ]; do
path="$(dirname "$path")"
done
- $XFS_INFO_PROG "$path" | grep realtime | sed -e 's/^.*extsz=\([0-9]*\).*$/\1/g'
+ _xfs_get_rtextsize "$path"
+}
+
+# Get the directory block size of a mounted filesystem.
+_xfs_get_dir_blocksize()
+{
+ local fs="$1"
+
+ $XFS_INFO_PROG "$fs" | sed -n "s/^naming.*bsize=\([[:digit:]]*\).*/\1/p"
+}
+
+# Decide if this path is a file on the realtime device
+_xfs_is_realtime_file()
+{
+ if [ "$USE_EXTERNAL" != "yes" ] || [ -z "$SCRATCH_RTDEV" ]; then
+ return 1
+ fi
+ $XFS_IO_PROG -c 'stat -v' "$1" | grep -q -w realtime
}
# Set or clear the realtime status of every supplied path. The first argument
# For each directory, each file subsequently created will target the given
# device for file data allocations. For each empty regular file, each
# subsequent file data allocation will be on the given device.
+#
+# NOTE: If you call this on $TEST_DIR, you must reset the rtinherit flag state
+# before the end of the test to avoid polluting subsequent tests.
_xfs_force_bdev()
{
local device="$1"
return $status
}
+_scratch_xfs_options()
+{
+ local type=$1
+ local rt_opt=""
+ local log_opt=""
+
+ case $type in
+ mkfs)
+ SCRATCH_OPTIONS="$SCRATCH_OPTIONS -f"
+ rt_opt="-r"
+ log_opt="-l"
+ ;;
+ mount)
+ rt_opt="-o"
+ log_opt="-o"
+ ;;
+ esac
+ [ "$USE_EXTERNAL" = yes -a ! -z "$SCRATCH_RTDEV" ] && \
+ SCRATCH_OPTIONS="$SCRATCH_OPTIONS ${rt_opt}rtdev=$SCRATCH_RTDEV"
+ [ "$USE_EXTERNAL" = yes -a ! -z "$SCRATCH_LOGDEV" ] && \
+ SCRATCH_OPTIONS="$SCRATCH_OPTIONS ${log_opt}logdev=$SCRATCH_LOGDEV"
+}
+
_scratch_xfs_db_options()
{
SCRATCH_OPTIONS=""
_scratch_unmount
}
+# If the xfs_info output for the given XFS filesystem mount mentions the given
+# feature. If so, return 0 for success. If not, return 1 for failure. If the
+# third option is -v, echo 1 for success and 0 for not.
+#
+# Starting with xfsprogs 4.17, this also works for unmounted filesystems.
+# The feature 'realtime' looks for rtextents > 0.
+_xfs_has_feature()
+{
+ local fs="$1"
+ local feat="$2"
+ local verbose="$3"
+ local feat_regex="1"
+
+ case "$feat" in
+ "realtime")
+ feat="rtextents"
+ feat_regex="[1-9][0-9]*"
+ ;;
+ esac
+
+ local answer="$($XFS_INFO_PROG "$fs" 2>&1 | grep -E -w -c "$feat=$feat_regex")"
+ if [ "$answer" -ne 0 ]; then
+ test "$verbose" = "-v" && echo 1
+ return 0
+ fi
+
+ test "$verbose" = "-v" && echo 0
+ return 1
+}
+
+# Require that the xfs_info output for the given XFS filesystem mount mentions
+# the given feature flag. If the third argument is -u (or is empty and the
+# second argument is $SCRATCH_MNT), unmount the fs on failure. If a fourth
+# argument is supplied, it will be used as the _notrun message.
+_require_xfs_has_feature()
+{
+ local fs="$1"
+ local feat="$2"
+ local umount="$3"
+ local message="$4"
+
+ if [ -z "$umount" ] && [ "$fs" = "$SCRATCH_MNT" ]; then
+ umount="-u"
+ fi
+
+ _xfs_has_feature "$1" "$2" && return 0
+
+ test "$umount" = "-u" && umount "$fs" &>/dev/null
+
+ test -n "$message" && _notrun "$message"
+
+ case "$fs" in
+ "$TEST_DIR"|"$TEST_DEV") fsname="test";;
+ "$SCRATCH_MNT"|"$SCRATCH_DEV") fsname="scratch";;
+ *) fsname="$fs";;
+ esac
+ _notrun "$2 not supported by $fsname filesystem type: $FSTYP"
+}
+
# this test requires the xfs kernel support crc feature on scratch device
#
_require_scratch_xfs_crc()
_scratch_mkfs_xfs >/dev/null 2>&1
_try_scratch_mount >/dev/null 2>&1 \
|| _notrun "Kernel doesn't support crc feature"
- $XFS_INFO_PROG $SCRATCH_MNT | grep -q 'crc=1' || _notrun "crc feature not supported by this filesystem"
+ _require_xfs_has_feature $SCRATCH_MNT crc -u \
+ "crc feature not supported by this filesystem"
_scratch_unmount
}
_scratch_unmount
}
+# this test requires the xfs large extent counter feature
+#
+_require_xfs_nrext64()
+{
+ _scratch_mkfs_xfs_supported -m crc=1 -i nrext64 > /dev/null 2>&1 \
+ || _notrun "mkfs.xfs does not support nrext64"
+ _scratch_mkfs_xfs -m crc=1 -i nrext64 > /dev/null 2>&1
+ _try_scratch_mount >/dev/null 2>&1 \
+ || _notrun "kernel does not support nrext64"
+ _scratch_unmount
+}
+
# check that xfs_db supports a specific command
_require_xfs_db_command()
{
_notrun "xfs_db $command support is missing"
}
+# Check the health of a mounted XFS filesystem. Callers probably want to
+# ensure that xfs_scrub has been run first. Returns 1 if unhealthy metadata
+# are found or 0 otherwise.
+_check_xfs_health() {
+ local mntpt="$1"
+ local ret=0
+ local t="$tmp.health_helper"
+
+ test -x "$XFS_SPACEMAN_PROG" || return 0
+
+ $XFS_SPACEMAN_PROG -c 'health -c -q' $mntpt > $t.out 2> $t.err
+ test $? -ne 0 && ret=1
+
+ # Don't return error if userspace or kernel don't support health
+ # reporting.
+ grep -q 'command.*health.*not found' $t.err && return 0
+ grep -q 'Inappropriate ioctl for device' $t.err && return 0
+
+ # Filter out the "please run scrub" message if nothing's been checked.
+ sed -e '/Health status has not been/d' -e '/Please run xfs_scrub/d' -i \
+ $t.err
+
+ grep -q unhealthy $t.out && ret=1
+ test $(wc -l < $t.err) -gt 0 && ret=1
+ cat $t.out
+ cat $t.err 1>&2
+ rm -f $t.out $t.err
+
+ return $ret
+}
+
# Does the filesystem mounted from a particular device support scrub?
_supports_xfs_scrub()
{
test "$FSTYP" = "xfs" || return 1
test -x "$XFS_SCRUB_PROG" || return 1
+ mountpoint $mountpoint >/dev/null || \
+ _fail "$mountpoint is not mounted"
+
# Probe for kernel support...
$XFS_IO_PROG -c 'help scrub' 2>&1 | grep -q 'types are:.*probe' || return 1
$XFS_IO_PROG -c "scrub probe" "$mountpoint" 2>&1 | grep -q "Inappropriate ioctl" && return 1
return 0
}
+# Does the scratch file system support scrub?
+_require_scratch_xfs_scrub()
+{
+ _supports_xfs_scrub $SCRATCH_MNT $SCRATCH_DEV || \
+ _notrun "Scrub not supported"
+}
+
# Save a snapshot of a corrupt xfs filesystem for later debugging.
_xfs_metadump() {
local metadump="$1"
local compressopt="$4"
shift; shift; shift; shift
local options="$@"
- test -z "$options" && options="-a -o"
if [ "$logdev" != "none" ]; then
options="$options -l $logdev"
return $res
}
+# What is the version of this metadump file?
+_xfs_metadumpfile_version() {
+ local file="$1"
+ local magic
+
+ magic="$($XFS_IO_PROG -c 'pread -q -v 0 4' "$file")"
+ case "$magic" in
+ "00000000: 58 4d 44 32 XMD2") echo 2;;
+ "00000000: 58 46 53 4d XFSM") echo 1;;
+ esac
+}
+
+_xfs_mdrestore() {
+ local metadump="$1"
+ local device="$2"
+ local logdev="$3"
+ shift; shift; shift
+ local options="$@"
+ local dumpfile_ver
+
+ # 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 [ ! -e "$metadump" ] && [ -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
+ dumpfile_ver="$(_xfs_metadumpfile_version "$metadump")"
+
+ if [ "$logdev" != "none" ] && [[ $dumpfile_ver > 1 ]]; then
+ # metadump and mdrestore began capturing and restoring the
+ # contents of external log devices with the addition of the
+ # metadump v2 format. Hence it only makes sense to specify -l
+ # here if the dump file itself is in v2 format.
+ #
+ # With a v1 metadump, the log device is not changed by the dump
+ # and restore process. Historically, fstests either didn't
+ # notice or _notrun themselves when external logs were in use.
+ # Don't break that for people testing with xfsprogs < 6.5.
+ options="$options -l $logdev"
+ fi
+
+ $XFS_MDRESTORE_PROG $options "${metadump}" "${device}"
+}
+
+# What is the maximum metadump file format supported by xfs_metadump?
+_xfs_metadump_max_version()
+{
+ if $XFS_METADUMP_PROG --help 2>&1 | grep -q -- '-v version'; then
+ echo 2
+ else
+ echo 1
+ fi
+}
+
# Snapshot the metadata on the scratch device
_scratch_xfs_metadump()
{
_xfs_metadump "$metadump" "$SCRATCH_DEV" "$logdev" nocompress "$@"
}
+# Restore snapshotted metadata on the scratch device
+_scratch_xfs_mdrestore()
+{
+ local metadump=$1
+ shift
+ local logdev=none
+ local options="$@"
+
+ # $SCRATCH_LOGDEV should have a non-zero length value only when all of
+ # the following conditions are met.
+ # 1. Metadump is in v2 format.
+ # 2. Metadump has contents dumped from an external log device.
+ if [ "$USE_EXTERNAL" = yes -a ! -z "$SCRATCH_LOGDEV" ]; then
+ logdev=$SCRATCH_LOGDEV
+ fi
+
+ _xfs_mdrestore "$metadump" "$SCRATCH_DEV" "$logdev" "$@"
+}
+
+# Do not use xfs_repair (offline fsck) to rebuild the filesystem
+_xfs_skip_offline_rebuild() {
+ touch "$RESULT_DIR/.skip_rebuild"
+}
+
+# Do not use xfs_scrub (online fsck) to rebuild the filesystem
+_xfs_skip_online_rebuild() {
+ touch "$RESULT_DIR/.skip_orebuild"
+}
+
# run xfs_check and friends on a FS.
_check_xfs_filesystem()
{
+ local can_scrub=
+
if [ $# -ne 3 ]; then
echo "Usage: _check_xfs_filesystem device <logdev>|none <rtdev>|none" 1>&2
exit 1
# Run online scrub if we can.
mntpt="$(_is_dev_mounted $device)"
if [ -n "$mntpt" ] && _supports_xfs_scrub "$mntpt" "$device"; then
+ can_scrub=1
+
# Tests can create a scenario in which a call to syncfs() issued
# at the end of the execution of the test script would return an
# error code. xfs_scrub internally calls syncfs() before
ok=0
fi
rm -f $tmp.scrub
+
+ # Does the health reporting notice anything?
+ _check_xfs_health $mntpt > $tmp.health 2>&1
+ res=$?
+ if [ $((res ^ ok)) -eq 0 ]; then
+ _log_err "_check_xfs_filesystem: filesystem on $device failed health check"
+ echo "*** xfs_spaceman -c 'health -c -q' output ***" >> $seqres.full
+ cat $tmp.health >> $seqres.full
+ echo "*** end xfs_spaceman output" >> $seqres.full
+ ok=0
+ fi
+ rm -f $tmp.health
fi
if [ "$type" = "xfs" ]; then
if [ "$ok" -ne 1 ] && [ "$DUMP_CORRUPT_FS" = "1" ]; then
local flatdev="$(basename "$device")"
_xfs_metadump "$seqres.$flatdev.check.md" "$device" "$logdev" \
- compress >> $seqres.full
+ compress -a -o >> $seqres.full
fi
# Optionally test the index rebuilding behavior.
- if [ -n "$TEST_XFS_REPAIR_REBUILD" ]; then
+ if [ -n "$TEST_XFS_REPAIR_REBUILD" ] && [ ! -e "$RESULT_DIR/.skip_rebuild" ]; then
rebuild_ok=1
$XFS_REPAIR_PROG $extra_options $extra_log_options $extra_rt_options $device >$tmp.repair 2>&1
if [ $? -ne 0 ]; then
if [ "$rebuild_ok" -ne 1 ] && [ "$DUMP_CORRUPT_FS" = "1" ]; then
local flatdev="$(basename "$device")"
_xfs_metadump "$seqres.$flatdev.rebuild.md" "$device" \
- "$logdev" compress >> $seqres.full
+ "$logdev" compress -a -o >> $seqres.full
fi
fi
_mount_or_remount_rw "$extra_mount_options" $device $mountpoint
fi
+ # If desired, test the online metadata rebuilding behavior if the
+ # filesystem was mounted when this function was called.
+ if [ -n "$TEST_XFS_SCRUB_REBUILD" ] && [ -n "$can_scrub" ] && [ ! -e "$RESULT_DIR/.skip_orebuild" ]; then
+ orebuild_ok=1
+
+ # Walk the entire directory tree to load directory blocks into
+ # memory and populate the dentry cache, which can speed up the
+ # repairs considerably when the directory tree is very large.
+ find $mntpt &>/dev/null &
+
+ XFS_SCRUB_FORCE_REPAIR=1 "$XFS_SCRUB_PROG" -v -d $mntpt 2>&1 | gzip > $tmp.scrub.gz
+ ret=$?
+ if [ $ret -ne 0 ]; then
+ if zgrep -q 'No space left on device' $tmp.scrub.gz; then
+ # It's not an error if the fs does not have
+ # enough space to complete a repair. We will
+ # check everything, though.
+ echo "*** XFS_SCRUB_FORCE_REPAIR=1 xfs_scrub -v -d ran out of space ret=$ret ***" >> $seqres.full
+ echo "See $seqres.scrubout.gz for details." >> $seqres.full
+ mv $tmp.scrub.gz $seqres.scrubout.gz
+ echo "*** end xfs_scrub output" >> $seqres.full
+ else
+ _log_err "_check_xfs_filesystem: filesystem on $device failed scrub orebuild"
+ echo "*** XFS_SCRUB_FORCE_REPAIR=1 xfs_scrub -v -d output ret=$ret ***" >> $seqres.full
+ echo "See $seqres.scrubout.gz for details." >> $seqres.full
+ mv $tmp.scrub.gz $seqres.scrubout.gz
+ echo "*** end xfs_scrub output" >> $seqres.full
+ ok=0
+ orebuild_ok=0
+ fi
+ fi
+ rm -f $tmp.scrub.gz
+
+ # Clear force_repair because xfs_scrub could have set it
+ $XFS_IO_PROG -x -c 'inject noerror' "$mntpt" >> $seqres.full
+
+ "$XFS_SCRUB_PROG" -v -d -n $mntpt > $tmp.scrub 2>&1
+ if [ $? -ne 0 ]; then
+ _log_err "_check_xfs_filesystem: filesystem on $device failed scrub orebuild recheck"
+ echo "*** xfs_scrub -v -d -n output ***" >> $seqres.full
+ cat $tmp.scrub >> $seqres.full
+ echo "*** end xfs_scrub output" >> $seqres.full
+ ok=0
+ orebuild_ok=0
+ fi
+ rm -f $tmp.scrub
+
+ mountpoint=`_umount_or_remount_ro $device`
+
+ $XFS_REPAIR_PROG -n $extra_options $extra_log_options $extra_rt_options $device >$tmp.repair 2>&1
+ if [ $? -ne 0 ]; then
+ _log_err "_check_xfs_filesystem: filesystem on $device is inconsistent (orebuild-reverify)"
+ echo "*** xfs_repair -n output ***" >>$seqres.full
+ cat $tmp.repair >>$seqres.full
+ echo "*** end xfs_repair output" >>$seqres.full
+
+ ok=0
+ orebuild_ok=0
+ fi
+ rm -f $tmp.repair
+
+ if [ $ok -eq 0 ]; then
+ echo "*** mount output ***" >>$seqres.full
+ _mount >>$seqres.full
+ echo "*** end mount output" >>$seqres.full
+ elif [ "$type" = "xfs" ]; then
+ _mount_or_remount_rw "$extra_mount_options" $device $mountpoint
+ fi
+
+ if [ "$orebuild_ok" -ne 1 ] && [ "$DUMP_CORRUPT_FS" = "1" ]; then
+ local flatdev="$(basename "$device")"
+ _xfs_metadump "$seqres.$flatdev.orebuild.md" "$device" \
+ "$logdev" compress -a -o >> $seqres.full
+ fi
+ fi
+
if [ $ok -eq 0 ]; then
status=1
if [ "$iam" != "check" ]; then
return $?
}
+_check_xfs_scratch_fs()
+{
+ local device="${1:-$SCRATCH_DEV}"
+ local scratch_log="none"
+ local scratch_rt="none"
+ [ "$USE_EXTERNAL" = yes -a ! -z "$SCRATCH_LOGDEV" ] && \
+ scratch_log="$SCRATCH_LOGDEV"
+
+ [ "$USE_EXTERNAL" = yes -a ! -z "$SCRATCH_RTDEV" ] && \
+ scratch_rt="$SCRATCH_RTDEV"
+
+ _check_xfs_filesystem $device $scratch_log $scratch_rt
+}
+
+# modeled after _scratch_xfs_repair
+_test_xfs_repair()
+{
+ TEST_OPTIONS=""
+ [ "$USE_EXTERNAL" = yes -a ! -z "$TEST_LOGDEV" ] && \
+ TEST_OPTIONS="-l$TEST_LOGDEV"
+ [ "$USE_EXTERNAL" = yes -a ! -z "$TEST_RTDEV" ] && \
+ TEST_OPTIONS=$TEST_OPTIONS" -r$TEST_RTDEV"
+ [ "$LARGE_TEST_DEV" = yes ] && TEST_OPTIONS=$TEST_OPTIONS" -t"
+ $XFS_REPAIR_PROG $TEST_OPTIONS $* $TEST_DEV
+}
+
_require_xfs_test_rmapbt()
{
_require_test
-
- if [ "$($XFS_INFO_PROG "$TEST_DIR" | grep -c "rmapbt=1")" -ne 1 ]; then
- _notrun "rmapbt not supported by test filesystem type: $FSTYP"
- fi
+ _require_xfs_has_feature "$TEST_DIR" rmapbt
}
_require_xfs_scratch_rmapbt()
_scratch_mkfs > /dev/null
_scratch_mount
- if [ "$($XFS_INFO_PROG "$SCRATCH_MNT" | grep -c "rmapbt=1")" -ne 1 ]; then
- _scratch_unmount
- _notrun "rmapbt not supported by scratch filesystem type: $FSTYP"
- fi
+ _require_xfs_has_feature "$SCRATCH_MNT" rmapbt
_scratch_unmount
}
_get_fs_sysfs_attr $dev error/metadata/${e}/max_retries
_set_fs_sysfs_attr $dev \
- error/metadata/${e}/retry_timeout_seconds 0
+ error/metadata/${e}/retry_timeout_seconds -1
echo -n "error/metadata/${e}/retry_timeout_seconds="
_get_fs_sysfs_attr $dev \
error/metadata/${e}/retry_timeout_seconds
_scratch_unmount
}
+# Prepare a mounted filesystem for an IO error shutdown test by disabling retry
+# for metadata writes. This prevents a (rare) log livelock when:
+#
+# - The log has given out all available grant space, preventing any new
+# writers from tripping over IO errors (and shutting down the fs/log),
+# - All log buffers were written to disk, and
+# - The log tail is pinned because the AIL keeps hitting EIO trying to write
+# committed changes back into the filesystem.
+#
+# Real users might want the default behavior of the AIL retrying writes forever
+# but for testing purposes we don't want to wait.
+#
+# The sole parameter should be the filesystem data device, e.g. $SCRATCH_DEV.
+_xfs_prepare_for_eio_shutdown()
+{
+ local dev="$1"
+ local ctlfile="error/fail_at_unmount"
+
+ # Once we enable IO errors, it's possible that a writer thread will
+ # trip over EIO, cancel the transaction, and shut down the system.
+ # This is expected behavior, so we need to remove the "Internal error"
+ # message from the list of things that can cause the test to be marked
+ # as failed.
+ _add_dmesg_filter "Internal error"
+
+ # Don't retry any writes during the (presumably) post-shutdown unmount
+ _has_fs_sysfs "$ctlfile" && _set_fs_sysfs_attr $dev "$ctlfile" 1
+
+ # Disable retry of metadata writes that fail with EIO
+ for ctl in max_retries retry_timeout_seconds; do
+ ctlfile="error/metadata/EIO/$ctl"
+
+ _has_fs_sysfs "$ctlfile" && _set_fs_sysfs_attr $dev "$ctlfile" 0
+ done
+}
+
# Skip if we are running an older binary without the stricter input checks.
# Make multiple checks to be sure that there is no regression on the one
# selected feature check, which would skew the result.
fi
}
+# Require that XFS is not configured in always_cow mode.
+_require_no_xfs_always_cow()
+{
+ if [ -f /sys/fs/xfs/debug/always_cow ]; then
+ grep -q "1" /sys/fs/xfs/debug/always_cow && \
+ _notrun "test requires XFS always_cow to be off, turn it off to run the test"
+ fi
+}
+
# Get a metadata field
# The first arg is the field name
# The rest of the arguments are xfs_db commands to find the metadata.
done
local wr_cmd="write"
- _scratch_xfs_db -x -c "help write" | egrep -q "(-c|-d)" && value="-- ${value}"
- _scratch_xfs_db -x -c "help write" | egrep -q "(-d)" && wr_cmd="${wr_cmd} -d"
+ _scratch_xfs_db -x -c "help write" | grep -E -q "(-c|-d)" && value="-- ${value}"
+ _scratch_xfs_db -x -c "help write" | grep -E -q "(-d)" && wr_cmd="${wr_cmd} -d"
_scratch_xfs_db -x "${cmds[@]}" -c "${wr_cmd} ${key} ${value}"
}
local pquota=0
# Can't have group and project quotas in XFS v4
- echo "$MOUNT_OPTIONS" | egrep -q "(gquota|grpquota|grpjquota=|gqnoenforce)" && gquota=1
- echo "$MOUNT_OPTIONS" | egrep -q "(\bpquota|prjquota|pqnoenforce)" && pquota=1
+ echo "$MOUNT_OPTIONS" | grep -E -q "(gquota|grpquota|grpjquota=|gqnoenforce)" && gquota=1
+ echo "$MOUNT_OPTIONS" | grep -E -q "(\bpquota|prjquota|pqnoenforce)" && pquota=1
if [ $gquota -gt 0 ] && [ $pquota -gt 0 ]; then
export MOUNT_OPTIONS=$(echo $MOUNT_OPTIONS \
# Find AG count of mounted filesystem
_xfs_mount_agcount()
{
- $XFS_INFO_PROG "$1" | grep agcount= | sed -e 's/^.*agcount=\([0-9]*\),.*$/\1/g'
+ $XFS_INFO_PROG "$1" | sed -n "s/^.*agcount=\([[:digit:]]*\).*/\1/p"
}
# Wipe the superblock of each XFS AGs
_notrun "mkfs.xfs doesn't support bigtime feature"
_try_scratch_mount || \
_notrun "kernel doesn't support xfs bigtime feature"
- $XFS_INFO_PROG "$SCRATCH_MNT" | grep -q -w "bigtime=1" || \
- _notrun "bigtime feature not advertised on mount?"
+ _require_xfs_has_feature $SCRATCH_MNT bigtime -u \
+ "crc feature not supported by this filesystem"
_scratch_unmount
}
print STDOUT "realtime =RDEV extsz=XXX blocks=XXX, rtextents=XXX\n";
}'
}
+
+_require_xfsrestore_xflag()
+{
+ $XFSRESTORE_PROG -h 2>&1 | grep -q -w -e '-x' || \
+ _notrun 'xfsrestore does not support -x flag.'
+}
+
+# Number of bytes reserved for a full inode record, which includes the
+# immediate fork areas.
+_xfs_get_inode_size()
+{
+ local mntpoint="$1"
+
+ $XFS_INFO_PROG "$mntpoint" | sed -n '/meta-data=.*isize/s/^.*isize=\([0-9]*\).*$/\1/p'
+}
+
+# Number of bytes reserved for only the inode record, excluding the
+# immediate fork areas.
+_xfs_get_inode_core_bytes()
+{
+ local dir="$1"
+
+ if _xfs_has_feature "$dir" crc; then
+ # v5 filesystems
+ echo 176
+ else
+ # v4 filesystems
+ echo 96
+ fi
+}
+
+# Create a file with a lower inode number than the root inode number. For this
+# creation, this function runs mkfs and mount on the scratch device with
+# options. This function prints the root inode number and the created inode
+# number.
+_scratch_xfs_create_fake_root()
+{
+ local root_inum
+ local inum
+
+ # A large stripe unit will put the root inode out quite far
+ # due to alignment, leaving free blocks ahead of it.
+ _scratch_mkfs_xfs -d sunit=1024,swidth=1024 > $seqres.full 2>&1 || _fail "mkfs failed"
+
+ # Mounting /without/ a stripe should allow inodes to be allocated
+ # in lower free blocks, without the stripe alignment.
+ _scratch_mount -o sunit=0,swidth=0
+
+ local root_inum=$(stat -c %i $SCRATCH_MNT)
+
+ # Consume space after the root inode so that the blocks before
+ # root look "close" for the next inode chunk allocation
+ $XFS_IO_PROG -f -c "falloc 0 16m" $SCRATCH_MNT/fillfile
+
+ # And make a bunch of inodes until we (hopefully) get one lower
+ # than root, in a new inode chunk.
+ echo "root_inum: $root_inum" >> $seqres.full
+ for i in $(seq 0 4096) ; do
+ fname=$SCRATCH_MNT/$(printf "FILE_%03d" $i)
+ touch $fname
+ inum=$(stat -c "%i" $fname)
+ [[ $inum -lt $root_inum ]] && break
+ done
+
+ echo "created: $inum" >> $seqres.full
+
+ [[ $inum -lt $root_inum ]] || _notrun "Could not set up test"
+
+ echo "$root_inum $inum"
+}
+
+# Find us the path to the AG header containing a per-AG btree with a specific
+# height.
+_scratch_xfs_find_agbtree_height() {
+ local bt_type="$1"
+ local bt_height="$2"
+ local agcount=$(_xfs_mount_agcount $SCRATCH_DEV)
+
+ 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
+
+ for ((agno = 0; agno < agcount; agno++)); do
+ bt_level=$(_scratch_xfs_db -c "${hdr} ${agno}" -c "p ${bt_prefix}level" | awk '{print $3}')
+ # "level" is really the btree height
+ if [ "${bt_level}" -eq "${bt_height}" ]; then
+ echo "${hdr} ${agno}"
+ return 0
+ fi
+ done
+
+ return 1
+}
+
+_require_xfs_mkfs_atomicswap()
+{
+ # atomicswap can be activated on rmap or reflink filesystems.
+ # reflink is newer (4.9 for reflink vs. 4.8 for rmap) so test that.
+ _scratch_mkfs_xfs_supported -m reflink=1 >/dev/null 2>&1 || \
+ _notrun "mkfs.xfs doesn't have atomicswap dependent features"
+}
+
+_require_xfs_scratch_atomicswap()
+{
+ _require_xfs_mkfs_atomicswap
+ _require_scratch
+ _require_xfs_io_command swapext '-v exchrange -a'
+ _scratch_mkfs -m reflink=1 > /dev/null
+ _try_scratch_mount || \
+ _notrun "atomicswap dependencies not supported by scratch filesystem type: $FSTYP"
+ _scratch_unmount
+}
+
+# Return the maximum start offset that the FITRIM command will accept, in units
+# of 1024 byte blocks.
+_xfs_discard_max_offset_kb()
+{
+ $XFS_IO_PROG -c 'statfs' "$1" | \
+ awk '{g[$1] = $3} END {print (g["geom.bsize"] * g["geom.datablocks"] / 1024)}'
+}
--- /dev/null
+#
+# Common zoned block device specific functions
+#
+
+#
+# blkzone report added zone capacity to be printed from v2.37.
+# This filter will add an extra column 'cap' with the same value of
+# 'len'(zone size) for blkzone version < 2.37
+#
+# Before: start: 0x000100000, len 0x040000, wptr 0x000000 ..
+# After: start: 0x000100000, len 0x040000, cap 0x040000, wptr 0x000000 ..
+_filter_blkzone_report()
+{
+ $AWK_PROG -F "," 'BEGIN{OFS=",";} $3 !~ /cap/ {$2=$2","$2;} {print;}' |\
+ sed -e 's/len/cap/2'
+}
+
+_require_limited_active_zones() {
+ local dev=$1
+ local sysfs=$(_sysfs_dev ${dev})
+ local attr="${sysfs}/queue/max_active_zones"
+
+ [ -e "${attr}" ] || _notrun "cannot find queue/max_active_zones. Maybe non-zoned device?"
+ if [ $(cat "${attr}") == 0 ]; then
+ _notrun "this test requires limited active zones"
+ fi
+}
+
+_zone_capacity() {
+ local phy=$1
+ local dev=$2
+
+ [ -z "$dev" ] && dev=$SCRATCH_DEV
+
+ size=$($BLKZONE_PROG report -o $phy -l 1 $dev |\
+ _filter_blkzone_report |\
+ grep -Po "cap 0x[[:xdigit:]]+" | cut -d ' ' -f 2)
+ echo $((size << 9))
+}
btrfs/ioctl.h \
cifs/ioctl.h \
sys/mman.h \
+ linux/ext4.h \
])
AC_CHECK_HEADERS([xfs/xfs_log_format.h],,,[
AC_PACKAGE_WANT_LINUX_FIEMAP_H
AC_PACKAGE_WANT_FALLOCATE
AC_PACKAGE_WANT_OPEN_BY_HANDLE_AT
-AC_PACKAGE_WANT_LINUX_PRCTL_H
AC_PACKAGE_WANT_LINUX_FS_H
AC_PACKAGE_WANT_LIBBTRFSUTIL
AC_HAVE_COPY_FILE_RANGE
+AC_HAVE_SEEK_DATA
+AC_HAVE_BMV_OF_SHARED
+AC_HAVE_NFTW
+AC_HAVE_RLIMIT_NOFILE
+AC_HAVE_XFS_IOC_EXCHANGE_RANGE
+AC_HAVE_FICLONE
+
AC_CHECK_FUNCS([renameat2])
AC_CHECK_FUNCS([reallocarray])
AC_CHECK_TYPES([struct mount_attr], [], [], [[#include <linux/mount.h>]])
#include <stddef.h>
#include <linux/btrfs.h>
]])
+AC_CHECK_DECLS([BTRFS_IOC_SNAP_DESTROY_V2],,,[#include <linux/btrfs.h>])
AC_CONFIG_HEADERS([include/config.h])
AC_CONFIG_FILES([include/builddefs])
dangerous_fuzzers fuzzers that can crash your computer
dangerous_norepair fuzzers to evaluate kernel metadata verifiers
dangerous_online_repair fuzzers to evaluate xfs_scrub online repair
+dangerous_fsstress_repair race fsstress and xfs_scrub online repair
+dangerous_fsstress_scrub race fsstress and xfs_scrub checking
dangerous_repair fuzzers to evaluate xfs_repair offline repair
dangerous_scrub fuzzers to evaluate xfs_scrub checking
+dangerous_selftest selftests that crash/hang
data data loss checkers
dax direct access mode for persistent memory files
db xfs_db functional tests
encrypt encrypted file contents
enospc ENOSPC error reporting
exportfs file handles
+fiemap fiemap ioctl
filestreams XFS filestreams allocator
+fiexchange XFS_IOC_EXCHANGE_RANGE ioctl
freeze filesystem freeze tests
fsck general fsck tests
fsmap FS_IOC_GETFSMAP ioctl
limit resource limits
locks file locking
log metadata logging
+logical_resolve btrfs logical to ino ioctl (logical-resolve command)
logprint xfs_logprint functional tests
long_rw long-soak read write IO path exercisers
metacopy overlayfs metadata-only copy-up
qgroup btrfs qgroup feature
quota filesystem usage quotas
raid btrfs RAID
+raid-stripe-tree btrfs raid-stripe-tree feature
read_repair btrfs error correction on read failure
realtime XFS realtime volumes
recoveryloop crash recovery loops
scrub filesystem metadata scrubbers
seed btrfs seeded filesystems
seek llseek functionality
+selftest tests with fixed results, used to validate testing setup
send btrfs send/receive
shrinkfs decreasing the size of a filesystem
shutdown FS_IOC_SHUTDOWN ioctl
+smoketest Simple smoke tests
snapshot btrfs snapshots
-soak long running soak tests of any kind
+soak long running soak tests whose runtime can be controlled
+ directly by setting the SOAK_DURATION variable
spaceman xfs_spaceman functional tests
splice splice system call
stress fsstress filesystem exerciser
subvol btrfs subvolumes
swap swap files
+swapext XFS_IOC_SWAPEXT ioctl
symlink symbolic links
tape dump and restore with a tape
+tempfsid temporary fsid
thin thin provisioning
trim FITRIM ioctl
udf UDF functionality tests
_require_chattr <letters>
_require_exportfs
+ _require_sgid_inheritance
+ _require_use_local_uidgid
+ _require_unix_perm_checking
(3) System call requirements.
The test also requires the use of the open_by_handle_at() system call and
will be skipped if it isn't available in the kernel.
+_require_sgid_inheritance
+
+ The test required that the $TEST_DEV filesystem supports the inheritance
+ of the SGID bit and the GID from a marked directory. The test will be
+ skipped if not supported.
+
+_require_use_local_uidgid
+
+ The test requires that the $TEST_DEV filesystem sets the uid and gid of a
+ newly created file to the creating process's fsuid and fsgid. Remote
+ filesystems, for example, may choose other settings or not even have these
+ concepts available. The test will be skipped if not supported.
+
+_require_unix_perm_checking
+
+ The test requires that the $TEST_DEV filesystem performs traditional UNIX
+ file permissions checking. A remote filesystem, for example, might use
+ some alternative distributed permissions model involving authentication
+ tokens rather than the local fsuid/fsgid.
+
========================
SYSTEM CALL REQUIREMENTS
--- /dev/null
+ ===========
+ TESTING AFS
+ ===========
+
+xfstests can be used with the Linux kernel AFS filesystem (kafs). kafs mounts
+each volume as a separate volume and, as such, allows them to be individually
+mounted on arbitrary paths. This allows xfstests to be easily configured to
+use specific volumes. This doesn't work with OpenAFS, say, as that operates
+with everything in a single superblock.
+
+xfstests requires that the volumes specified by forced to use RW volumes by
+prefixing the mount device with '%' rather than '#' as per the format of a
+mountpoint string:
+
+ %<volume>
+
+using the workstation cell, or:
+
+ %<cell>:<volume>
+
+using an explicit cell.
+
+For example:
+
+ FSTYP=afs
+ TEST_DEV=%example.com:xfstest.test
+ TEST_DIR=/mnt/xfstest.test
+ SCRATCH_DEV=%example.com:xfstest.scratch
+ SCRATCH_MNT=/mnt/xfstest.scratch
+
+will use the xfstest.test and xfstest.scratch volumes located in the
+example.com cell.
+
+
+Note that AFS in general and kafs in particular lack a number of features that
+can be tested for and so a lot of tests will be skipped.
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<xs:schema
+ xmlns="https://git.kernel.org/pub/scm/fs/xfs/xfstests-dev.git"
+ xmlns:xs="http://www.w3.org/2001/XMLSchema"
+
+ targetNamespace="https://git.kernel.org/pub/scm/fs/xfs/xfstests-dev.git"
+ elementFormDefault="qualified"
+ attributeFormDefault="unqualified">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">fstests xunit test result schema, derived from https://github.com/windyroad/JUnit-Schema</xs:documentation>
+ </xs:annotation>
+ <xs:element name="testsuite" type="testsuite"/>
+ <xs:simpleType name="ISO8601_DATETIME_PATTERN">
+ <xs:restriction base="xs:dateTime">
+ <xs:pattern value="[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}[+-][0-9]{2}:[0-9]{2}"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:element name="testsuites">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">Contains an aggregation of testsuite results</xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="testsuite" minOccurs="0" maxOccurs="unbounded">
+ <xs:complexType>
+ <xs:complexContent>
+ <xs:extension base="testsuite">
+ <xs:attribute name="package" type="xs:token" use="required">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">Derived from testsuite/@name in the non-aggregated documents</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="id" type="xs:int" use="required">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">Starts at '0' for the first testsuite and is incremented by 1 for each following testsuite</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ <xs:complexType name="testsuite">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">Contains the results of executing a testsuite</xs:documentation>
+ </xs:annotation>
+ <xs:sequence>
+ <xs:element name="properties">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">Properties (e.g., environment settings) set during test execution</xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="property" minOccurs="0" maxOccurs="unbounded">
+ <xs:complexType>
+ <xs:attribute name="name" use="required">
+ <xs:simpleType>
+ <xs:restriction base="xs:token">
+ <xs:minLength value="1"/>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+ <xs:attribute name="value" type="xs:string" use="required"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="testcase" minOccurs="0" maxOccurs="unbounded">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:choice minOccurs="0" maxOccurs="1">
+ <xs:element name="skipped" minOccurs="0" maxOccurs="1">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">Indicates that the test was skipped.</xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:simpleContent>
+ <xs:extension base="pre-string">
+ <xs:attribute name="message" type="xs:string">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">The message specifying why the test case was skipped</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:extension>
+ </xs:simpleContent>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="error" minOccurs="0" maxOccurs="1">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">Indicates that the test errored. An errored test is one that had an unanticipated problem. e.g., an unchecked throwable; or a problem with the implementation of the test. Contains as a text node relevant data for the error, e.g., a stack trace</xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:simpleContent>
+ <xs:extension base="pre-string">
+ <xs:attribute name="message" type="xs:string">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">The error message. e.g., if a java exception is thrown, the return value of getMessage()</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="type" type="xs:string" use="required">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">The type of error that occured. e.g., if a java execption is thrown the full class name of the exception.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:extension>
+ </xs:simpleContent>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="failure">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">Indicates that the test failed. A failure is a test which the code has explicitly failed by using the mechanisms for that purpose. e.g., via an assertEquals. Contains as a text node relevant data for the failure, e.g., a stack trace</xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:simpleContent>
+ <xs:extension base="pre-string">
+ <xs:attribute name="message" type="xs:string">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">The message specified in the assert</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="type" type="xs:string" use="required">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">The type of the assert.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:extension>
+ </xs:simpleContent>
+ </xs:complexType>
+ </xs:element>
+ </xs:choice>
+ <xs:choice minOccurs="0" maxOccurs="3">
+ <xs:element name="system-out" minOccurs="0" maxOccurs="1">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">Data that was written to the .full log file while the test was executed.</xs:documentation>
+ </xs:annotation>
+ <xs:simpleType>
+ <xs:restriction base="pre-string">
+ <xs:whiteSpace value="preserve"/>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:element>
+ <xs:element name="system-err" minOccurs="0" maxOccurs="1">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">Data that was compared to the golden output file after the test was executed.</xs:documentation>
+ </xs:annotation>
+ <xs:simpleType>
+ <xs:restriction base="pre-string">
+ <xs:whiteSpace value="preserve"/>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:element>
+ <xs:element name="kernel-log" minOccurs="0" maxOccurs="1">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">Kernel log recorded while the test was executed.</xs:documentation>
+ </xs:annotation>
+ <xs:simpleType>
+ <xs:restriction base="pre-string">
+ <xs:whiteSpace value="preserve"/>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:element>
+ </xs:choice>
+ </xs:sequence>
+ <xs:attribute name="name" type="xs:token" use="required">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">Name of the test method</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="classname" type="xs:token" use="required">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">Full class name for the class the test method is in.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="time" type="xs:decimal" use="required">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">Time taken (in seconds) to execute the test</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ <xs:attribute name="name" use="required">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">Full class name of the test for non-aggregated testsuite documents. Class name without the package for aggregated testsuites documents</xs:documentation>
+ </xs:annotation>
+ <xs:simpleType>
+ <xs:restriction base="xs:token">
+ <xs:minLength value="1"/>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+ <xs:attribute name="timestamp" type="ISO8601_DATETIME_PATTERN" use="required">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">Time that the last testcase was started. If no tests are started, this is the time the report was generated. Timezone must be specified as an offset from UTC.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="report_timestamp" type="ISO8601_DATETIME_PATTERN" use="required">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">Time that the report was generated.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="start_timestamp" type="ISO8601_DATETIME_PATTERN" use="required">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">Time that fstests was started.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="hostname" use="required">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">Host on which the tests were executed. 'localhost' should be used if the hostname cannot be determined.</xs:documentation>
+ </xs:annotation>
+ <xs:simpleType>
+ <xs:restriction base="xs:token">
+ <xs:minLength value="1"/>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+ <xs:attribute name="tests" type="xs:int" use="required">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">The total number of tests in the suite</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="failures" type="xs:int" use="required">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">The total number of tests in the suite that failed. A failure is a test which the code has explicitly failed by using the mechanisms for that purpose. e.g., via an assertEquals</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="skipped" type="xs:int" use="optional">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">The total number of ignored or skipped tests in the suite.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="time" type="xs:decimal" use="required">
+ <xs:annotation>
+ <xs:documentation xml:lang="en">Time taken (in seconds) to execute the tests in the suite</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ </xs:complexType>
+ <xs:simpleType name="pre-string">
+ <xs:restriction base="xs:string">
+ <xs:whiteSpace value="preserve"/>
+ </xs:restriction>
+ </xs:simpleType>
+</xs:schema>
HAVE_FALLOCATE = @have_fallocate@
HAVE_COPY_FILE_RANGE = @have_copy_file_range@
HAVE_LIBBTRFSUTIL = @have_libbtrfsutil@
+HAVE_SEEK_DATA = @have_seek_data@
+HAVE_NFTW = @have_nftw@
+HAVE_BMV_OF_SHARED = @have_bmv_of_shared@
+HAVE_RLIMIT_NOFILE = @have_rlimit_nofile@
+HAVE_XFS_IOC_EXCHANGE_RANGE = @have_xfs_ioc_exchange_range@
+HAVE_FICLONE = @have_ficlone@
GCCFLAGS = -funsigned-char -fno-strict-aliasing -Wall
m/^# SPDX/i and next; # SPDX tags
m/^# Copyright/i and next; # Copyright tags
m/^#\s*\-{10}/ and last; # dashed lines
- m/^seq/i and last; # test start
+ m/^\. \.\/common\/preamble/ and last; # test start
s/^# *//;
LCFLAGS += -DHAVE_COPY_FILE_RANGE
endif
+ifeq ($(HAVE_XFS_IOC_EXCHANGE_RANGE),yes)
+LCFLAGS += -DHAVE_XFS_IOC_EXCHANGE_RANGE
+endif
+
default: depend $(TARGETS)
depend: .dep
case 'C':
C_opt++;
+ tok = strtok(optarg, ",");
for(s=checkmap; s->string != NULL; s++)
if(!strcmp(s->string, optarg))
break;
OP_URING_WRITE,
OP_WRITE,
OP_WRITEV,
+ OP_XCHGRANGE,
OP_LAST
} opty_t;
void uring_write_f(opnum_t, long);
void write_f(opnum_t, long);
void writev_f(opnum_t, long);
+void xchgrange_f(opnum_t, long);
+
char *xattr_flag_to_string(int);
struct opdesc ops[OP_LAST] = {
[OP_URING_WRITE] = {"uring_write", uring_write_f, 1, 1 },
[OP_WRITE] = {"write", write_f, 4, 1 },
[OP_WRITEV] = {"writev", writev_f, 4, 1 },
+ [OP_XCHGRANGE] = {"xchgrange", xchgrange_f, 2, 1 },
}, *ops_end;
flist_t flist[FT_nft] = {
int execute_freq = 1;
struct print_string flag_str = {0};
+struct timespec deadline = { 0 };
+
void add_to_flist(int, int, int, int);
void append_pathname(pathname_t *, char *);
int attr_list_path(pathname_t *, char *, const int);
int truncate64_path(pathname_t *, off64_t);
int unlink_path(pathname_t *);
void usage(void);
+void read_freq(void);
void write_freq(void);
void zero_freq(void);
void non_btrfs_freq(const char *);
}
}
+bool
+keep_looping(int i, int loops)
+{
+ int ret;
+
+ if (deadline.tv_nsec) {
+ struct timespec now;
+
+ ret = clock_gettime(CLOCK_MONOTONIC, &now);
+ if (ret) {
+ perror("CLOCK_MONOTONIC");
+ return false;
+ }
+
+ return now.tv_sec <= deadline.tv_sec;
+ }
+
+ if (!loops)
+ return true;
+
+ return i < loops;
+}
+
+static struct option longopts[] = {
+ {"duration", optional_argument, 0, 256},
+ { }
+};
+
int main(int argc, char **argv)
{
char buf[10];
xfs_error_injection_t err_inj;
struct sigaction action;
int loops = 1;
- const char *allopts = "cd:e:f:i:l:m:M:n:o:p:rs:S:vVwx:X:zH";
+ const char *allopts = "cd:e:f:i:l:m:M:n:o:p:rRs:S:vVwx:X:zH";
+ long long duration;
errrange = errtag = 0;
umask(0);
nops = sizeof(ops) / sizeof(ops[0]);
ops_end = &ops[nops];
myprog = argv[0];
- while ((c = getopt(argc, argv, allopts)) != -1) {
+ while ((c = getopt_long(argc, argv, allopts, longopts, NULL)) != -1) {
switch (c) {
case 'c':
cleanup = 1;
case 'r':
namerand = 1;
break;
+ case 'R':
+ read_freq();
+ break;
case 's':
seed = strtoul(optarg, NULL, 0);
break;
case 'X':
execute_freq = strtoul(optarg, NULL, 0);
break;
+ case 256: /* --duration */
+ if (!optarg) {
+ fprintf(stderr, "Specify time with --duration=\n");
+ exit(87);
+ }
+ duration = strtoll(optarg, NULL, 0);
+ if (duration < 1) {
+ fprintf(stderr, "%lld: invalid duration\n", duration);
+ exit(88);
+ }
+
+ i = clock_gettime(CLOCK_MONOTONIC, &deadline);
+ if (i) {
+ perror("CLOCK_MONOTONIC");
+ exit(89);
+ }
+
+ deadline.tv_sec += duration;
+ deadline.tv_nsec = 1;
+ break;
case '?':
fprintf(stderr, "%s - invalid parameters\n",
myprog);
#endif
#ifdef URING
have_io_uring = true;
- /* If ENOSYS, just ignore uring, other errors are fatal. */
- if (io_uring_queue_init(URING_ENTRIES, &ring, 0)) {
- if (errno == ENOSYS) {
- have_io_uring = false;
- } else {
- fprintf(stderr, "io_uring_queue_init failed\n");
- exit(1);
- }
+ /*
+ * If ENOSYS, just ignore uring, due to kernel doesn't support it.
+ * If EPERM, maybe due to sysctl kernel.io_uring_disabled isn't 0,
+ * or some selinux policies, etc.
+ * Other errors are fatal.
+ */
+ c = io_uring_queue_init(URING_ENTRIES, &ring, 0);
+ switch(c){
+ case 0:
+ have_io_uring = true;
+ break;
+ case -ENOSYS:
+ have_io_uring = false;
+ if (verbose)
+ printf("io_uring isn't supported by kernel\n");
+ break;
+ case -EPERM:
+ have_io_uring = false;
+ if (verbose)
+ printf("io_uring isn't allowed, check io_uring_disabled sysctl or selinux policy\n");
+ break;
+ default:
+ fprintf(stderr, "io_uring_queue_init failed, errno=%d\n", -c);
+ exit(1);
}
#endif
- for (i = 0; !loops || (i < loops); i++)
+ for (i = 0; keep_looping(i, loops); i++)
doproc();
#ifdef AIO
if(io_destroy(io_ctx) != 0) {
return NULL;
}
+bool
+keep_running(opnum_t opno, opnum_t operations)
+{
+ int ret;
+
+ if (deadline.tv_nsec) {
+ struct timespec now;
+
+ ret = clock_gettime(CLOCK_MONOTONIC, &now);
+ if (ret) {
+ perror("CLOCK_MONOTONIC");
+ return false;
+ }
+
+ return now.tv_sec <= deadline.tv_sec;
+ }
+
+ return opno < operations;
+}
+
void
doproc(void)
{
srandom(seed);
if (namerand)
namerand = random();
- for (opno = 0; opno < operations; opno++) {
+ for (opno = 0; keep_running(opno, operations); opno++) {
if (execute_cmd && opno && opno % dividend == 0) {
if (verbose)
printf("%lld: execute command %s\n", opno,
printf(" -o logfile specifies logfile name\n");
printf(" -p nproc specifies the no. of processes (default 1)\n");
printf(" -r specifies random name padding\n");
+ printf(" -R zeros frequencies of write operations\n");
printf(" -s seed specifies the seed for the random generator (default random)\n");
printf(" -v specifies verbose mode\n");
printf(" -w zeros frequencies of non-write operations\n");
printf(" -V specifies verifiable logging mode (omitting inode numbers)\n");
printf(" -X ncmd number of calls to the -x command (default 1)\n");
printf(" -H prints usage and exits\n");
+ printf(" --duration=s ignore any -n setting and run for this many seconds\n");
+}
+
+void
+read_freq(void)
+{
+ opdesc_t *p;
+
+ for (p = ops; p < ops_end; p++) {
+ if (p->iswrite)
+ p->freq = 0;
+ }
}
void
(long long) s->st_blocks, (long long) s->st_size);
}
+#ifdef AIO
+static int io_get_single_event(struct io_event *event)
+{
+ int ret;
+
+ /*
+ * We can get -EINTR if competing with io_uring using signal
+ * based notifications. For that case, just retry the wait.
+ */
+ do {
+ ret = io_getevents(io_ctx, 1, 1, event, NULL);
+ } while (ret == -EINTR);
+ return ret;
+}
+#endif
+
void
afsync_f(opnum_t opno, long r)
{
close(fd);
return;
}
- if ((e = io_getevents(io_ctx, 1, 1, &event, NULL)) != 1) {
+ if ((e = io_get_single_event(&event)) != 1) {
if (v)
printf("%d/%lld: afsync - io_getevents failed %d\n",
procid, opno, e);
procid, opno, iswrite ? "awrite" : "aread", e);
goto aio_out;
}
- if ((e = io_getevents(io_ctx, 1, 1, &event, NULL)) != 1) {
+ if ((e = io_get_single_event(&event)) != 1) {
if (v)
printf("%d/%lld: %s - io_getevents failed %d\n",
procid, opno, iswrite ? "awrite" : "aread", e);
free_pathname(&f);
}
+/* exchange some arbitrary range of f1 to f2...fn. */
+void
+xchgrange_f(
+ opnum_t opno,
+ long r)
+{
+#ifdef XFS_IOC_EXCHANGE_RANGE
+ struct xfs_exch_range fxr = { 0 };
+ static __u64 swap_flags = 0;
+ struct pathname fpath1;
+ struct pathname fpath2;
+ struct stat64 stat1;
+ struct stat64 stat2;
+ char inoinfo1[1024];
+ char inoinfo2[1024];
+ off64_t lr;
+ off64_t off1;
+ off64_t off2;
+ off64_t max_off2;
+ size_t len;
+ int v1;
+ int v2;
+ int fd1;
+ int fd2;
+ int ret;
+ int tries = 0;
+ int e;
+
+ /* Load paths */
+ init_pathname(&fpath1);
+ if (!get_fname(FT_REGm, r, &fpath1, NULL, NULL, &v1)) {
+ if (v1)
+ printf("%d/%lld: xchgrange read - no filename\n",
+ procid, opno);
+ goto out_fpath1;
+ }
+
+ init_pathname(&fpath2);
+ if (!get_fname(FT_REGm, random(), &fpath2, NULL, NULL, &v2)) {
+ if (v2)
+ printf("%d/%lld: xchgrange write - no filename\n",
+ procid, opno);
+ goto out_fpath2;
+ }
+
+ /* Open files */
+ fd1 = open_path(&fpath1, O_RDONLY);
+ e = fd1 < 0 ? errno : 0;
+ check_cwd();
+ if (fd1 < 0) {
+ if (v1)
+ printf("%d/%lld: xchgrange read - open %s failed %d\n",
+ procid, opno, fpath1.path, e);
+ goto out_fpath2;
+ }
+
+ fd2 = open_path(&fpath2, O_WRONLY);
+ e = fd2 < 0 ? errno : 0;
+ check_cwd();
+ if (fd2 < 0) {
+ if (v2)
+ printf("%d/%lld: xchgrange write - open %s failed %d\n",
+ procid, opno, fpath2.path, e);
+ goto out_fd1;
+ }
+
+ /* Get file stats */
+ if (fstat64(fd1, &stat1) < 0) {
+ if (v1)
+ printf("%d/%lld: xchgrange read - fstat64 %s failed %d\n",
+ procid, opno, fpath1.path, errno);
+ goto out_fd2;
+ }
+ inode_info(inoinfo1, sizeof(inoinfo1), &stat1, v1);
+
+ if (fstat64(fd2, &stat2) < 0) {
+ if (v2)
+ printf("%d/%lld: xchgrange write - fstat64 %s failed %d\n",
+ procid, opno, fpath2.path, errno);
+ goto out_fd2;
+ }
+ inode_info(inoinfo2, sizeof(inoinfo2), &stat2, v2);
+
+ if (stat1.st_size < (stat1.st_blksize * 2) ||
+ stat2.st_size < (stat2.st_blksize * 2)) {
+ if (v2)
+ printf("%d/%lld: xchgrange - files are too small\n",
+ procid, opno);
+ goto out_fd2;
+ }
+
+ /* Never let us swap more than 1/4 of the files. */
+ len = (random() % FILELEN_MAX) + 1;
+ if (len > stat1.st_size / 4)
+ len = stat1.st_size / 4;
+ if (len > stat2.st_size / 4)
+ len = stat2.st_size / 4;
+ len = rounddown_64(len, stat1.st_blksize);
+ if (len == 0)
+ len = stat1.st_blksize;
+
+ /* Calculate offsets */
+ lr = ((int64_t)random() << 32) + random();
+ if (stat1.st_size == len)
+ off1 = 0;
+ else
+ off1 = (off64_t)(lr % MIN(stat1.st_size - len, MAXFSIZE));
+ off1 %= maxfsize;
+ off1 = rounddown_64(off1, stat1.st_blksize);
+
+ /*
+ * If srcfile == destfile, randomly generate destination ranges
+ * until we find one that doesn't overlap the source range.
+ */
+ max_off2 = MIN(stat2.st_size - len, MAXFSIZE);
+ do {
+ lr = ((int64_t)random() << 32) + random();
+ if (stat2.st_size == len)
+ off2 = 0;
+ else
+ off2 = (off64_t)(lr % max_off2);
+ off2 %= maxfsize;
+ off2 = rounddown_64(off2, stat2.st_blksize);
+ } while (stat1.st_ino == stat2.st_ino &&
+ llabs(off2 - off1) < len &&
+ tries++ < 10);
+
+ /* Swap data blocks */
+ fxr.file1_fd = fd1;
+ fxr.file1_offset = off1;
+ fxr.length = len;
+ fxr.file2_offset = off2;
+ fxr.flags = swap_flags;
+
+retry:
+ ret = ioctl(fd2, XFS_IOC_EXCHANGE_RANGE, &fxr);
+ e = ret < 0 ? errno : 0;
+ if (e == EOPNOTSUPP && !(swap_flags & XFS_EXCH_RANGE_NONATOMIC)) {
+ swap_flags = XFS_EXCH_RANGE_NONATOMIC;
+ fxr.flags |= swap_flags;
+ goto retry;
+ }
+ if (v1 || v2) {
+ printf("%d/%lld: xchgrange %s%s [%lld,%lld] -> %s%s [%lld,%lld]",
+ procid, opno,
+ fpath1.path, inoinfo1, (long long)off1, (long long)len,
+ fpath2.path, inoinfo2, (long long)off2, (long long)len);
+
+ if (ret < 0)
+ printf(" error %d", e);
+ printf("\n");
+ }
+
+out_fd2:
+ close(fd2);
+out_fd1:
+ close(fd1);
+out_fpath2:
+ free_pathname(&fpath2);
+out_fpath1:
+ free_pathname(&fpath1);
+#endif
+}
+
/* reflink some arbitrary range of f1 to f2. */
void
clonerange_f(
mode |= FALLOC_FL_KEEP_SIZE & random();
e = fallocate(fd, mode, (loff_t)off, (loff_t)len) < 0 ? errno : 0;
if (v)
- printf("%d/%lld: fallocate(%s) %s %st %lld %lld %d\n",
+ printf("%d/%lld: fallocate(%s) %s%s [%lld,%lld] %d\n",
procid, opno, translate_falloc_flags(mode),
f.path, st, (long long)off, (long long)len, e);
free_pathname(&f);
e = ioctl(fd, FS_IOC_FIEMAP, (unsigned long)fiemap);
if (v)
- printf("%d/%lld: ioctl(FIEMAP) %s%s %lld %lld (%s) %d\n",
+ printf("%d/%lld: ioctl(FIEMAP) %s%s [%lld,%lld,%s] %d\n",
procid, opno, f.path, st, (long long)fiemap->fm_start,
(long long) fiemap->fm_length,
translate_fiemap_flags(fiemap->fm_flags), e);
fl.l_len = (off64_t)(random() % (1024 * 1024));
e = xfsctl(f.path, fd, XFS_IOC_RESVSP64, &fl) < 0 ? errno : 0;
if (v)
- printf("%d/%lld: xfsctl(XFS_IOC_RESVSP64) %s%s %lld %lld %d\n",
+ printf("%d/%lld: xfsctl(XFS_IOC_RESVSP64) %s%s [%lld,%lld] %d\n",
procid, opno, f.path, st,
(long long)off, (long long)fl.l_len, e);
free_pathname(&f);
fl.l_len = (off64_t)(random() % (1 << 20));
e = xfsctl(f.path, fd, XFS_IOC_UNRESVSP64, &fl) < 0 ? errno : 0;
if (v)
- printf("%d/%lld: xfsctl(XFS_IOC_UNRESVSP64) %s%s %lld %lld %d\n",
+ printf("%d/%lld: xfsctl(XFS_IOC_UNRESVSP64) %s%s [%lld,%lld] %d\n",
procid, opno, f.path, st,
(long long)off, (long long)fl.l_len, e);
free_pathname(&f);
OP_CLONE_RANGE,
OP_DEDUPE_RANGE,
OP_COPY_RANGE,
+ OP_EXCHANGE_RANGE,
OP_MAX_FULL,
/* integrity operations */
int clone_range_calls = 1; /* -J flag disables */
int dedupe_range_calls = 1; /* -B flag disables */
int copy_range_calls = 1; /* -E flag disables */
+int xchg_range_calls = 1; /* -0 flag disables */
int integrity = 0; /* -i flag */
int fsxgoodfd = 0;
int o_direct; /* -Z */
#define fsxread(a,b,c,d) fsx_rw(READ, a,b,c,d)
#define fsxwrite(a,b,c,d) fsx_rw(WRITE, a,b,c,d)
+struct timespec deadline;
+
const char *replayops = NULL;
const char *recordops = NULL;
FILE * fsxlogf = NULL;
[OP_DEDUPE_RANGE] = "dedupe_range",
[OP_COPY_RANGE] = "copy_range",
[OP_FSYNC] = "fsync",
+ [OP_EXCHANGE_RANGE] = "xchg_range",
};
static const char *op_name(int operation)
if (overlap)
prt("\t******IIII");
break;
+ case OP_EXCHANGE_RANGE:
+ prt("XCHG 0x%x thru 0x%x\t(0x%x bytes) to 0x%x thru 0x%x",
+ lp->args[0], lp->args[0] + lp->args[1] - 1,
+ lp->args[1],
+ lp->args[2], lp->args[2] + lp->args[1] - 1);
+ overlap2 = badoff >= lp->args[2] &&
+ badoff < lp->args[2] + lp->args[1];
+ if (overlap && overlap2)
+ prt("\tXXXX**XXXX");
+ else if (overlap)
+ prt("\tXXXX******");
+ else if (overlap2)
+ prt("\t******XXXX");
+ break;
case OP_CLONE_RANGE:
prt("CLONE 0x%x thru 0x%x\t(0x%x bytes) to 0x%x thru 0x%x",
lp->args[0], lp->args[0] + lp->args[1] - 1,
if (memcmp(good_buf + offset, buf, size) != 0) {
prt("READ BAD DATA: offset = 0x%x, size = 0x%x, fname = %s\n",
offset, size, fname);
- prt("OFFSET\tGOOD\tBAD\tRANGE\n");
+ prt("%-10s %-6s %-6s %s\n", "OFFSET", "GOOD", "BAD", "RANGE");
while (size > 0) {
c = good_buf[offset];
t = buf[i];
if (c != t) {
if (n < 16) {
bad = short_at(&buf[i]);
- prt("0x%05x\t0x%04x\t0x%04x", offset,
- short_at(&good_buf[offset]), bad);
+ prt("0x%-8x 0x%04x 0x%04x 0x%x\n",
+ offset,
+ short_at(&good_buf[offset]), bad,
+ n);
op = buf[offset & 1 ? i+1 : i];
- prt("\t0x%05x\n", n);
if (op)
prt("operation# (mod 256) for "
"the bad data may be %u\n",
}
#endif
+#ifdef XFS_IOC_EXCHANGE_RANGE
+static __u64 swap_flags = 0;
+
+int
+test_xchg_range(void)
+{
+ struct xfs_exch_range fsr = {
+ .file1_fd = fd,
+ .flags = XFS_EXCH_RANGE_DRY_RUN | swap_flags,
+ };
+ int ret, e;
+
+retry:
+ ret = ioctl(fd, XFS_IOC_EXCHANGE_RANGE, &fsr);
+ e = ret < 0 ? errno : 0;
+ if (e == EOPNOTSUPP && !(swap_flags & XFS_EXCH_RANGE_NONATOMIC)) {
+ /*
+ * If the call fails with atomic mode, try again with non
+ * atomic mode.
+ */
+ swap_flags = XFS_EXCH_RANGE_NONATOMIC;
+ fsr.flags |= swap_flags;
+ goto retry;
+ }
+ if (e == EOPNOTSUPP || errno == ENOTTY) {
+ if (!quiet)
+ fprintf(stderr,
+ "main: filesystem does not support "
+ "exchange range, disabling!\n");
+ return 0;
+ }
+
+ return 1;
+}
+
+void
+do_xchg_range(unsigned offset, unsigned length, unsigned dest)
+{
+ struct xfs_exch_range fsr = {
+ .file1_fd = fd,
+ .file1_offset = offset,
+ .file2_offset = dest,
+ .length = length,
+ .flags = swap_flags,
+ };
+ void *p;
+
+ if (length == 0) {
+ if (!quiet && testcalls > simulatedopcount)
+ prt("skipping zero length exchange range\n");
+ log5(OP_EXCHANGE_RANGE, offset, length, dest, FL_SKIPPED);
+ return;
+ }
+
+ if ((loff_t)offset >= file_size || (loff_t)dest >= file_size) {
+ if (!quiet && testcalls > simulatedopcount)
+ prt("skipping exchange range behind EOF\n");
+ log5(OP_EXCHANGE_RANGE, offset, length, dest, FL_SKIPPED);
+ return;
+ }
+
+ p = malloc(length);
+ if (!p) {
+ if (!quiet && testcalls > simulatedopcount)
+ prt("skipping exchange range due to ENOMEM\n");
+ log5(OP_EXCHANGE_RANGE, offset, length, dest, FL_SKIPPED);
+ return;
+ }
+
+ log5(OP_EXCHANGE_RANGE, offset, length, dest, FL_NONE);
+
+ if (testcalls <= simulatedopcount)
+ goto out_free;
+
+ if ((progressinterval && testcalls % progressinterval == 0) ||
+ (debug && (monitorstart == -1 || monitorend == -1 ||
+ dest <= monitorstart || dest + length <= monitorend))) {
+ prt("%lu swap\tfrom 0x%x to 0x%x, (0x%x bytes) at 0x%x\n",
+ testcalls, offset, offset+length, length, dest);
+ }
+
+ if (ioctl(fd, XFS_IOC_EXCHANGE_RANGE, &fsr) == -1) {
+ prt("exchange range: 0x%x to 0x%x at 0x%x\n", offset,
+ offset + length, dest);
+ prterr("do_xchg_range: XFS_IOC_EXCHANGE_RANGE");
+ report_failure(161);
+ goto out_free;
+ }
+
+ memcpy(p, good_buf + offset, length);
+ memcpy(good_buf + offset, good_buf + dest, length);
+ memcpy(good_buf + dest, p, length);
+out_free:
+ free(p);
+}
+
+#else
+int
+test_xchg_range(void)
+{
+ return 0;
+}
+
+void
+do_xchg_range(unsigned offset, unsigned length, unsigned dest)
+{
+ return;
+}
+#endif
+
#ifdef FICLONERANGE
int
test_clone_range(void)
op_args_count(int operation)
{
switch (operation) {
+ case OP_EXCHANGE_RANGE:
case OP_CLONE_RANGE:
case OP_DEDUPE_RANGE:
case OP_COPY_RANGE:
case OP_COPY_RANGE:
generate_dest_range(true, maxfilelen, &offset, &size, &offset2);
break;
+ case OP_EXCHANGE_RANGE:
+ generate_dest_range(false, file_size, &offset, &size, &offset2);
+ break;
}
have_op:
goto out;
}
break;
+ case OP_EXCHANGE_RANGE:
+ if (!xchg_range_calls) {
+ log5(op, offset, size, offset2, FL_SKIPPED);
+ goto out;
+ }
+ break;
case OP_CLONE_RANGE:
if (!clone_range_calls) {
log5(op, offset, size, offset2, FL_SKIPPED);
do_insert_range(offset, size);
break;
+ case OP_EXCHANGE_RANGE:
+ if (size == 0) {
+ log5(OP_EXCHANGE_RANGE, offset, size, offset2, FL_SKIPPED);
+ goto out;
+ }
+ if (offset2 + size > maxfilelen) {
+ log5(OP_EXCHANGE_RANGE, offset, size, offset2, FL_SKIPPED);
+ goto out;
+ }
+
+ do_xchg_range(offset, size, offset2);
+ break;
case OP_CLONE_RANGE:
if (size == 0) {
log5(OP_CLONE_RANGE, offset, size, offset2, FL_SKIPPED);
usage(void)
{
fprintf(stdout, "usage: %s",
- "fsx [-dknqxBEFJLOWZ][-A|-U] [-b opnum] [-c Prob] [-g filldata] [-i logdev] [-j logid] [-l flen] [-m start:end] [-o oplen] [-p progressinterval] [-r readbdy] [-s style] [-t truncbdy] [-w writebdy] [-D startingop] [-N numops] [-P dirpath] [-S seed] fname\n\
+ "fsx [-dfknqxyzBEFHIJKLORWXZ0]\n\
+ [-b opnum] [-c Prob] [-g filldata] [-i logdev] [-j logid]\n\
+ [-l flen] [-m start:end] [-o oplen] [-p progressinterval]\n\
+ [-r readbdy] [-s style] [-t truncbdy] [-w writebdy]\n\
+ [-A|-U] [-D startingop] [-N numops] [-P dirpath] [-S seed]\n\
+ [--replay-ops=opsfile] [--record-ops[=opsfile]] [--duration=seconds]\n\
+ ... fname\n\
-b opnum: beginning operation number (default 1)\n\
-c P: 1 in P chance of file close+open at each op (default infinity)\n\
-d: debug output for all operations\n\
- -f flush and invalidate cache after I/O\n\
+ -f: flush and invalidate cache after I/O\n\
-g X: write character X instead of random generated data\n\
-i logdev: do integrity testing, logdev is the dm log writes device\n\
-j logid: prefix debug log messsages with this id\n\
-s style: 1 gives smaller truncates (default 0)\n\
-t truncbdy: 4096 would make truncates page aligned (default 1)\n\
-w writebdy: 4096 would make writes page aligned (default 1)\n\
- -x: preallocate file space before starting, XFS only (default 0)\n\
- -y synchronize changes to a file\n"
+ -x: preallocate file space before starting, XFS only\n\
+ -y: synchronize changes to a file\n"
#ifdef AIO
" -A: Use the AIO system calls, -A excludes -U\n"
#endif
#ifdef URING
" -U: Use the IO_URING system calls, -U excludes -A\n"
- #endif
+#endif
" -D startingop: debug output starting at specified operation\n"
#ifdef HAVE_LINUX_FALLOC_H
" -F: Do not use fallocate (preallocation) calls\n"
#ifdef HAVE_COPY_FILE_RANGE
" -E: Do not use copy range calls\n"
#endif
-" -L: fsxLite - no file creations & no file size changes\n\
+#ifdef XFS_IOC_EXCHANGE_RANGE
+" -0: Do not use exchange range calls\n"
+#endif
+" -K: Do not use keep size\n\
+ -L: fsxLite - no file creations & no file size changes\n\
-N numops: total # operations to do (default infinity)\n\
-O: use oplen (see -o flag) for every op (default random)\n\
- -P: save .fsxlog .fsxops and .fsxgood files in dirpath (default ./)\n\
+ -P dirpath: save .fsxlog .fsxops and .fsxgood files in dirpath (default ./)\n\
+ -R: read() system calls only (mapped reads disabled)\n\
-S seed: for random # generator (default 1) 0 gets timestamp\n\
-W: mapped write operations DISabled\n\
- -X: Read file and compare to good buffer after every operation.\n\
- -R: read() system calls only (mapped reads disabled)\n\
- -Z: O_DIRECT (use -R, -W, -r and -w too)\n\
- --replay-ops opsfile: replay ops from recorded .fsxops file\n\
+ -X: Read file and compare to good buffer after every operation\n\
+ -Z: O_DIRECT (use -R, -W, -r and -w too)\n\
+ --replay-ops=opsfile: replay ops from recorded .fsxops file\n\
--record-ops[=opsfile]: dump ops file also on success. optionally specify ops file name\n\
+ --duration=seconds: ignore any -N setting and run for this many seconds\n\
fname: this filename is REQUIRED (no default)\n");
exit(90);
}
return -1;
}
#else
+int
aio_rw(int rw, int fd, char *buf, unsigned len, unsigned offset)
{
fprintf(stderr, "io_rw: need AIO support!\n");
#endif
}
+bool
+keep_running(void)
+{
+ int ret;
+
+ if (deadline.tv_nsec) {
+ struct timespec now;
+
+ ret = clock_gettime(CLOCK_MONOTONIC, &now);
+ if (ret) {
+ perror("CLOCK_MONOTONIC");
+ return false;
+ }
+
+ return now.tv_sec <= deadline.tv_sec;
+ }
+
+ if (numops == -1)
+ return true;
+
+ return numops-- != 0;
+}
+
static struct option longopts[] = {
{"replay-ops", required_argument, 0, 256},
{"record-ops", optional_argument, 0, 255},
+ {"duration", optional_argument, 0, 254},
{ }
};
char logfile[PATH_MAX];
struct stat statbuf;
int o_flags = O_RDWR|O_CREAT|O_TRUNC;
+ long long duration;
logfile[0] = 0;
dname[0] = 0;
page_size = getpagesize();
page_mask = page_size - 1;
mmap_mask = page_mask;
-
setvbuf(stdout, (char *)0, _IOLBF, 0); /* line buffered stdout */
while ((ch = getopt_long(argc, argv,
- "b:c:dfg:i:j:kl:m:no:p:qr:s:t:w:xyABD:EFJKHzCILN:OP:RS:UWXZ",
+ "0b:c:dfg:i:j:kl:m:no:p:qr:s:t:w:xyABD:EFJKHzCILN:OP:RS:UWXZ",
longopts, NULL)) != EOF)
switch (ch) {
case 'b':
case 'I':
insert_range_calls = 0;
break;
+ case '0':
+ xchg_range_calls = 0;
+ break;
case 'J':
clone_range_calls = 0;
break;
o_direct = O_DIRECT;
o_flags |= O_DIRECT;
break;
+ case 254: /* --duration */
+ if (!optarg) {
+ fprintf(stderr, "Specify time with --duration=\n");
+ exit(87);
+ }
+ duration = strtoll(optarg, NULL, 0);
+ if (duration < 1) {
+ fprintf(stderr, "%lld: invalid duration\n", duration);
+ exit(88);
+ }
+
+ i = clock_gettime(CLOCK_MONOTONIC, &deadline);
+ if (i) {
+ perror("CLOCK_MONOTONIC");
+ exit(89);
+ }
+
+ deadline.tv_sec += duration;
+ deadline.tv_nsec = 1;
+ break;
case 255: /* --record-ops */
if (optarg)
snprintf(opsfile, sizeof(opsfile), "%s", optarg);
dedupe_range_calls = test_dedupe_range();
if (copy_range_calls)
copy_range_calls = test_copy_range();
+ if (xchg_range_calls)
+ xchg_range_calls = test_xchg_range();
- while (numops == -1 || numops--)
+ while (keep_running())
if (!test())
break;
+++ /dev/null
-#
-# Find format of installed man pages.
-# Always gzipped on Debian, but not Redhat pre-7.0.
-# We don't deal with bzip2'd man pages, which Mandrake uses,
-# someone will send us a patch sometime hopefully. :-)
-#
-AC_DEFUN([AC_MANUAL_FORMAT],
- [ have_zipped_manpages=false
- for d in ${prefix}/share/man ${prefix}/man ; do
- if test -f $d/man1/man.1.gz
- then
- have_zipped_manpages=true
- break
- fi
- done
- AC_SUBST(have_zipped_manpages)
- ])
-#
-# Check if we have a working fadvise system call
-#
-AC_DEFUN([AC_HAVE_FADVISE],
- [ AC_MSG_CHECKING([for fadvise ])
- AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+#
+#
+# Check if we have a copy_file_range system call (Linux)
+#
+AC_DEFUN([AC_HAVE_COPY_FILE_RANGE],
+ [ AC_MSG_CHECKING([for copy_file_range])
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[
#define _GNU_SOURCE
-#define _FILE_OFFSET_BITS 64
-#include <fcntl.h>
+#include <sys/syscall.h>
+#include <unistd.h>
]], [[
- posix_fadvise(0, 1, 0, POSIX_FADV_NORMAL);
- ]])],[have_fadvise=yes
- AC_MSG_RESULT(yes)],[AC_MSG_RESULT(no)])
- AC_SUBST(have_fadvise)
+ syscall(__NR_copy_file_range, 0, 0, 0, 0, 0, 0);
+ ]])],[have_copy_file_range=yes
+ AC_MSG_RESULT(yes)],[AC_MSG_RESULT(no)])
+ AC_SUBST(have_copy_file_range)
])
-#
-# Check if we have a working madvise system call
-#
-AC_DEFUN([AC_HAVE_MADVISE],
- [ AC_MSG_CHECKING([for madvise ])
- AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+# Check if we have SEEK_DATA
+AC_DEFUN([AC_HAVE_SEEK_DATA],
+ [ AC_MSG_CHECKING([for SEEK_DATA])
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[
#define _GNU_SOURCE
-#define _FILE_OFFSET_BITS 64
-#include <sys/mman.h>
+#include <sys/types.h>
+#include <unistd.h>
]], [[
- posix_madvise(0, 0, MADV_NORMAL);
- ]])],[have_madvise=yes
- AC_MSG_RESULT(yes)],[AC_MSG_RESULT(no)])
- AC_SUBST(have_madvise)
+ lseek(-1, 0, SEEK_DATA);
+ ]])],[have_seek_data=yes
+ AC_MSG_RESULT(yes)],[AC_MSG_RESULT(no)])
+ AC_SUBST(have_seek_data)
])
-#
-# Check if we have a working mincore system call
-#
-AC_DEFUN([AC_HAVE_MINCORE],
- [ AC_MSG_CHECKING([for mincore ])
- AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+# Check if we have nftw
+AC_DEFUN([AC_HAVE_NFTW],
+ [ AC_MSG_CHECKING([for nftw])
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[
#define _GNU_SOURCE
-#define _FILE_OFFSET_BITS 64
-#include <sys/mman.h>
+#include <stddef.h>
+#include <ftw.h>
]], [[
- mincore(0, 0, 0);
- ]])],[have_mincore=yes
- AC_MSG_RESULT(yes)],[AC_MSG_RESULT(no)])
- AC_SUBST(have_mincore)
+ nftw("/", (int (*)(const char *, const struct stat *, int, struct FTW *))1, 0, FTW_ACTIONRETVAL);
+ ]])],[have_nftw=yes
+ AC_MSG_RESULT(yes)],[AC_MSG_RESULT(no)])
+ AC_SUBST(have_nftw)
])
-#
-# Check if we have a working sendfile system call
-#
-AC_DEFUN([AC_HAVE_SENDFILE],
- [ AC_MSG_CHECKING([for sendfile ])
- AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+# Check if we have RLIMIT_NOFILE
+AC_DEFUN([AC_HAVE_RLIMIT_NOFILE],
+ [ AC_MSG_CHECKING([for RLIMIT_NOFILE])
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[
#define _GNU_SOURCE
-#define _FILE_OFFSET_BITS 64
-#include <sys/sendfile.h>
- ]], [[
- sendfile(0, 0, 0, 0);
- ]])],[have_sendfile=yes
- AC_MSG_RESULT(yes)],[AC_MSG_RESULT(no)])
- AC_SUBST(have_sendfile)
- ])
-
-#
-# Check if we have a getmntent libc call (Linux)
-#
-AC_DEFUN([AC_HAVE_GETMNTENT],
- [ AC_MSG_CHECKING([for getmntent ])
- AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
-#include <stdio.h>
-#include <mntent.h>
+#include <sys/time.h>
+#include <sys/resource.h>
]], [[
- getmntent(0);
- ]])],[have_getmntent=yes
- AC_MSG_RESULT(yes)],[AC_MSG_RESULT(no)])
- AC_SUBST(have_getmntent)
- ])
+ struct rlimit rlimit;
-#
-# Check if we have a getmntinfo libc call (FreeBSD, Mac OS X)
-#
-AC_DEFUN([AC_HAVE_GETMNTINFO],
- [ AC_MSG_CHECKING([for getmntinfo ])
- AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
-#include <sys/param.h>
-#include <sys/ucred.h>
-#include <sys/mount.h>
- ]], [[
- getmntinfo(0, 0);
- ]])],[have_getmntinfo=yes
+ rlimit.rlim_cur = 0;
+ getrlimit(RLIMIT_NOFILE, &rlimit);
+ ]])],[have_rlimit_nofile=yes
AC_MSG_RESULT(yes)],[AC_MSG_RESULT(no)])
- AC_SUBST(have_getmntinfo)
+ AC_SUBST(have_rlimit_nofile)
])
-#
-#
-# Check if we have a copy_file_range system call (Linux)
-#
-AC_DEFUN([AC_HAVE_COPY_FILE_RANGE],
- [ AC_MSG_CHECKING([for copy_file_range])
+# Check if we have FICLONE
+AC_DEFUN([AC_HAVE_FICLONE],
+ [ AC_MSG_CHECKING([for FICLONE])
AC_LINK_IFELSE([AC_LANG_PROGRAM([[
-#define _GNU_SOURCE
-#include <sys/syscall.h>
-#include <unistd.h>
+#include <sys/ioctl.h>
+#include <linux/fs.h>
]], [[
- syscall(__NR_copy_file_range, 0, 0, 0, 0, 0, 0);
- ]])],[have_copy_file_range=yes
+ ioctl(-1, FICLONE, -1);
+ ]])],[have_ficlone=yes
AC_MSG_RESULT(yes)],[AC_MSG_RESULT(no)])
- AC_SUBST(have_copy_file_range)
+ AC_SUBST(have_ficlone)
])
-
+++ /dev/null
-AC_DEFUN([AC_PACKAGE_NEED_NCURSES_H],
- [ AC_CHECK_HEADERS([ncurses.h])
- if test "$ac_cv_header_ncurses_h" != yes; then
- echo
- echo 'FATAL ERROR: could not find a valid ncurses header.'
- echo 'Install the ncurses development package.'
- exit 1
- fi
- ])
-
-AC_DEFUN([AC_PACKAGE_WANT_WORKING_LIBNCURSES],
- [ AC_CHECK_LIB(ncurses, initscr,, [
- echo
- echo 'FATAL ERROR: could not find a valid ncurses library.'
- echo 'Install the ncurses library package.'
- exit 1
- ])
- AC_MSG_CHECKING([for bad glibc/ncurses header interaction])
- libcurses="-lncurses"
- LIBS="$LIBS $libcurses"
- CFLAGS="$CFLAGS -D_GNU_SOURCE"
- AC_LINK_IFELSE([AC_LANG_PROGRAM([[
-#include <ncurses.h>
-#include <signal.h>]], [[wgetch(stdscr);]])],[enable_curses=yes; AC_MSG_RESULT([ok])],[enable_curses=no; libcurses=""; AC_MSG_RESULT([disabling curses])])
- AC_SUBST(enable_curses)
- AC_SUBST(libcurses)
- ])
+++ /dev/null
-AC_DEFUN([AC_PACKAGE_NEED_PTHREAD_H],
- [ AC_CHECK_HEADERS(pthread.h)
- if test $ac_cv_header_pthread_h = no; then
- AC_CHECK_HEADERS(pthread.h,, [
- echo
- echo 'FATAL ERROR: could not find a valid pthread header.'
- exit 1])
- fi
- ])
-
-AC_DEFUN([AC_PACKAGE_NEED_PTHREADMUTEXINIT],
- [ AC_CHECK_LIB(pthread, pthread_mutex_init,, [
- echo
- echo 'FATAL ERROR: could not find a valid pthread library.'
- exit 1
- ])
- libpthread=-lpthread
- AC_SUBST(libpthread)
- ])
+++ /dev/null
-#
-# Check if we have a type for the pointer's size integer (__psint_t)
-#
-AC_DEFUN([AC_TYPE_PSINT],
- [ AC_MSG_CHECKING([for __psint_t ])
- AC_TRY_COMPILE([
-#include <sys/types.h>
-#include <stdlib.h>
-#include <stddef.h>
- ], [
- __psint_t psint;
- ], AC_DEFINE(HAVE___PSINT_T) AC_MSG_RESULT(yes) , AC_MSG_RESULT(no))
- ])
-
-#
-# Check if we have a type for the pointer's size unsigned (__psunsigned_t)
-#
-AC_DEFUN([AC_TYPE_PSUNSIGNED],
- [ AC_MSG_CHECKING([for __psunsigned_t ])
- AC_TRY_COMPILE([
-#include <sys/types.h>
-#include <stdlib.h>
-#include <stddef.h>
- ], [
- __psunsigned_t psuint;
- ], AC_DEFINE(HAVE___PSUNSIGNED_T) AC_MSG_RESULT(yes) , AC_MSG_RESULT(no))
- ])
-
-#
-# Check type sizes
-#
-AC_DEFUN([AC_SIZEOF_POINTERS_AND_LONG],
- [ if test "$cross_compiling" = yes -a -z "$ac_cv_sizeof_long"; then
- AC_MSG_WARN([Cross compiling; assuming 32bit long and 32bit pointers])
- fi
- AC_CHECK_SIZEOF(long, 4)
- AC_CHECK_SIZEOF(char *, 4)
- if test $ac_cv_sizeof_long -eq 4 -o $ac_cv_sizeof_long -eq 0; then
- AC_DEFINE(HAVE_32BIT_LONG)
- fi
- if test $ac_cv_sizeof_long -eq 8; then
- AC_DEFINE(HAVE_64BIT_LONG)
- fi
- if test $ac_cv_sizeof_char_p -eq 4 -o $ac_cv_sizeof_char_p -eq 0; then
- AC_DEFINE(HAVE_32BIT_PTR)
- fi
- if test $ac_cv_sizeof_char_p -eq 8; then
- AC_DEFINE(HAVE_64BIT_PTR)
- fi
- ])
fi
])
-AC_DEFUN([AC_PACKAGE_NEED_LIBXFSINIT_LIBXFS],
- [ AC_CHECK_LIB(xfs, libxfs_init,, [
- echo
- echo 'FATAL ERROR: could not find a valid XFS base library.'
- echo 'Install or upgrade the XFS library package.'
- echo 'Alternatively, run "make install-dev" from the xfsprogs source.'
- exit 1
- ])
- libxfs="-lxfs"
- test -f ${libexecdir}${libdirsuffix}/libxfs.la && \
- libxfs="${libexecdir}${libdirsuffix}/libxfs.la"
- AC_SUBST(libxfs)
- ])
-
-AC_DEFUN([AC_PACKAGE_NEED_OPEN_BY_FSHANDLE],
- [ AC_CHECK_LIB(handle, open_by_fshandle,, [
- echo
- echo 'FATAL ERROR: could not find a current XFS handle library.'
- echo 'Install or upgrade the XFS library package.'
- echo 'Alternatively, run "make install-dev" from the xfsprogs source.'
- exit 1
- ])
- libhdl="-lhandle"
- test -f ${libexecdir}${libdirsuffix}/libhandle.la && \
- libhdl="${libexecdir}${libdirsuffix}/libhandle.la"
- AC_SUBST(libhdl)
- ])
-
AC_DEFUN([AC_PACKAGE_NEED_ATTRLIST_LIBHANDLE],
[ AC_CHECK_LIB(handle, attr_list_by_handle,, [
echo
exit 1
])
])
+
+# Check if we have BMV_OF_SHARED from the GETBMAPX ioctl
+AC_DEFUN([AC_HAVE_BMV_OF_SHARED],
+ [ AC_MSG_CHECKING([for BMV_OF_SHARED])
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[
+#define _GNU_SOURCE
+#include <xfs/xfs.h>
+ ]], [[
+ struct getbmapx obj;
+ ioctl(-1, XFS_IOC_GETBMAPX, &obj);
+ obj.bmv_oflags |= BMV_OF_SHARED;
+ ]])],[have_bmv_of_shared=yes
+ AC_MSG_RESULT(yes)],[AC_MSG_RESULT(no)])
+ AC_SUBST(have_bmv_of_shared)
+ ])
+
+# Check if we have XFS_IOC_EXCHANGE_RANGE
+AC_DEFUN([AC_HAVE_XFS_IOC_EXCHANGE_RANGE],
+ [ AC_MSG_CHECKING([for XFS_IOC_EXCHANGE_RANGE])
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[
+#define _GNU_SOURCE
+#include <xfs/xfs.h>
+ ]], [[
+ struct xfs_exch_range obj;
+ ioctl(-1, XFS_IOC_EXCHANGE_RANGE, &obj);
+ ]])],[have_xfs_ioc_exchange_range=yes
+ AC_MSG_RESULT(yes)],[AC_MSG_RESULT(no)])
+ AC_SUBST(have_xfs_ioc_exchange_range)
+ ])
+++ /dev/null
-AC_DEFUN([AC_FUNC_GCC_VISIBILITY],
- [AC_CACHE_CHECK(whether __attribute__((visibility())) is supported,
- libc_cv_visibility_attribute,
- [cat > conftest.c <<EOF
- int foo __attribute__ ((visibility ("hidden"))) = 1;
- int bar __attribute__ ((visibility ("protected"))) = 1;
-EOF
- libc_cv_visibility_attribute=no
- if ${CC-cc} -Werror -S conftest.c -o conftest.s \
- >/dev/null 2>&1; then
- if grep '\.hidden.*foo' conftest.s >/dev/null; then
- if grep '\.protected.*bar' conftest.s >/dev/null; then
- libc_cv_visibility_attribute=yes
- fi
- fi
- fi
- rm -f conftest.[cs]
- ])
- if test $libc_cv_visibility_attribute = yes; then
- AC_DEFINE(HAVE_VISIBILITY_ATTRIBUTE, [], [GCC supports visibility attributes])
- fi
- ])
t_ext4_dax_journal_corruption t_ext4_dax_inline_corruption \
t_ofd_locks t_mmap_collision mmap-write-concurrent \
t_get_file_time t_create_short_dirs t_create_long_dirs t_enospc \
- t_mmap_writev_overlap checkpoint_journal mmap-rw-fault allocstale
+ t_mmap_writev_overlap checkpoint_journal mmap-rw-fault allocstale \
+ t_mmap_cow_memory_failure fake-dump-rootino dio-buf-fault rewinddir-test \
+ readdir-while-renames
LINUX_TARGETS = xfsctl bstat t_mtab getdevicesize preallo_rw_pattern_reader \
preallo_rw_pattern_writer ftrunc trunc fs_perms testx looptest \
dio-invalidate-cache stat_test t_encrypted_d_revalidate \
attr_replace_test swapon mkswap t_attr_corruption t_open_tmpfiles \
fscrypt-crypt-util bulkstat_null_ocount splice-test chprojid_fail \
- detached_mounts_propagation ext4_resize t_readdir_3 splice2pipe
+ detached_mounts_propagation ext4_resize t_readdir_3 splice2pipe \
+ uuid_ioctl t_snapshot_deleted_subvolume fiemap-fault
EXTRA_EXECS = dmerror fill2attr fill2fs fill2fs_check scaleread.sh \
- btrfs_crc32c_forged_name.py
+ btrfs_crc32c_forged_name.py popdir.pl popattr.py \
+ soak_duration.awk
SUBDIRS = log-writes perf
-LLDLIBS = $(LIBHANDLE) $(LIBACL) -lpthread -lrt
+LLDLIBS = $(LIBHANDLE) $(LIBACL) -lpthread -lrt -luuid
ifeq ($(HAVE_XLOG_ASSIGN_LSN), true)
LINUX_TARGETS += loggen
LLDLIBS += -lcap
endif
+ifeq ($(HAVE_SEEK_DATA), yes)
+ ifeq ($(HAVE_NFTW), yes)
+ ifeq ($(HAVE_BMV_OF_SHARED), yes)
+ ifeq ($(HAVE_RLIMIT_NOFILE), yes)
+ TARGETS += xfsfind
+ endif
+ endif
+ endif
+endif
+
+ifeq ($(HAVE_FICLONE),yes)
+ TARGETS += t_reflink_read_race
+endif
+
+ifeq ($(HAVE_XFS_IOC_EXCHANGE_RANGE),yes)
+LCFLAGS += -DHAVE_XFS_IOC_EXCHANGE_RANGE
+endif
+
CFILES = $(TARGETS:=.c)
LDIRT = $(TARGETS) fssum
#include <sys/socket.h>
#include <sys/un.h>
-#define offsetof(TYPE, MEMBER) ((size_t)&((TYPE *)0)->MEMBER)
-
int main(int argc, char *argv[])
{
struct sockaddr_un sun;
}
for (j = 0; j < rdata.read_sz; j++) {
- if (rdata.buf[j] != 'a') {
+ if (rdata.buf[j] != 'a' && rdata.buf[j] != 0) {
fail("encounter an error: "
"block %d offset %d, content %x\n",
i, j, rbuf[j]);
void usage(char *progname)
{
- fprintf(stderr, "usage: %s [-t truncsize ] <-a size=N,off=M [-a ...]> filename\n"
+ fprintf(stderr, "usage: %s [-t truncsize ] <-a size=N,off=M [-a ...]> [-S] [-N] filename\n"
"\t-t truncsize: truncate the file to a special size before AIO wirte\n"
"\t-a: specify once AIO write size and startoff, this option can be specified many times, but less than 128\n"
"\t\tsize=N: AIO write size\n"
"\t\toff=M: AIO write startoff\n"
- "e.g: %s -t 4608 -a size=4096,off=512 -a size=4096,off=4608 filename\n",
- progname, progname);
+ "\t-S: uses O_SYNC flag for open. By default O_SYNC is not used\n"
+ "\t-N: no_verify: means no write verification. By default noverify is false\n"
+ "e.g: %s -t 4608 -a size=4096,off=512 -a size=4096,off=4608 filename\n"
+ "e.g: %s -t 1048576 -a size=1048576 -S -N filename\n",
+ progname, progname, progname);
exit(1);
}
return 1;
}
+ for (i = 0; i < num_events; i++) {
+ int err = (int)evs[i].res;
+
+ if (err < 0) {
+ fprintf(stderr, "error %s with event %d\n",
+ strerror(err), i);
+ return 1;
+ }
+ }
+
/* Try to destroy at here, not necessary, so don't check result */
io_destroy(ctx);
perror("pread");
return 1;
} else if (sret != p->param->buf_size) {
- fprintf(stderr, "short read %zd was less than %zu\n",
- sret, p->param->buf_size);
+ fprintf(stderr, "short read %zd was less than %zu at %zu\n",
+ sret, p->param->buf_size, p->param->offset);
return 1;
}
if (memcmp(p->param->buf,
p->param->cmp_buf, p->param->buf_size)) {
- printf("Find corruption\n");
+ printf("Find corruption at %zu length %zu\n", p->param->offset,
+ p->param->buf_size);
dump_buffer(p->param->buf, p->param->offset,
p->param->buf_size);
corrupted++;
char *filename = NULL;
int num_events = 0;
off_t tsize = 0;
+ int o_sync = 0;
+ int no_verify = 0;
- while ((c = getopt(argc, argv, "a:t:")) != -1) {
+ while ((c = getopt(argc, argv, "a:t:SN")) != -1) {
char *endp;
switch (c) {
case 't':
tsize = strtoul(optarg, &endp, 0);
break;
+ case 'S':
+ o_sync = O_SYNC;
+ break;
+ case 'N':
+ no_verify = 1;
+ break;
default:
usage(argv[0]);
}
else
usage(argv[0]);
- fd = open(filename, O_DIRECT | O_CREAT | O_TRUNC | O_RDWR, 0600);
+ fd = open(filename, O_DIRECT | O_CREAT | O_TRUNC | O_RDWR | o_sync, 0600);
if (fd == -1) {
perror("open");
return 1;
return 1;
}
- if (io_verify(fd) != 0) {
- fprintf(stderr, "Data verification fails\n");
- return 1;
+ if (no_verify == 0) {
+ if (io_verify(fd) != 0) {
+ fprintf(stderr, "Data verification fails\n");
+ return 1;
+ }
}
close(fd);
}
strncpy(filename, argv[argc-1], PATH_MAX);
+ filename[PATH_MAX - 1] = '\0';
if (alignment == 0)
alignment = get_logical_block_size(filename);
fprintf(stderr, __VA_ARGS__); exit (1); \
} while (0)
+void usage(char *progname)
+{
+ fail("usage: %s [-m max_attr_size] <file>\n", progname);
+}
+
int main(int argc, char *argv[])
{
int ret;
int fd;
+ int c;
char *path;
char *name = "user.world";
char *value;
struct stat sbuf;
size_t size = sizeof(value);
+ size_t maxsize = XATTR_SIZE_MAX;
+
+ while ((c = getopt(argc, argv, "m:")) != -1) {
+ char *endp;
+
+ switch (c) {
+ case 'm':
+ maxsize = strtoul(optarg, &endp, 0);
+ if (*endp || (maxsize > XATTR_SIZE_MAX))
+ fail("Invalid 'max_attr_size' value\n");
+ break;
+ default:
+ usage(argv[0]);
+ }
+ }
- if (argc != 2)
- fail("Usage: %s <file>\n", argv[0]);
- path = argv[1];
+ if (optind == argc - 1)
+ path = argv[optind];
+ else
+ usage(argv[0]);
fd = open(path, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
if (fd < 0) die();
size = sbuf.st_blksize * 3 / 4;
if (!size)
fail("Invalid st_blksize(%ld)\n", sbuf.st_blksize);
- size = MIN(size, XATTR_SIZE_MAX);
+ size = MIN(size, maxsize);
value = malloc(size);
if (!value)
fail("Failed to allocate memory\n");
# deduce the 4 bytes we need to insert
for c in struct.pack('<L',fwd_crc)[::-1]:
bkd_crc = ((bkd_crc << 8) & 0xffffffff) ^ self.reverse[bkd_crc >> 24]
- bkd_crc ^= ord(c)
+ bkd_crc ^= c
- res = s[:pos] + struct.pack('<L', bkd_crc) + s[pos:]
+ res = bytes(s[:pos], 'ascii') + struct.pack('<L', bkd_crc) + \
+ bytes(s[pos:], 'ascii')
return res
def parse_args(self):
help="number of forged names to create")
return parser.parse_args()
+def has_invalid_chars(result: bytes):
+ for i in result:
+ if i == 0 or i == int.from_bytes(b'/', byteorder="little"):
+ return True
+ return False
+
if __name__=='__main__':
crc = CRC32()
args = crc.parse_args()
dirpath=args.dir
while count < args.count :
- origname = os.urandom (89).encode ("hex")[:-1].strip ("\x00")
+ origname = os.urandom (89).hex()[:-1].strip ("\x00")
forgename = crc.forge(wanted_crc, origname, 4)
- if ("/" not in forgename) and ("\x00" not in forgename):
+ if not has_invalid_chars(forgename):
srcpath=dirpath + '/' + str(count)
- dstpath=dirpath + '/' + forgename
- file (srcpath, 'a').close()
+ # We have to convert all strings to bytes to concatenate the forged
+ # name (bytes).
+ # Thankfully os.rename() can accept bytes directly.
+ dstpath=bytes(dirpath, "ascii") + bytes('/', "ascii") + forgename
+ open(srcpath, mode="a").close()
os.rename(srcpath, dstpath)
os.system('btrfs fi sync %s' % (dirpath))
count+=1;
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
if (ret < 0)
exit_log("%m - Failed to create new mount namespace");
- ret = mount(NULL, base_dir, NULL, MS_REC | MS_SHARED, NULL);
+ ret = sys_mount(NULL, base_dir, NULL, MS_REC | MS_SHARED, NULL);
if (ret < 0)
exit_log("%m - Failed to make base_dir shared mountpoint");
}
close(fd_tree);
- ret = umount2(target, MNT_DETACH);
+ ret = sys_umount2(target, MNT_DETACH);
if (ret < 0) {
fprintf(stderr, "%m - Failed to unmount %s", target);
exit_code = EXIT_FAILURE;
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2023 Meta Platforms, Inc. All Rights Reserved.
+ */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE /* to get definition of O_DIRECT flag. */
+#endif
+
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+
+/*
+ * mmap a source file, then do a direct write of that mmapped region to a
+ * destination file.
+ */
+
+int prep_mmap_buffer(char *src_filename, void **addr)
+{
+ struct stat st;
+ int fd;
+ int ret;
+
+ fd = open(src_filename, O_RDWR, 0666);
+ if (fd == -1)
+ err(1, "failed to open %s", src_filename);
+
+ ret = fstat(fd, &st);
+ if (ret)
+ err(1, "failed to stat %d", fd);
+
+ *addr = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (*addr == MAP_FAILED)
+ err(1, "failed to mmap %d", fd);
+
+ return st.st_size;
+}
+
+int do_dio(char *dst_filename, void *buf, size_t sz)
+{
+ int fd;
+ ssize_t ret;
+
+ fd = open(dst_filename, O_CREAT | O_TRUNC | O_WRONLY | O_DIRECT, 0666);
+ if (fd == -1)
+ err(1, "failed to open %s", dst_filename);
+ while (sz) {
+ ret = write(fd, buf, sz);
+ if (ret < 0) {
+ if (errno == -EINTR)
+ continue;
+ else
+ err(1, "failed to write %lu bytes to %d", sz, fd);
+ } else if (ret == 0) {
+ break;
+ }
+ buf += ret;
+ sz -= ret;
+ }
+ return sz;
+}
+
+int main(int argc, char *argv[]) {
+ size_t sz;
+ void *buf = NULL;
+ char c;
+
+ if (argc != 3)
+ errx(1, "no in and out file name arguments given");
+ sz = prep_mmap_buffer(argv[1], &buf);
+
+ /* touch the first page of the mapping to bring it into cache */
+ c = ((char *)buf)[0];
+ printf("%u\n", c);
+
+ do_dio(argv[2], buf, sz);
+}
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mount.h>
+#include <linux/types.h>
-typedef unsigned long long __u64;
+#ifdef HAVE_LINUX_EXT4_H
+#include <linux/ext4.h>
+#endif
#ifndef EXT4_IOC_RESIZE_FS
#define EXT4_IOC_RESIZE_FS _IOW('f', 16, __u64)
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2022 Fujitsu Limited. All Rights Reserved. */
+#define _LARGEFILE64_SOURCE
+#include <endian.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <uuid/uuid.h>
+
+// Definitions from xfsdump
+#define PGSZLOG2 12
+#define PGSZ (1 << PGSZLOG2)
+#define GLOBAL_HDR_SZ PGSZ
+#define GLOBAL_HDR_MAGIC_SZ 8
+#define GLOBAL_HDR_STRING_SZ 0x100
+#define GLOBAL_HDR_TIME_SZ 4
+#define GLOBAL_HDR_UUID_SZ 0x10
+typedef int32_t time32_t;
+struct global_hdr {
+ char gh_magic[GLOBAL_HDR_MAGIC_SZ]; /* 8 8 */
+ /* unique signature of xfsdump */
+ uint32_t gh_version; /* 4 c */
+ /* header version */
+ uint32_t gh_checksum; /* 4 10 */
+ /* 32-bit unsigned additive inverse of entire header */
+ time32_t gh_timestamp; /* 4 14 */
+ /* time32_t of dump */
+ char gh_pad1[4]; /* 4 18 */
+ /* alignment */
+ uint64_t gh_ipaddr; /* 8 20 */
+ /* from gethostid(2), room for expansion */
+ uuid_t gh_dumpid; /* 10 30 */
+ /* ID of dump session */
+ char gh_pad2[0xd0]; /* d0 100 */
+ /* alignment */
+ char gh_hostname[GLOBAL_HDR_STRING_SZ]; /* 100 200 */
+ /* from gethostname(2) */
+ char gh_dumplabel[GLOBAL_HDR_STRING_SZ]; /* 100 300 */
+ /* label of dump session */
+ char gh_pad3[0x100]; /* 100 400 */
+ /* padding */
+ char gh_upper[GLOBAL_HDR_SZ - 0x400]; /* c00 1000 */
+ /* header info private to upper software layers */
+};
+typedef struct global_hdr global_hdr_t;
+
+#define sizeofmember( t, m ) sizeof( ( ( t * )0 )->m )
+
+#define DRIVE_HDR_SZ sizeofmember(global_hdr_t, gh_upper)
+struct drive_hdr {
+ uint32_t dh_drivecnt; /* 4 4 */
+ /* number of drives used to dump the fs */
+ uint32_t dh_driveix; /* 4 8 */
+ /* 0-based index of the drive used to dump this stream */
+ int32_t dh_strategyid; /* 4 c */
+ /* ID of the drive strategy used to produce this dump */
+ char dh_pad1[0x1f4]; /* 1f4 200 */
+ /* padding */
+ char dh_specific[0x200]; /* 200 400 */
+ /* drive strategy-specific info */
+ char dh_upper[DRIVE_HDR_SZ - 0x400]; /* 800 c00 */
+ /* header info private to upper software layers */
+};
+typedef struct drive_hdr drive_hdr_t;
+
+#define MEDIA_HDR_SZ sizeofmember(drive_hdr_t, dh_upper)
+struct media_hdr {
+ char mh_medialabel[GLOBAL_HDR_STRING_SZ]; /* 100 100 */
+ /* label of media object containing file */
+ char mh_prevmedialabel[GLOBAL_HDR_STRING_SZ]; /* 100 200 */
+ /* label of upstream media object */
+ char mh_pad1[GLOBAL_HDR_STRING_SZ]; /* 100 300 */
+ /* in case more labels needed */
+ uuid_t mh_mediaid; /* 10 310 */
+ /* ID of media object */
+ uuid_t mh_prevmediaid; /* 10 320 */
+ /* ID of upstream media object */
+ char mh_pad2[GLOBAL_HDR_UUID_SZ]; /* 10 330 */
+ /* in case more IDs needed */
+ uint32_t mh_mediaix; /* 4 334 */
+ /* 0-based index of this media object within the dump stream */
+ uint32_t mh_mediafileix; /* 4 338 */
+ /* 0-based index of this file within this media object */
+ uint32_t mh_dumpfileix; /* 4 33c */
+ /* 0-based index of this file within this dump stream */
+ uint32_t mh_dumpmediafileix; /* 4 340 */
+ /* 0-based index of file within dump stream and media object */
+ uint32_t mh_dumpmediaix; /* 4 344 */
+ /* 0-based index of this dump within the media object */
+ int32_t mh_strategyid; /* 4 348 */
+ /* ID of the media strategy used to produce this dump */
+ char mh_pad3[0x38]; /* 38 380 */
+ /* padding */
+ char mh_specific[0x80]; /* 80 400 */
+ /* media strategy-specific info */
+ char mh_upper[MEDIA_HDR_SZ - 0x400]; /* 400 800 */
+ /* header info private to upper software layers */
+};
+typedef struct media_hdr media_hdr_t;
+
+#define CONTENT_HDR_SZ sizeofmember(media_hdr_t, mh_upper)
+#define CONTENT_HDR_FSTYPE_SZ 16
+#define CONTENT_STATSZ 160 /* must match dlog.h DLOG_MULTI_STATSZ */
+struct content_hdr {
+ char ch_mntpnt[GLOBAL_HDR_STRING_SZ]; /* 100 100 */
+ /* full pathname of fs mount point */
+ char ch_fsdevice[GLOBAL_HDR_STRING_SZ]; /* 100 200 */
+ /* full pathname of char device containing fs */
+ char ch_pad1[GLOBAL_HDR_STRING_SZ]; /* 100 300 */
+ /* in case another label is needed */
+ char ch_fstype[CONTENT_HDR_FSTYPE_SZ]; /* 10 310 */
+ /* from fsid.h */
+ uuid_t ch_fsid; /* 10 320 */
+ /* file system uuid */
+ char ch_pad2[GLOBAL_HDR_UUID_SZ]; /* 10 330 */
+ /* in case another id is needed */
+ char ch_pad3[8]; /* 8 338 */
+ /* padding */
+ int32_t ch_strategyid; /* 4 33c */
+ /* ID of the content strategy used to produce this dump */
+ char ch_pad4[4]; /* 4 340 */
+ /* alignment */
+ char ch_specific[0xc0]; /* c0 400 */
+ /* content strategy-specific info */
+};
+typedef struct content_hdr content_hdr_t;
+
+typedef uint64_t xfs_ino_t;
+struct startpt {
+ xfs_ino_t sp_ino; /* first inode to dump */
+ off64_t sp_offset; /* bytes to skip in file data fork */
+ int32_t sp_flags;
+ int32_t sp_pad1;
+};
+typedef struct startpt startpt_t;
+
+#define CONTENT_INODE_HDR_SZ sizeofmember(content_hdr_t, ch_specific)
+struct content_inode_hdr {
+ int32_t cih_mediafiletype; /* 4 4 */
+ /* dump media file type: see #defines below */
+ int32_t cih_dumpattr; /* 4 8 */
+ /* dump attributes: see #defines below */
+ int32_t cih_level; /* 4 c */
+ /* dump level */
+ char pad1[4]; /* 4 10 */
+ /* alignment */
+ time32_t cih_last_time; /* 4 14 */
+ /* if an incremental, time of previous dump at a lesser level */
+ time32_t cih_resume_time; /* 4 18 */
+ /* if a resumed dump, time of interrupted dump */
+ xfs_ino_t cih_rootino; /* 8 20 */
+ /* root inode number */
+ uuid_t cih_last_id; /* 10 30 */
+ /* if an incremental, uuid of prev dump */
+ uuid_t cih_resume_id; /* 10 40 */
+ /* if a resumed dump, uuid of interrupted dump */
+ startpt_t cih_startpt; /* 18 58 */
+ /* starting point of media file contents */
+ startpt_t cih_endpt; /* 18 70 */
+ /* starting point of next stream */
+ uint64_t cih_inomap_hnkcnt; /* 8 78 */
+
+ uint64_t cih_inomap_segcnt; /* 8 80 */
+
+ uint64_t cih_inomap_dircnt; /* 8 88 */
+
+ uint64_t cih_inomap_nondircnt; /* 8 90 */
+
+ xfs_ino_t cih_inomap_firstino; /* 8 98 */
+
+ xfs_ino_t cih_inomap_lastino; /* 8 a0 */
+
+ uint64_t cih_inomap_datasz; /* 8 a8 */
+ /* bytes of non-metadata dumped */
+ char cih_pad2[CONTENT_INODE_HDR_SZ - 0xa8]; /* 18 c0 */
+ /* padding */
+};
+typedef struct content_inode_hdr content_inode_hdr_t;
+// End of definitions from xfsdump
+
+int main(int argc, char *argv[]) {
+
+ if (argc < 3) {
+ fprintf(stderr, "Usage: %s <path/to/dumpfile> <fake rootino>\n", argv[0]);
+ exit(1);
+ }
+
+ const char *filepath = argv[1];
+ const uint64_t fake_root_ino = (uint64_t)strtol(argv[2], NULL, 10);
+
+ int fd = open(filepath, O_RDWR);
+ if (fd < 0) {
+ perror("open");
+ exit(1);
+ }
+ global_hdr_t *header = mmap(NULL, GLOBAL_HDR_SZ, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
+ if (header == MAP_FAILED) {
+ perror("mmap");
+ exit(1);
+ }
+
+ drive_hdr_t *dh = (drive_hdr_t *)header->gh_upper;
+ media_hdr_t *mh = (media_hdr_t *)dh->dh_upper;
+ content_hdr_t *ch = (content_hdr_t *)mh->mh_upper;
+ content_inode_hdr_t *cih = (content_inode_hdr_t *)ch->ch_specific;
+ uint64_t *rootino_ptr = &cih->cih_rootino;
+
+ int32_t checksum = (int32_t)be32toh(header->gh_checksum);
+ uint64_t orig_rootino = be64toh(*rootino_ptr);
+
+ // Fake root inode number
+ *rootino_ptr = htobe64(fake_root_ino);
+
+ // Update checksum along with overwriting rootino.
+ uint64_t gap = orig_rootino - fake_root_ino;
+ checksum += (gap >> 32) + (gap & 0x00000000ffffffff);
+ header->gh_checksum = htobe32(checksum);
+
+ munmap(header, GLOBAL_HDR_SZ);
+ close(fd);
+}
int err;
err = io_uring_queue_init(1, &ring, 0);
- if (err == 0)
+ switch (err) {
+ case 0:
return 0;
-
- if (err == -ENOSYS) /* CONFIG_IO_URING=n */
+ case -ENOSYS:
+ /* CONFIG_IO_URING=n */
return 1;
-
- fprintf(stderr, "unexpected error from io_uring_queue_init(): %s\n",
- strerror(-err));
- return 2;
+ case -EPERM:
+ /* Might be due to sysctl io_uring_disabled isn't 0 */
+ return 2;
+ default:
+ fprintf(stderr, "unexpected error from io_uring_queue_init(): %s\n",
+ strerror(-err));
+ return 100;
+ }
#else
/* liburing is unavailable, assume IO_URING is unsupported */
return 1;
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2024 Meta Platforms, Inc. All Rights Reserved.
+ */
+
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <linux/fs.h>
+#include <linux/types.h>
+#include <linux/fiemap.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+int prep_mmap_buffer(int fd, void **addr)
+{
+ struct stat st;
+ int ret;
+
+ ret = fstat(fd, &st);
+ if (ret)
+ err(1, "failed to stat %d", fd);
+
+ *addr = mmap(NULL, st.st_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
+ if (*addr == MAP_FAILED)
+ err(1, "failed to mmap %d", fd);
+
+ return st.st_size;
+}
+
+int main(int argc, char *argv[])
+{
+ struct fiemap *fiemap;
+ size_t sz, last = 0;
+ void *buf = NULL;
+ int ret, fd;
+
+ if (argc != 2)
+ errx(1, "no in and out file name arguments given");
+
+ fd = open(argv[1], O_RDWR, 0666);
+ if (fd == -1)
+ err(1, "failed to open %s", argv[1]);
+
+ sz = prep_mmap_buffer(fd, &buf);
+
+ fiemap = (struct fiemap *)buf;
+ fiemap->fm_flags = 0;
+ fiemap->fm_extent_count = (sz - sizeof(struct fiemap)) /
+ sizeof(struct fiemap_extent);
+
+ while (last < sz) {
+ int i;
+
+ fiemap->fm_start = last;
+ fiemap->fm_length = sz - last;
+
+ ret = ioctl(fd, FS_IOC_FIEMAP, (unsigned long)fiemap);
+ if (ret < 0)
+ err(1, "fiemap failed %d", errno);
+ for (i = 0; i < fiemap->fm_mapped_extents; i++)
+ last = fiemap->fm_extents[i].fe_logical +
+ fiemap->fm_extents[i].fe_length;
+ }
+
+ munmap(buf, sz);
+ close(fd);
+ return 0;
+}
static int
check_data(struct fiemap *fiemap, __u64 logical_offset, int blocksize,
- int last, int prealloc)
+ int prealloc)
{
struct fiemap_extent *extent;
__u64 orig_offset = logical_offset;
if (!found) {
printf("ERROR: couldn't find extent at %llu\n",
(unsigned long long)(orig_offset / blocksize));
- } else if (last &&
- !(fiemap->fm_extents[c].fe_flags & FIEMAP_EXTENT_LAST)) {
- printf("ERROR: last extent not marked as last: %llu\n",
- (unsigned long long)(orig_offset / blocksize));
- found = 0;
}
return (!found) ? -1 : 0;
if (logical_offset + blocksize < start)
break;
+ /*
+ * Filesystems are allowed to fill in holes with preallocated
+ * unwritten extents
+ */
+ if (extent->fe_flags & FIEMAP_EXTENT_UNWRITTEN)
+ continue;
+
if (logical_offset >= start &&
logical_offset < end) {
{
struct fiemap *fiemap;
char *fiebuf;
- int blocks_to_map, ret, cur_extent = 0, last_data = 0;
+ int blocks_to_map, ret, cur_extent = 0;
__u64 map_start, map_length;
int i, c;
map_start = 0;
map_length = blocks_to_map * blocksize;
- for (i = 0; i < blocks; i++) {
- if (map[i] != 'H')
- last_data = i;
- }
-
fiemap->fm_flags = syncfile ? FIEMAP_FLAG_SYNC : 0;
fiemap->fm_extent_count = blocks_to_map;
fiemap->fm_mapped_extents = 0;
switch (map[i]) {
case 'D':
if (check_data(fiemap, logical_offset,
- blocksize, last_data == i, 0))
+ blocksize, 0))
goto error;
break;
case 'H':
break;
case 'P':
if (check_data(fiemap, logical_offset,
- blocksize, last_data == i, 1))
+ blocksize, 1))
goto error;
break;
default:
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0-or-later WITH Linux-syscall-note */
+/*
+ * FIEXCHANGE ioctl definitions, to facilitate exchanging parts of files.
+ *
+ * Copyright (C) 2022 Oracle. All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <djwong@kernel.org>
+ */
+#ifndef _LINUX_FIEXCHANGE_H
+#define _LINUX_FIEXCHANGE_H
+
+#include <linux/types.h>
+
+/*
+ * Exchange part of file1 with part of the file that this ioctl that is being
+ * called against (which we'll call file2). Filesystems must be able to
+ * restart and complete the operation even after the system goes down.
+ */
+struct xfs_exch_range {
+ __s64 file1_fd;
+ __s64 file1_offset; /* file1 offset, bytes */
+ __s64 file2_offset; /* file2 offset, bytes */
+ __s64 length; /* bytes to exchange */
+
+ __u64 flags; /* see XFS_EXCH_RANGE_* below */
+
+ /* file2 metadata for optional freshness checks */
+ __s64 file2_ino; /* inode number */
+ __s64 file2_mtime; /* modification time */
+ __s64 file2_ctime; /* change time */
+ __s32 file2_mtime_nsec; /* mod time, nsec */
+ __s32 file2_ctime_nsec; /* change time, nsec */
+
+ __u64 pad[6]; /* must be zeroes */
+};
+
+/*
+ * Atomic exchange operations are not required. This relaxes the requirement
+ * that the filesystem must be able to complete the operation after a crash.
+ */
+#define XFS_EXCH_RANGE_NONATOMIC (1 << 0)
+
+/*
+ * Check that file2's inode number, mtime, and ctime against the values
+ * provided, and return -EBUSY if there isn't an exact match.
+ */
+#define XFS_EXCH_RANGE_FILE2_FRESH (1 << 1)
+
+/*
+ * Check that the file1's length is equal to file1_offset + length, and that
+ * file2's length is equal to file2_offset + length. Returns -EDOM if there
+ * isn't an exact match.
+ */
+#define XFS_EXCH_RANGE_FULL_FILES (1 << 2)
+
+/*
+ * Exchange file data all the way to the ends of both files, and then exchange
+ * the file sizes. This flag can be used to replace a file's contents with a
+ * different amount of data. length will be ignored.
+ */
+#define XFS_EXCH_RANGE_TO_EOF (1 << 3)
+
+/* Flush all changes in file data and file metadata to disk before returning. */
+#define XFS_EXCH_RANGE_FSYNC (1 << 4)
+
+/* Dry run; do all the parameter verification but do not change anything. */
+#define XFS_EXCH_RANGE_DRY_RUN (1 << 5)
+
+/*
+ * Only exchange ranges where file1's range maps to a written extent. This can
+ * be used to emulate scatter-gather atomic writes with a temp file.
+ */
+#define XFS_EXCH_RANGE_FILE1_WRITTEN (1 << 6)
+
+/*
+ * Commit the contents of file1 into file2 if file2 has the same inode number,
+ * mtime, and ctime as the arguments provided to the call. The old contents of
+ * file2 will be moved to file1.
+ *
+ * With this flag, all committed information can be retrieved even if the
+ * system crashes or is rebooted. This includes writing through or flushing a
+ * disk cache if present. The call blocks until the device reports that the
+ * commit is complete.
+ *
+ * This flag should not be combined with NONATOMIC. It can be combined with
+ * FILE1_WRITTEN.
+ */
+#define XFS_EXCH_RANGE_COMMIT (XFS_EXCH_RANGE_FILE2_FRESH | \
+ XFS_EXCH_RANGE_FSYNC)
+
+#define XFS_EXCH_RANGE_ALL_FLAGS (XFS_EXCH_RANGE_NONATOMIC | \
+ XFS_EXCH_RANGE_FILE2_FRESH | \
+ XFS_EXCH_RANGE_FULL_FILES | \
+ XFS_EXCH_RANGE_TO_EOF | \
+ XFS_EXCH_RANGE_FSYNC | \
+ XFS_EXCH_RANGE_DRY_RUN | \
+ XFS_EXCH_RANGE_FILE1_WRITTEN)
+
+#define XFS_IOC_EXCHANGE_RANGE _IOWR('X', 129, struct xfs_exch_range)
+
+#endif /* _LINUX_FIEXCHANGE_H */
/*
* Define to enable the tests of the crypto code in this file. If enabled, you
* must link this program with OpenSSL (-lcrypto) v1.1.0 or later, and your
- * kernel needs CONFIG_CRYPTO_USER_API_SKCIPHER=y and CONFIG_CRYPTO_ADIANTUM=y.
+ * kernel needs CONFIG_CRYPTO_USER_API_SKCIPHER=y, CONFIG_CRYPTO_ADIANTUM=y, and
+ * CONFIG_CRYPTO_HCTR2=y.
*/
#undef ENABLE_ALG_TESTS
"resulting ciphertext (or plaintext) to stdout.\n"
"\n"
"CIPHER can be AES-256-XTS, AES-256-CTS-CBC, AES-128-CBC-ESSIV, AES-128-CTS-CBC,\n"
-"or Adiantum. MASTER_KEY must be a hex string long enough for the cipher.\n"
+"Adiantum, or AES-256-HCTR2. MASTER_KEY must be a hex string long enough for\n"
+"the cipher.\n"
"\n"
"WARNING: this program is only meant for testing, not for \"real\" use!\n"
"\n"
"Options:\n"
-" --block-number=BNUM Starting block number for IV generation.\n"
+" --data-unit-index=DUIDX Starting data unit index for IV generation.\n"
" Default: 0\n"
-" --block-size=BLOCK_SIZE Encrypt each BLOCK_SIZE bytes independently.\n"
+" --data-unit-size=DUSIZE Encrypt each DUSIZE bytes independently.\n"
" Default: 4096 bytes\n"
" --decrypt Decrypt instead of encrypt\n"
" --direct-key Use the format where the IVs include the file\n"
" HKDF-SHA512, or none. Default: none\n"
" --mode-num=NUM The encryption mode number. This may be required\n"
" for key derivation, depending on other options.\n"
-" --padding=PADDING If last block is partial, zero-pad it to next\n"
-" PADDING-byte boundary. Default: BLOCK_SIZE\n"
+" --padding=PADDING If last data unit is partial, zero-pad it to next\n"
+" PADDING-byte boundary. Default: DUSIZE\n"
, fp);
}
while (count--)
*buf++ = rand();
}
-#endif
+
+#include <linux/if_alg.h>
+#include <sys/socket.h>
+#define SOL_ALG 279
+static void af_alg_crypt(int algfd, int op, const u8 *key, size_t keylen,
+ const u8 *iv, size_t ivlen,
+ const u8 *src, u8 *dst, size_t datalen)
+{
+ size_t controllen = CMSG_SPACE(sizeof(int)) +
+ CMSG_SPACE(sizeof(struct af_alg_iv) + ivlen);
+ u8 *control = xmalloc(controllen);
+ struct iovec iov = { .iov_base = (u8 *)src, .iov_len = datalen };
+ struct msghdr msg = {
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ .msg_control = control,
+ .msg_controllen = controllen,
+ };
+ struct cmsghdr *cmsg;
+ struct af_alg_iv *algiv;
+ int reqfd;
+
+ memset(control, 0, controllen);
+
+ cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+ cmsg->cmsg_level = SOL_ALG;
+ cmsg->cmsg_type = ALG_SET_OP;
+ *(int *)CMSG_DATA(cmsg) = op;
+
+ cmsg = CMSG_NXTHDR(&msg, cmsg);
+ cmsg->cmsg_len = CMSG_LEN(sizeof(struct af_alg_iv) + ivlen);
+ cmsg->cmsg_level = SOL_ALG;
+ cmsg->cmsg_type = ALG_SET_IV;
+ algiv = (struct af_alg_iv *)CMSG_DATA(cmsg);
+ algiv->ivlen = ivlen;
+ memcpy(algiv->iv, iv, ivlen);
+
+ if (setsockopt(algfd, SOL_ALG, ALG_SET_KEY, key, keylen) != 0)
+ die_errno("can't set key on AF_ALG socket");
+
+ reqfd = accept(algfd, NULL, NULL);
+ if (reqfd < 0)
+ die_errno("can't accept() AF_ALG socket");
+ if (sendmsg(reqfd, &msg, 0) != datalen)
+ die_errno("can't sendmsg() AF_ALG request socket");
+ if (xread(reqfd, dst, datalen) != datalen)
+ die("short read from AF_ALG request socket");
+ close(reqfd);
+
+ free(control);
+}
+#endif /* ENABLE_ALG_TESTS */
/*----------------------------------------------------------------------------*
* Finite field arithmetic *
} ble128;
/* Multiply a GF(2^128) element by the polynomial 'x' */
-static inline void gf2_128_mul_x(ble128 *t)
+static inline void gf2_128_mul_x_xts(ble128 *t)
{
u64 lo = le64_to_cpu(t->lo);
u64 hi = le64_to_cpu(t->hi);
t->lo = cpu_to_le64((lo << 1) ^ ((hi & (1ULL << 63)) ? 0x87 : 0));
}
+static inline void gf2_128_mul_x_polyval(ble128 *t)
+{
+ u64 lo = le64_to_cpu(t->lo);
+ u64 hi = le64_to_cpu(t->hi);
+ u64 lo_reducer = (hi & (1ULL << 63)) ? 1 : 0;
+ u64 hi_reducer = (hi & (1ULL << 63)) ? 0xc2ULL << 56 : 0;
+
+ t->hi = cpu_to_le64(((hi << 1) | (lo >> 63)) ^ hi_reducer);
+ t->lo = cpu_to_le64((lo << 1) ^ lo_reducer);
+}
+
+static void gf2_128_mul_polyval(ble128 *r, const ble128 *b)
+{
+ int i;
+ ble128 p;
+ u64 lo = le64_to_cpu(b->lo);
+ u64 hi = le64_to_cpu(b->hi);
+
+ memset(&p, 0, sizeof(p));
+ for (i = 0; i < 64; i++) {
+ if (lo & (1ULL << i))
+ xor((u8 *)&p, (u8 *)&p, (u8 *)r, sizeof(p));
+ gf2_128_mul_x_polyval(r);
+ }
+ for (i = 0; i < 64; i++) {
+ if (hi & (1ULL << i))
+ xor((u8 *)&p, (u8 *)&p, (u8 *)r, sizeof(p));
+ gf2_128_mul_x_polyval(r);
+ }
+ *r = p;
+}
+
/*----------------------------------------------------------------------------*
* Group arithmetic *
*----------------------------------------------------------------------------*/
}
#ifdef ENABLE_ALG_TESTS
-#include <openssl/aes.h>
+#include <openssl/evp.h>
static void test_aes_keysize(int keysize)
{
unsigned long num_tests = NUM_ALG_TEST_ITERATIONS;
+ const EVP_CIPHER *evp_cipher;
+ EVP_CIPHER_CTX *ctx;
+ switch (keysize) {
+ case 16:
+ evp_cipher = EVP_aes_128_ecb();
+ break;
+ case 24:
+ evp_cipher = EVP_aes_192_ecb();
+ break;
+ case 32:
+ evp_cipher = EVP_aes_256_ecb();
+ break;
+ default:
+ ASSERT(0);
+ }
+
+ ctx = EVP_CIPHER_CTX_new();
+ ASSERT(ctx != NULL);
while (num_tests--) {
struct aes_key k;
- AES_KEY ref_k;
u8 key[AES_256_KEY_SIZE];
u8 ptext[AES_BLOCK_SIZE];
u8 ctext[AES_BLOCK_SIZE];
u8 ref_ctext[AES_BLOCK_SIZE];
u8 decrypted[AES_BLOCK_SIZE];
+ int outl, res;
rand_bytes(key, keysize);
rand_bytes(ptext, AES_BLOCK_SIZE);
aes_setkey(&k, key, keysize);
aes_encrypt(&k, ptext, ctext);
- ASSERT(AES_set_encrypt_key(key, keysize*8, &ref_k) == 0);
- AES_encrypt(ptext, ref_ctext, &ref_k);
-
+ res = EVP_EncryptInit_ex(ctx, evp_cipher, NULL, key, NULL);
+ ASSERT(res > 0);
+ res = EVP_EncryptUpdate(ctx, ref_ctext, &outl, ptext,
+ AES_BLOCK_SIZE);
+ ASSERT(res > 0);
+ ASSERT(outl == AES_BLOCK_SIZE);
ASSERT(memcmp(ctext, ref_ctext, AES_BLOCK_SIZE) == 0);
aes_decrypt(&k, ctext, decrypted);
ASSERT(memcmp(ptext, decrypted, AES_BLOCK_SIZE) == 0);
}
+ EVP_CIPHER_CTX_free(ctx);
}
static void test_aes(void)
size_t ikmlen = 1 + (rand() % sizeof(ikm));
size_t saltlen = rand() % (1 + sizeof(salt));
size_t infolen = rand() % (1 + sizeof(info));
- size_t outlen = rand() % (1 + sizeof(actual_output));
+ /*
+ * Don't test zero-length outputs, since OpenSSL 3.0 and later
+ * returns an error for those.
+ */
+ size_t outlen = 1 + (rand() % sizeof(actual_output));
rand_bytes(ikm, ikmlen);
rand_bytes(salt, saltlen);
}
#endif /* ENABLE_ALG_TESTS */
+/*----------------------------------------------------------------------------*
+ * POLYVAL *
+ *----------------------------------------------------------------------------*/
+
+/*
+ * Reference: "AES-GCM-SIV: Nonce Misuse-Resistant Authenticated Encryption"
+ * https://datatracker.ietf.org/doc/html/rfc8452
+ */
+
+#define POLYVAL_KEY_SIZE 16
+#define POLYVAL_BLOCK_SIZE 16
+
+static void polyval_update(const u8 key[POLYVAL_KEY_SIZE],
+ const u8 *msg, size_t msglen,
+ u8 accumulator[POLYVAL_BLOCK_SIZE])
+{
+ ble128 h;
+ ble128 aligned_accumulator;
+ // x^{-128} = x^127 + x^124 + x^121 + x^114 + 1
+ static const ble128 inv128 = {
+ cpu_to_le64(1),
+ cpu_to_le64(0x9204ULL << 48)
+ };
+
+ /* Partial block support is not necessary for HCTR2 */
+ ASSERT(msglen % POLYVAL_BLOCK_SIZE == 0);
+
+ memcpy(&h, key, POLYVAL_BLOCK_SIZE);
+ memcpy(&aligned_accumulator, accumulator, POLYVAL_BLOCK_SIZE);
+ gf2_128_mul_polyval(&h, &inv128);
+
+ while (msglen > 0) {
+ xor((u8 *)&aligned_accumulator, (u8 *)&aligned_accumulator, msg,
+ POLYVAL_BLOCK_SIZE);
+ gf2_128_mul_polyval(&aligned_accumulator, &h);
+ msg += POLYVAL_BLOCK_SIZE;
+ msglen -= POLYVAL_BLOCK_SIZE;
+ }
+ memcpy(accumulator, &aligned_accumulator, POLYVAL_BLOCK_SIZE);
+}
+
/*----------------------------------------------------------------------------*
* AES encryption modes *
*----------------------------------------------------------------------------*/
else
aes_encrypt(&cipher_key, &dst[i], &dst[i]);
xor(&dst[i], &dst[i], (const u8 *)&t, AES_BLOCK_SIZE);
- gf2_128_mul_x(&t);
+ gf2_128_mul_x_xts(&t);
}
}
while (num_tests--) {
u8 key[2 * AES_256_KEY_SIZE];
u8 iv[AES_BLOCK_SIZE];
- u8 ptext[512];
+ u8 ptext[32 * AES_BLOCK_SIZE];
u8 ctext[sizeof(ptext)];
u8 ref_ctext[sizeof(ptext)];
u8 decrypted[sizeof(ptext)];
- const size_t datalen = ROUND_DOWN(rand() % (1 + sizeof(ptext)),
- AES_BLOCK_SIZE);
+ /*
+ * Don't test message lengths that aren't a multiple of the AES
+ * block size, since support for that is not implemented here.
+ * Also don't test zero-length messages, since OpenSSL 3.0 and
+ * later returns an error for those.
+ */
+ const size_t datalen = AES_BLOCK_SIZE *
+ (1 + rand() % (sizeof(ptext) / AES_BLOCK_SIZE));
int outl, res;
rand_bytes(key, sizeof(key));
aes_cts_cbc_decrypt(key, AES_128_KEY_SIZE, iv, src, dst, nbytes);
}
+/*
+ * Reference: "Length-preserving encryption with HCTR2"
+ * https://ia.cr/2021/1441
+ */
+
+static void aes_256_xctr_crypt(const u8 key[AES_256_KEY_SIZE],
+ const u8 iv[AES_BLOCK_SIZE], const u8 *src,
+ u8 *dst, size_t nbytes)
+{
+ struct aes_key k;
+ union {
+ u8 bytes[AES_BLOCK_SIZE];
+ __le64 ctr;
+ } blk;
+ size_t i;
+
+ aes_setkey(&k, key, AES_256_KEY_SIZE);
+
+ for (i = 0; i < nbytes; i += AES_BLOCK_SIZE) {
+ memcpy(blk.bytes, iv, AES_BLOCK_SIZE);
+ blk.ctr ^= cpu_to_le64((i / AES_BLOCK_SIZE) + 1);
+ aes_encrypt(&k, blk.bytes, blk.bytes);
+ xor(&dst[i], blk.bytes, &src[i], MIN(AES_BLOCK_SIZE, nbytes - i));
+ }
+}
+
+/*
+ * Reference: "Length-preserving encryption with HCTR2"
+ * https://ia.cr/2021/1441
+ */
+
+#define HCTR2_IV_SIZE 32
+static void hctr2_hash_iv(const u8 hbar[POLYVAL_KEY_SIZE],
+ const u8 iv[HCTR2_IV_SIZE], size_t msglen,
+ u8 digest[POLYVAL_BLOCK_SIZE])
+{
+ le128 tweaklen_blk = {
+ .lo = cpu_to_le64(HCTR2_IV_SIZE * 8 * 2 + 2 +
+ (msglen % AES_BLOCK_SIZE != 0))
+ };
+
+ memset(digest, 0, POLYVAL_BLOCK_SIZE);
+ polyval_update(hbar, (u8 *)&tweaklen_blk, POLYVAL_BLOCK_SIZE, digest);
+ polyval_update(hbar, iv, HCTR2_IV_SIZE, digest);
+}
+
+static void hctr2_hash_message(const u8 hbar[POLYVAL_KEY_SIZE],
+ const u8 *msg, size_t msglen,
+ u8 digest[POLYVAL_BLOCK_SIZE])
+{
+ size_t remainder = msglen % AES_BLOCK_SIZE;
+ u8 padded_block[POLYVAL_BLOCK_SIZE] = {0};
+
+ polyval_update(hbar, msg, msglen - remainder, digest);
+ if (remainder) {
+ memcpy(padded_block, &msg[msglen - remainder], remainder);
+ padded_block[remainder] = 1;
+ polyval_update(hbar, padded_block, POLYVAL_BLOCK_SIZE, digest);
+ }
+}
+
+static void aes_256_hctr2_crypt(const u8 key[AES_256_KEY_SIZE],
+ const u8 iv[HCTR2_IV_SIZE], const u8 *src,
+ u8 *dst, size_t nbytes, bool decrypting)
+{
+ struct aes_key k;
+ u8 hbar[AES_BLOCK_SIZE] = {0};
+ u8 L[AES_BLOCK_SIZE] = {1};
+ size_t bulk_bytes = nbytes - AES_BLOCK_SIZE;
+ u8 digest[POLYVAL_BLOCK_SIZE];
+ const u8 *M = src;
+ const u8 *N = src + AES_BLOCK_SIZE;
+ u8 MM[AES_BLOCK_SIZE];
+ u8 UU[AES_BLOCK_SIZE];
+ u8 S[AES_BLOCK_SIZE];
+ u8 *U = dst;
+ u8 *V = dst + AES_BLOCK_SIZE;
+
+ ASSERT(nbytes >= AES_BLOCK_SIZE);
+ aes_setkey(&k, key, AES_256_KEY_SIZE);
+
+ aes_encrypt(&k, hbar, hbar);
+ aes_encrypt(&k, L, L);
+
+ hctr2_hash_iv(hbar, iv, bulk_bytes, digest);
+ hctr2_hash_message(hbar, N, bulk_bytes, digest);
+
+ xor(MM, M, digest, AES_BLOCK_SIZE);
+
+ if (decrypting)
+ aes_decrypt(&k, MM, UU);
+ else
+ aes_encrypt(&k, MM, UU);
+
+ xor(S, MM, UU, AES_BLOCK_SIZE);
+ xor(S, L, S, AES_BLOCK_SIZE);
+
+ aes_256_xctr_crypt(key, S, N, V, bulk_bytes);
+
+ hctr2_hash_iv(hbar, iv, bulk_bytes, digest);
+ hctr2_hash_message(hbar, V, bulk_bytes, digest);
+
+ xor(U, UU, digest, AES_BLOCK_SIZE);
+}
+
+static void aes_256_hctr2_encrypt(const u8 key[AES_256_KEY_SIZE],
+ const u8 iv[HCTR2_IV_SIZE], const u8 *src,
+ u8 *dst, size_t nbytes)
+{
+ aes_256_hctr2_crypt(key, iv, src, dst, nbytes, false);
+}
+
+static void aes_256_hctr2_decrypt(const u8 key[AES_256_KEY_SIZE],
+ const u8 iv[HCTR2_IV_SIZE], const u8 *src,
+ u8 *dst, size_t nbytes)
+{
+ aes_256_hctr2_crypt(key, iv, src, dst, nbytes, true);
+}
+
+#ifdef ENABLE_ALG_TESTS
+#include <linux/if_alg.h>
+#include <sys/socket.h>
+static void test_aes_256_hctr2(void)
+{
+ int algfd = socket(AF_ALG, SOCK_SEQPACKET, 0);
+ struct sockaddr_alg addr = {
+ .salg_type = "skcipher",
+ .salg_name = "hctr2(aes)",
+ };
+ unsigned long num_tests = NUM_ALG_TEST_ITERATIONS;
+
+ if (algfd < 0)
+ die_errno("can't create AF_ALG socket");
+ if (bind(algfd, (struct sockaddr *)&addr, sizeof(addr)) != 0)
+ die_errno("can't bind AF_ALG socket to HCTR2 algorithm");
+
+ while (num_tests--) {
+ u8 key[AES_256_KEY_SIZE];
+ u8 iv[HCTR2_IV_SIZE];
+ u8 ptext[4096];
+ u8 ctext[sizeof(ptext)];
+ u8 ref_ctext[sizeof(ptext)];
+ u8 decrypted[sizeof(ptext)];
+ const size_t datalen = 16 + (rand() % (sizeof(ptext) - 15));
+
+ rand_bytes(key, sizeof(key));
+ rand_bytes(iv, sizeof(iv));
+ rand_bytes(ptext, datalen);
+
+ aes_256_hctr2_encrypt(key, iv, ptext, ctext, datalen);
+ af_alg_crypt(algfd, ALG_OP_ENCRYPT, key, sizeof(key),
+ iv, sizeof(iv), ptext, ref_ctext, datalen);
+ ASSERT(memcmp(ctext, ref_ctext, datalen) == 0);
+
+ aes_256_hctr2_decrypt(key, iv, ctext, decrypted, datalen);
+ ASSERT(memcmp(ptext, decrypted, datalen) == 0);
+ }
+ close(algfd);
+}
+#endif /* ENABLE_ALG_TESTS */
+
/*----------------------------------------------------------------------------*
* XChaCha12 stream cipher *
*----------------------------------------------------------------------------*/
}
#ifdef ENABLE_ALG_TESTS
-#include <linux/if_alg.h>
-#include <sys/socket.h>
-#define SOL_ALG 279
-static void af_alg_crypt(int algfd, int op, const u8 *key, size_t keylen,
- const u8 *iv, size_t ivlen,
- const u8 *src, u8 *dst, size_t datalen)
-{
- size_t controllen = CMSG_SPACE(sizeof(int)) +
- CMSG_SPACE(sizeof(struct af_alg_iv) + ivlen);
- u8 *control = xmalloc(controllen);
- struct iovec iov = { .iov_base = (u8 *)src, .iov_len = datalen };
- struct msghdr msg = {
- .msg_iov = &iov,
- .msg_iovlen = 1,
- .msg_control = control,
- .msg_controllen = controllen,
- };
- struct cmsghdr *cmsg;
- struct af_alg_iv *algiv;
- int reqfd;
-
- memset(control, 0, controllen);
-
- cmsg = CMSG_FIRSTHDR(&msg);
- cmsg->cmsg_len = CMSG_LEN(sizeof(int));
- cmsg->cmsg_level = SOL_ALG;
- cmsg->cmsg_type = ALG_SET_OP;
- *(int *)CMSG_DATA(cmsg) = op;
-
- cmsg = CMSG_NXTHDR(&msg, cmsg);
- cmsg->cmsg_len = CMSG_LEN(sizeof(struct af_alg_iv) + ivlen);
- cmsg->cmsg_level = SOL_ALG;
- cmsg->cmsg_type = ALG_SET_IV;
- algiv = (struct af_alg_iv *)CMSG_DATA(cmsg);
- algiv->ivlen = ivlen;
- memcpy(algiv->iv, iv, ivlen);
-
- if (setsockopt(algfd, SOL_ALG, ALG_SET_KEY, key, keylen) != 0)
- die_errno("can't set key on AF_ALG socket");
-
- reqfd = accept(algfd, NULL, NULL);
- if (reqfd < 0)
- die_errno("can't accept() AF_ALG socket");
- if (sendmsg(reqfd, &msg, 0) != datalen)
- die_errno("can't sendmsg() AF_ALG request socket");
- if (xread(reqfd, dst, datalen) != datalen)
- die("short read from AF_ALG request socket");
- close(reqfd);
-
- free(control);
-}
-
static void test_adiantum(void)
{
int algfd = socket(AF_ALG, SOCK_SEQPACKET, 0);
.decrypt = aes_128_cts_cbc_decrypt,
.keysize = AES_128_KEY_SIZE,
.min_input_size = AES_BLOCK_SIZE,
+ }, {
+ .name = "AES-256-HCTR2",
+ .encrypt = aes_256_hctr2_encrypt,
+ .decrypt = aes_256_hctr2_decrypt,
+ .keysize = AES_256_KEY_SIZE,
+ .min_input_size = AES_BLOCK_SIZE,
}, {
.name = "Adiantum",
.encrypt = adiantum_encrypt,
union fscrypt_iv {
/* usual IV format */
struct {
- /* logical block number within the file */
- __le64 block_number;
+ /* data unit index within the file */
+ __le64 data_unit_index;
/* per-file nonce; only set in DIRECT_KEY mode */
u8 nonce[FILE_NONCE_SIZE];
/* IV format for IV_INO_LBLK_* modes */
struct {
/*
- * IV_INO_LBLK_64: logical block number within the file
- * IV_INO_LBLK_32: hashed inode number + logical block number
- * within the file, mod 2^32
+ * IV_INO_LBLK_64: data unit index within the file
+ * IV_INO_LBLK_32: hashed inode number + data unit index within
+ * the file, mod 2^32
*/
- __le32 block_number32;
+ __le32 data_unit_index32;
/* IV_INO_LBLK_64: inode number */
__le32 inode_number;
static void crypt_loop(const struct fscrypt_cipher *cipher, const u8 *key,
union fscrypt_iv *iv, bool decrypting,
- size_t block_size, size_t padding, bool is_bnum_32bit)
+ size_t data_unit_size, size_t padding,
+ bool is_data_unit_index_32bit)
{
- u8 *buf = xmalloc(block_size);
+ u8 *buf = xmalloc(data_unit_size);
size_t res;
- while ((res = xread(STDIN_FILENO, buf, block_size)) > 0) {
- size_t crypt_len = block_size;
+ while ((res = xread(STDIN_FILENO, buf, data_unit_size)) > 0) {
+ size_t crypt_len = data_unit_size;
if (padding > 0) {
crypt_len = MAX(res, cipher->min_input_size);
crypt_len = ROUND_UP(crypt_len, padding);
- crypt_len = MIN(crypt_len, block_size);
+ crypt_len = MIN(crypt_len, data_unit_size);
}
ASSERT(crypt_len >= res);
memset(&buf[res], 0, crypt_len - res);
full_write(STDOUT_FILENO, buf, crypt_len);
- if (is_bnum_32bit)
- iv->block_number32 = cpu_to_le32(
- le32_to_cpu(iv->block_number32) + 1);
+ if (is_data_unit_index_32bit)
+ iv->data_unit_index32 = cpu_to_le32(
+ le32_to_cpu(iv->data_unit_index32) + 1);
else
- iv->block_number = cpu_to_le64(
- le64_to_cpu(iv->block_number) + 1);
+ iv->data_unit_index = cpu_to_le64(
+ le64_to_cpu(iv->data_unit_index) + 1);
}
free(buf);
}
bool direct_key;
bool iv_ino_lblk_64;
bool iv_ino_lblk_32;
- u64 block_number;
+ u64 data_unit_index;
u64 inode_number;
u8 fs_uuid[UUID_SIZE];
bool fs_uuid_specified;
if (params->direct_key) {
if (!params->file_nonce_specified)
die("--direct-key requires --file-nonce");
- iv->block_number = cpu_to_le64(params->block_number);
+ iv->data_unit_index = cpu_to_le64(params->data_unit_index);
memcpy(iv->nonce, params->file_nonce, FILE_NONCE_SIZE);
} else if (params->iv_ino_lblk_64) {
- if (params->block_number > UINT32_MAX)
- die("iv-ino-lblk-64 can't use --block-number > UINT32_MAX");
+ if (params->data_unit_index > UINT32_MAX)
+ die("iv-ino-lblk-64 can't use --data-unit-index > UINT32_MAX");
if (params->inode_number == 0)
die("iv-ino-lblk-64 requires --inode-number");
if (params->inode_number > UINT32_MAX)
die("iv-ino-lblk-64 can't use --inode-number > UINT32_MAX");
- iv->block_number32 = cpu_to_le32(params->block_number);
+ iv->data_unit_index32 = cpu_to_le32(params->data_unit_index);
iv->inode_number = cpu_to_le32(params->inode_number);
} else if (params->iv_ino_lblk_32) {
- if (params->block_number > UINT32_MAX)
- die("iv-ino-lblk-32 can't use --block-number > UINT32_MAX");
+ if (params->data_unit_index > UINT32_MAX)
+ die("iv-ino-lblk-32 can't use --data-unit-index > UINT32_MAX");
if (params->inode_number == 0)
die("iv-ino-lblk-32 requires --inode-number");
- iv->block_number32 = cpu_to_le32(hash_inode_number(params) +
- params->block_number);
+ iv->data_unit_index32 = cpu_to_le32(hash_inode_number(params) +
+ params->data_unit_index);
} else {
- iv->block_number = cpu_to_le64(params->block_number);
+ iv->data_unit_index = cpu_to_le64(params->data_unit_index);
}
}
}
enum {
- OPT_BLOCK_NUMBER,
- OPT_BLOCK_SIZE,
+ OPT_DATA_UNIT_INDEX,
+ OPT_DATA_UNIT_SIZE,
OPT_DECRYPT,
OPT_DIRECT_KEY,
OPT_DUMP_KEY_IDENTIFIER,
};
static const struct option longopts[] = {
- { "block-number", required_argument, NULL, OPT_BLOCK_NUMBER },
- { "block-size", required_argument, NULL, OPT_BLOCK_SIZE },
+ { "data-unit-index", required_argument, NULL, OPT_DATA_UNIT_INDEX },
+ { "data-unit-size", required_argument, NULL, OPT_DATA_UNIT_SIZE },
{ "decrypt", no_argument, NULL, OPT_DECRYPT },
{ "direct-key", no_argument, NULL, OPT_DIRECT_KEY },
{ "dump-key-identifier", no_argument, NULL, OPT_DUMP_KEY_IDENTIFIER },
int main(int argc, char *argv[])
{
- size_t block_size = 4096;
+ size_t data_unit_size = 4096;
bool decrypting = false;
bool dump_key_identifier = false;
struct key_and_iv_params params;
test_aes_256_xts();
test_aes_256_cts_cbc();
test_adiantum();
+ test_aes_256_hctr2();
#endif
while ((c = getopt_long(argc, argv, "", longopts, NULL)) != -1) {
switch (c) {
- case OPT_BLOCK_NUMBER:
+ case OPT_DATA_UNIT_INDEX:
errno = 0;
- params.block_number = strtoull(optarg, &tmp, 10);
+ params.data_unit_index = strtoull(optarg, &tmp, 10);
if (*tmp || errno)
- die("Invalid block number: %s", optarg);
+ die("Invalid data unit index: %s", optarg);
break;
- case OPT_BLOCK_SIZE:
+ case OPT_DATA_UNIT_SIZE:
errno = 0;
- block_size = strtoul(optarg, &tmp, 10);
- if (block_size <= 0 || *tmp || errno)
- die("Invalid block size: %s", optarg);
+ data_unit_size = strtoul(optarg, &tmp, 10);
+ if (data_unit_size <= 0 || *tmp || errno)
+ die("Invalid data unit size: %s", optarg);
break;
case OPT_DECRYPT:
decrypting = true;
if (cipher == NULL)
die("Unknown cipher: %s", argv[0]);
- if (block_size < cipher->min_input_size)
- die("Block size of %zu bytes is too small for cipher %s",
- block_size, cipher->name);
+ if (data_unit_size < cipher->min_input_size)
+ die("Data unit size of %zu bytes is too small for cipher %s",
+ data_unit_size, cipher->name);
parse_master_key(argv[1], ¶ms);
get_key_and_iv(¶ms, real_key, cipher->keysize, &iv);
- crypt_loop(cipher, real_key, &iv, decrypting, block_size, padding,
+ crypt_loop(cipher, real_key, &iv, decrypting, data_unit_size, padding,
params.iv_ino_lblk_64 || params.iv_ino_lblk_32);
return 0;
}
static void create_file(const char *dir, int loop, int child, int fnum)
{
char *buf;
- int size, fd;
+ int size, fd, ret;
char fname[1024];
buf = x_malloc(block_size);
- sprintf(fname, "%s/file%d", dir, fnum);
+ ret = snprintf(fname, sizeof(fname), "%s/file%d", dir, fnum);
+ if (ret < 0 || ret >= sizeof(fname)) {
+ fprintf(stderr,"file path '%s' too long %d\n", dir, ret);
+ exit(1);
+ }
+
fd = open(fname, O_RDWR|O_CREAT|O_TRUNC | (use_sync?O_SYNC:0), 0644);
if (fd == -1) {
perror(fname);
static void check_file(const char *dir, int loop, int child, int fnum)
{
uchar *buf;
- int size, fd;
+ int size, fd, ret;
char fname[1024];
buf = x_malloc(block_size);
- sprintf(fname, "%s/file%d", dir, fnum);
+ ret = snprintf(fname, sizeof(fname), "%s/file%d", dir, fnum);
+ if (ret < 0 || ret >= sizeof(fname)) {
+ fprintf(stderr,"file path is '%s' too long %d\n", dir, ret);
+ exit(1);
+ }
fd = open(fname, O_RDONLY);
if (fd == -1) {
perror(fname);
#include <sys/mman.h>
#endif
+#ifndef HAVE_XFS_IOC_EXCHANGE_RANGE
+# include "fiexchange.h"
+#endif
+
static inline unsigned long long
rounddown_64(unsigned long long x, unsigned int y)
{
#include <string.h>
#include <errno.h>
#include <err.h>
+#include <getopt.h>
char *filename;
unsigned int page_size;
return count2;
}
+static void usage(const char *argv0)
+{
+ fprintf(stderr, "Usage: %s [-2] {filename}\n", argv0);
+ exit(2);
+}
+
int main(int argc, char *argv[])
{
- if (argc != 2)
- errx(1, "no test filename argument given");
- filename = argv[1];
+ int opt, opt_2 = 0;
+
+ while ((opt = getopt(argc, argv, "2")) != -1) {
+ switch(opt) {
+ case '2':
+ opt_2 = 1;
+ break;
+ default:
+ usage(argv[0]);
+ }
+ }
+
+ if (optind + 1 != argc)
+ usage(argv[0]);
+ filename = argv[optind];
page_size = ret = sysconf(_SC_PAGE_SIZE);
if (ret == -1)
errx(1, "pread (D_DIRECT) from hole is broken");
done();
+ if (opt_2) {
+ init('f', O_RDWR | O_DIRECT);
+ ret = do_write(fd, addr + page_size, page_size, page_size);
+ if (ret != page_size)
+ err(1, "pwrite %s (O_DIRECT): %ld != %u", filename, ret, page_size);
+ done();
+ }
+
if (unlink(filename))
err(1, "unlink %s", filename);
--- /dev/null
+#!/usr/bin/python3
+
+# Copyright (c) 2023 Oracle. All rights reserved.
+# SPDX-License-Identifier: GPL-2.0
+#
+# Create a bunch of xattrs in a file.
+
+import argparse
+import sys
+import os
+
+parser = argparse.ArgumentParser(description = 'Mass create xattrs in a file')
+parser.add_argument(
+ '--file', required = True, type = str, help = 'manipulate this file')
+parser.add_argument(
+ '--start', type = int, default = 0,
+ help = 'create xattrs starting with this number')
+parser.add_argument(
+ '--incr', type = int, default = 1,
+ help = 'increment attr number by this much')
+parser.add_argument(
+ '--end', type = int, default = 1000,
+ help = 'stop at this attr number')
+parser.add_argument(
+ '--remove', dest = 'remove', action = 'store_true',
+ help = 'remove instead of creating')
+parser.add_argument(
+ '--format', type = str, default = '%08d',
+ help = 'printf formatting string for attr name')
+parser.add_argument(
+ '--verbose', dest = 'verbose', action = 'store_true',
+ help = 'verbose output')
+
+args = parser.parse_args()
+
+fmtstring = "user.%s" % args.format
+
+# If we are passed a regular file, open it as a proper file descriptor and
+# pass that around for speed. Otherwise, we pass the path.
+fp = None
+try:
+ fp = open(args.file, 'r')
+ fd = fp.fileno()
+ os.listxattr(fd)
+ if args.verbose:
+ print("using fd calls")
+except:
+ if args.verbose:
+ print("using path calls")
+ fd = args.file
+
+for i in range(args.start, args.end + 1, args.incr):
+ fname = fmtstring % i
+
+ if args.remove:
+ if args.verbose:
+ print("removexattr %s" % fname)
+ os.removexattr(fd, fname)
+ else:
+ if args.verbose:
+ print("setxattr %s" % fname)
+ os.setxattr(fd, fname, b'abcdefgh')
--- /dev/null
+#!/usr/bin/perl -w
+
+# Copyright (c) 2023 Oracle. All rights reserved.
+# SPDX-License-Identifier: GPL-2.0
+#
+# Create a bunch of files and subdirs in a directory.
+
+use Getopt::Long;
+use File::Basename;
+
+$progname=$0;
+GetOptions("start=i" => \$start,
+ "end=i" => \$end,
+ "file-pct=i" => \$file_pct,
+ "incr=i" => \$incr,
+ "format=s" => \$format,
+ "dir=s" => \$dir,
+ "remove!" => \$remove,
+ "help!" => \$help,
+ "verbose!" => \$verbose);
+
+
+# check/remove output directory, get filesystem info
+if (defined $help) {
+ # newline at end of die message suppresses line number
+ print STDERR <<"EOF";
+Usage: $progname [options]
+Options:
+ --dir chdir here before starting
+ --start=num create names starting with this number (0)
+ --incr=num increment file number by this much (1)
+ --end=num stop at this file number (100)
+ --file-pct create this percentage of regular files (90 percent)
+ --remove remove instead of creating
+ --format=str printf formatting string for file name ("%08d")
+ --verbose verbose output
+ --help this help screen
+EOF
+ exit(1) unless defined $help;
+ # otherwise...
+ exit(0);
+}
+
+if (defined $dir) {
+ chdir($dir) or die("chdir $dir");
+}
+$start = 0 if (!defined $start);
+$end = 100 if (!defined $end);
+$file_pct = 90 if (!defined $file_pct);
+$format = "%08d" if (!defined $format);
+$incr = 1 if (!defined $incr);
+
+if ($file_pct < 0) {
+ $file_pct = 0;
+} elsif ($file_pct > 100) {
+ $file_pct = 100;
+}
+
+for ($i = $start; $i <= $end; $i += $incr) {
+ $fname = sprintf($format, $i);
+
+ if ($remove) {
+ $verbose && print "rm $fname\n";
+ unlink($fname) or rmdir($fname) or die("unlink $fname");
+ } elsif (($i % 100) < $file_pct) {
+ # create a file
+ $verbose && print "touch $fname\n";
+ open(DONTCARE, ">$fname") or die("touch $fname");
+ close(DONTCARE);
+ } else {
+ # create a subdir
+ $verbose && print "mkdir $fname\n";
+ mkdir($fname, 0755) or die("mkdir $fname");
+ }
+}
+
+exit(0);
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2023 SUSE Linux Products GmbH. All Rights Reserved.
+ */
+#include <dirent.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+/* Number of files we add to the test directory. */
+#define NUM_FILES 5000
+
+int main(int argc, char *argv[])
+{
+ struct dirent *entry;
+ DIR *dir = NULL;
+ char *dir_path = NULL;
+ int dentry_count = 0;
+ int ret = 0;
+ int i;
+
+ if (argc != 2) {
+ fprintf(stderr, "Usage: %s <directory>\n", argv[0]);
+ ret = 1;
+ goto out;
+ }
+
+ dir_path = malloc(strlen(argv[1]) + strlen("/testdir") + 1);
+ if (!dir_path) {
+ fprintf(stderr, "malloc failure\n");
+ ret = ENOMEM;
+ goto out;
+ }
+ i = strlen(argv[1]);
+ memcpy(dir_path, argv[1], i);
+ memcpy(dir_path + i, "/testdir", strlen("/testdir"));
+ dir_path[i + strlen("/testdir")] = '\0';
+
+ ret = mkdir(dir_path, 0700);
+ if (ret == -1) {
+ fprintf(stderr, "Failed to create test directory: %d\n", errno);
+ ret = errno;
+ goto out;
+ }
+
+ ret = chdir(dir_path);
+ if (ret == -1) {
+ fprintf(stderr, "Failed to chdir to the test directory: %d\n", errno);
+ ret = errno;
+ goto out;
+ }
+
+ /* Now create all files inside the directory. */
+ for (i = 1; i <= NUM_FILES; i++) {
+ /* 8 characters is enough for NUM_FILES name plus '\0'. */
+ char file_name[8];
+ FILE *f;
+
+ ret = snprintf(file_name, sizeof(file_name), "%d", i);
+ if (ret < 0 || ret >= sizeof(file_name)) {
+ fprintf(stderr, "Buffer to small for filename %i\n", i);
+ ret = EOVERFLOW;
+ goto out;
+ }
+ f = fopen(file_name, "w");
+ if (f == NULL) {
+ fprintf(stderr, "Failed to create file number %d: %d\n",
+ i, errno);
+ ret = errno;
+ goto out;
+ }
+ fclose(f);
+ }
+
+ dir = opendir(dir_path);
+ if (dir == NULL) {
+ fprintf(stderr, "Failed to open directory: %d\n", errno);
+ ret = errno;
+ goto out;
+ }
+
+ /*
+ * readdir(3) returns NULL when it reaches the end of the directory or
+ * when an error happens, so reset errno to 0 to distinguish between
+ * both cases later.
+ */
+ errno = 0;
+ while ((entry = readdir(dir)) != NULL) {
+ dentry_count++;
+ /*
+ * The actual number of dentries returned varies per filesystem
+ * implementation. On a 6.7-rc4 kernel, on x86_64 with default
+ * mkfs options, xfs returns 5031 dentries while ext4, f2fs and
+ * btrfs return 5002 (the 5000 files plus "." and ".."). These
+ * variations are fine and valid according to POSIX, as some of
+ * the renames may be visible or not while calling readdir(3).
+ * We only want to check we don't enter into an infinite loop,
+ * so let the maximum number of dentries be 3 * NUM_FILES, which
+ * is very reasonable.
+ */
+ if (dentry_count > 3 * NUM_FILES) {
+ fprintf(stderr,
+ "Found too many directory entries (%d)\n",
+ dentry_count);
+ ret = 1;
+ goto out;
+ }
+ /* Can't rename "." and "..", skip them. */
+ if (strcmp(entry->d_name, ".") == 0 ||
+ strcmp(entry->d_name, "..") == 0)
+ continue;
+ ret = rename(entry->d_name, "TEMPFILE");
+ if (ret == -1) {
+ fprintf(stderr,
+ "Failed to rename '%s' to TEMPFILE: %d\n",
+ entry->d_name, errno);
+ ret = errno;
+ goto out;
+ }
+ ret = rename("TEMPFILE", entry->d_name);
+ if (ret == -1) {
+ fprintf(stderr,
+ "Failed to rename TEMPFILE to '%s': %d\n",
+ entry->d_name, errno);
+ ret = errno;
+ goto out;
+ }
+ }
+
+ if (errno) {
+ fprintf(stderr, "Failed to read directory: %d\n", errno);
+ ret = errno;
+ goto out;
+ }
+
+ /* It should return at least NUM_FILES entries +2 (for "." and ".."). */
+ if (dentry_count < NUM_FILES + 2) {
+ fprintf(stderr,
+ "Found less directory entries than expected (%d but expected %d)\n",
+ dentry_count, NUM_FILES + 2);
+ ret = 2;
+ }
+out:
+ free(dir_path);
+ if (dir != NULL)
+ closedir(dir);
+
+ return ret;
+}
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2023 SUSE Linux Products GmbH. All Rights Reserved.
+ */
+#include <dirent.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+/*
+ * Number of files we add to the test directory after calling opendir(3) and
+ * before calling rewinddir(3).
+ */
+#define NUM_FILES 10000
+
+int main(int argc, char *argv[])
+{
+ int file_counters[NUM_FILES] = { 0 };
+ int dot_count = 0;
+ int dot_dot_count = 0;
+ struct dirent *entry;
+ DIR *dir = NULL;
+ char *dir_path = NULL;
+ char *file_path = NULL;
+ int ret = 0;
+ int i;
+
+ if (argc != 2) {
+ fprintf(stderr, "Usage: %s <directory>\n", argv[0]);
+ ret = 1;
+ goto out;
+ }
+
+ dir_path = malloc(strlen(argv[1]) + strlen("/testdir") + 1);
+ if (!dir_path) {
+ fprintf(stderr, "malloc failure\n");
+ ret = ENOMEM;
+ goto out;
+ }
+ i = strlen(argv[1]);
+ memcpy(dir_path, argv[1], i);
+ memcpy(dir_path + i, "/testdir", strlen("/testdir"));
+ dir_path[i + strlen("/testdir")] = '\0';
+
+ /* More than enough to contain any full file path. */
+ file_path = malloc(strlen(dir_path) + 12);
+ if (!file_path) {
+ fprintf(stderr, "malloc failure\n");
+ ret = ENOMEM;
+ goto out;
+ }
+
+ ret = mkdir(dir_path, 0700);
+ if (ret == -1) {
+ fprintf(stderr, "Failed to create test directory: %d\n", errno);
+ ret = errno;
+ goto out;
+ }
+
+ /* Open the directory first. */
+ dir = opendir(dir_path);
+ if (dir == NULL) {
+ fprintf(stderr, "Failed to open directory: %d\n", errno);
+ ret = errno;
+ goto out;
+ }
+
+ /*
+ * Now create all files inside the directory.
+ * File names go from 1 to NUM_FILES, 0 is unused as it's the return
+ * value for atoi(3) when an error happens.
+ */
+ for (i = 1; i <= NUM_FILES; i++) {
+ FILE *f;
+
+ sprintf(file_path, "%s/%d", dir_path, i);
+ f = fopen(file_path, "w");
+ if (f == NULL) {
+ fprintf(stderr, "Failed to create file number %d: %d\n",
+ i, errno);
+ ret = errno;
+ goto out;
+ }
+ fclose(f);
+ }
+
+ /*
+ * Rewind the directory and read it.
+ * POSIX requires that after a rewind, any new names added to the
+ * directory after the openddir(3) call and before the rewinddir(3)
+ * call, must be returned by readdir(3) calls
+ */
+ rewinddir(dir);
+
+ /*
+ * readdir(3) returns NULL when it reaches the end of the directory or
+ * when an error happens, so reset errno to 0 to distinguish between
+ * both cases later.
+ */
+ errno = 0;
+ while ((entry = readdir(dir)) != NULL) {
+ if (strcmp(entry->d_name, ".") == 0) {
+ dot_count++;
+ continue;
+ }
+ if (strcmp(entry->d_name, "..") == 0) {
+ dot_dot_count++;
+ continue;
+ }
+ i = atoi(entry->d_name);
+ if (i == 0) {
+ fprintf(stderr,
+ "Failed to parse name '%s' to integer: %d\n",
+ entry->d_name, errno);
+ ret = errno;
+ goto out;
+ }
+ /* File names go from 1 to NUM_FILES, so subtract 1. */
+ file_counters[i - 1]++;
+ }
+
+ if (errno) {
+ fprintf(stderr, "Failed to read directory: %d\n", errno);
+ ret = errno;
+ goto out;
+ }
+
+ /*
+ * Now check that the readdir() calls return every single file name
+ * and without repeating any of them. If any name is missing or
+ * repeated, don't exit immediatelly, so that we print a message for
+ * all missing or repeated names.
+ */
+ for (i = 0; i < NUM_FILES; i++) {
+ if (file_counters[i] != 1) {
+ fprintf(stderr, "File name %d appeared %d times\n",
+ i + 1, file_counters[i]);
+ ret = EINVAL;
+ }
+ }
+ if (dot_count != 1) {
+ fprintf(stderr, "File name . appeared %d times\n", dot_count);
+ ret = EINVAL;
+ }
+ if (dot_dot_count != 1) {
+ fprintf(stderr, "File name .. appeared %d times\n", dot_dot_count);
+ ret = EINVAL;
+ }
+out:
+ free(dir_path);
+ free(file_path);
+ if (dir != NULL)
+ closedir(dir);
+
+ return ret;
+}
}
}
+/* Compute the file allocation unit size for an XFS file. */
+static int detect_xfs_alloc_unit(int fd)
+{
+ struct fsxattr fsx;
+ struct xfs_fsop_geom fsgeom;
+ int ret;
+
+ ret = ioctl(fd, XFS_IOC_FSGEOMETRY, &fsgeom);
+ if (ret)
+ return -1;
+
+ ret = ioctl(fd, XFS_IOC_FSGETXATTR, &fsx);
+ if (ret)
+ return -1;
+
+ alloc_size = fsgeom.blocksize;
+ if (fsx.fsx_xflags & XFS_XFLAG_REALTIME)
+ alloc_size *= fsgeom.rtextsize;
+
+ return 0;
+}
+
static int get_io_sizes(int fd)
{
off_t pos = 0, offset = 1;
struct stat buf;
int shift, ret;
+ ret = detect_xfs_alloc_unit(fd);
+ if (!ret)
+ goto done;
+
ret = fstat(fd, &buf);
if (ret) {
fprintf(stderr, " ERROR %d: Failed to find io blocksize\n",
if (!shift)
offset += pos ? 0 : 1;
alloc_size = offset;
+done:
fprintf(stdout, "Allocation size: %ld\n", alloc_size);
return 0;
return ret;
}
+/*
+ * Test seeking for data on a 1 byte file, both when there's delalloc and also
+ * after delalloc is flushed.
+ */
+static int test22(int fd, int testnum)
+{
+ const char buf = 'X';
+ int ret;
+
+ ret = do_pwrite(fd, &buf, 1, 0);
+ if (ret)
+ return ret;
+
+ /*
+ * Our file as a size of 1 byte and that byte is in delalloc. Seeking
+ * for data, with a start offset of 0, should return file offset 0.
+ */
+ ret = do_lseek(testnum, 1, fd, 1, SEEK_DATA, 0, 0);
+ if (ret)
+ return ret;
+
+ /* Flush all delalloc. */
+ ret = fsync(fd);
+ if (ret) {
+ fprintf(stderr, "fsync failed: %s (%d)\n", strerror(errno), errno);
+ return ret;
+ }
+
+ /*
+ * We should get the same result we got when we had delalloc, 0 is the
+ * offset with data.
+ */
+ return do_lseek(testnum, 2, fd, 1, SEEK_DATA, 0, 0);
+}
+
/*
* Make sure hole size is properly reported when punched in the middle of a file
*/
{ 19, test19, "Test file SEEK_DATA from middle of a large hole" },
{ 20, test20, "Test file SEEK_DATA from middle of a huge hole" },
{ 21, test21, "Test file SEEK_HOLE that was created by PUNCH_HOLE" },
+ { 22, test22, "Test a 1 byte file" },
};
static int run_test(struct testrec *tr)
--- /dev/null
+#!/usr/bin/awk
+#
+# Convert time interval specifications with suffixes to an integer number of
+# seconds.
+{
+ nr = $1;
+ if ($2 == "" || $2 ~ /s/) # seconds
+ ;
+ else if ($2 ~ /m/) # minutes
+ nr *= 60;
+ else if ($2 ~ /h/) # hours
+ nr *= 3600;
+ else if ($2 ~ /d/) # days
+ nr *= 86400;
+ else if ($2 ~ /w/) # weeks
+ nr *= 604800;
+ else {
+ printf("%s: unknown suffix\n", $2);
+ exit 1;
+ }
+
+ printf("%d\n", nr);
+}
#include <errno.h>
#include <malloc.h>
-#define SECTOR_SIZE 512
-#define BUFFER_SIZE (150 * SECTOR_SIZE)
+unsigned int sector_size;
+unsigned int buffer_size;
void read_from_pipe(int fd, const char *filename, size_t size)
{
- char buffer[SECTOR_SIZE];
+ char *buffer;
size_t sz;
ssize_t ret;
+ buffer = malloc(buffer_size);
while (size) {
sz = size;
- if (sz > sizeof buffer)
- sz = sizeof buffer;
+ if (sz > buffer_size)
+ sz = buffer_size;
ret = read(fd, buffer, sz);
if (ret < 0)
err(1, "read: %s", filename);
}
size -= sz;
}
+ free(buffer);
}
void do_splice1(int fd, const char *filename, size_t size)
void usage(const char *argv0)
{
- fprintf(stderr, "USAGE: %s [-rd] {filename}\n", basename(argv0));
+ fprintf(stderr, "USAGE: %s [-rd] [-s sectorsize] {filename}\n", basename(argv0));
exit(2);
}
int opt, open_flags, fd;
ssize_t ret;
+ /*
+ * init default sector_size and buffer_size, might be changed if the -s
+ * option is specified
+ */
+ sector_size = 512;
+ buffer_size = 150 * sector_size;
+
do_splice = do_splice1;
open_flags = O_CREAT | O_TRUNC | O_RDWR | O_DIRECT;
- while ((opt = getopt(argc, argv, "rd")) != -1) {
+ while ((opt = getopt(argc, argv, "rds:")) != -1) {
switch(opt) {
+ case 's':
+ sector_size = strtol(optarg, NULL, 0);
+ buffer_size = 150 * sector_size;
+ break;
case 'r':
do_splice = do_splice2;
break;
usage(argv[0]);
filename = argv[optind];
+ /* force below printf line buffered */
+ setlinebuf(stdout);
printf("%s reader %s O_DIRECT\n",
do_splice == do_splice1 ? "sequential" : "concurrent",
(open_flags & O_DIRECT) ? "with" : "without");
- buffer = memalign(SECTOR_SIZE, BUFFER_SIZE);
+ buffer = memalign(sector_size, buffer_size);
if (buffer == NULL)
err(1, "memalign");
if (fd == -1)
err(1, "open: %s", filename);
- memset(buffer, 'x', BUFFER_SIZE);
- ret = write(fd, buffer, BUFFER_SIZE);
+ memset(buffer, 'x', buffer_size);
+ ret = write(fd, buffer, buffer_size);
if (ret < 0)
err(1, "write: %s", filename);
- if (ret != BUFFER_SIZE) {
+ if (ret != buffer_size) {
fprintf(stderr, "%s: short write\n", filename);
exit(1);
}
if (ret != 0)
err(1, "lseek: %s", filename);
- do_splice(fd, filename, BUFFER_SIZE);
+ do_splice(fd, filename, buffer_size);
if (unlink(filename) == -1)
err(1, "unlink: %s", filename);
*/
static void prepare_pipe(int p[2])
{
+ unsigned int r = 0;
if (pipe(p)) {
perror("pipe failed");
abort();
/* fill the pipe completely; each pipe_buffer will now have
the PIPE_BUF_FLAG_CAN_MERGE flag */
- for (unsigned r = pipe_size; r > 0;) {
+ for (r = pipe_size; r > 0;) {
unsigned n = r > sizeof(buffer) ? sizeof(buffer) : r;
write(p[1], buffer, n);
r -= n;
/* drain the pipe, freeing all pipe_buffer instances (but
leaving the flags initialized) */
- for (unsigned r = pipe_size; r > 0;) {
+ for (r = pipe_size; r > 0;) {
unsigned n = r > sizeof(buffer) ? sizeof(buffer) : r;
read(p[0], buffer, n);
r -= n;
return 0;
}
-void do_rename(char *prefix)
+static void do_rename(char *prefix)
{
int i = 0;
int fd;
char c_name[BUF_SIZE];
char n_name[BUF_SIZE];
- strncpy(c_name, prefix, BUF_SIZE);
+ strncpy(c_name, prefix, BUF_SIZE - 1);
fd = open(c_name, O_CREAT | O_RDWR, 0644);
if (fd < 0) {
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2022 Fujitsu Limited. All Rights Reserved. */
+#include <errno.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <semaphore.h>
+#include <sys/mman.h>
+#include <sys/wait.h>
+#include <sys/sem.h>
+#include <time.h>
+#include <unistd.h>
+
+sem_t *sem;
+
+void sigbus_handler(int signal)
+{
+ printf("Process is killed by signal: %d\n", signal);
+ sem_post(sem);
+}
+
+void mmap_read_file(char *filename, off_t offset, size_t size)
+{
+ int fd;
+ char *map, *dummy;
+ struct timespec ts;
+
+ fd = open(filename, O_RDWR);
+ map = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, offset);
+ dummy = malloc(size);
+
+ /* make sure page fault happens */
+ memcpy(dummy, map, size);
+
+ /* ready */
+ sem_post(sem);
+
+ usleep(200000);
+
+ clock_gettime(CLOCK_REALTIME, &ts);
+ ts.tv_sec += 3;
+ /* wait for injection done */
+ sem_timedwait(sem, &ts);
+
+ free(dummy);
+ munmap(map, size);
+ close(fd);
+}
+
+void mmap_read_file_then_poison(char *filename, off_t offset, size_t size,
+ off_t poisonOffset, size_t poisonSize)
+{
+ int fd, error;
+ char *map, *dummy;
+
+ /* wait for parent preparation done */
+ sem_wait(sem);
+
+ fd = open(filename, O_RDWR);
+ map = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, offset);
+ dummy = malloc(size);
+
+ /* make sure page fault happens */
+ memcpy(dummy, map, size);
+
+ printf("Inject poison...\n");
+ error = madvise(map + poisonOffset, poisonSize, MADV_HWPOISON);
+ if (error)
+ printf("madvise() has fault: %d, errno: %d\n", error, errno);
+
+ free(dummy);
+ munmap(map, size);
+ close(fd);
+}
+
+int main(int argc, char *argv[])
+{
+ char *pReadFile = NULL, *pPoisonFile = NULL;
+ size_t mmapSize, poisonSize;
+ off_t mmapOffset = 0, poisonOffset = 0;
+ long pagesize = sysconf(_SC_PAGESIZE);
+ int c;
+ pid_t pid;
+
+ if (pagesize < 1) {
+ fprintf(stderr, "sysconf(_SC_PAGESIZE): failed to get page size\n");
+ abort();
+ }
+
+ /* default mmap / poison size, in unit of System Page Size */
+ mmapSize = poisonSize = pagesize;
+
+ while ((c = getopt(argc, argv, "o::s::O::S::R:P:")) != -1) {
+ switch (c) {
+ /* mmap offset */
+ case 'o':
+ mmapOffset = atoi(optarg) * pagesize;
+ break;
+ /* mmap size */
+ case 's':
+ mmapSize = atoi(optarg) * pagesize;
+ break;
+ /* madvice offset */
+ case 'O':
+ poisonOffset = atoi(optarg) * pagesize;
+ break;
+ /* madvice size */
+ case 'S':
+ poisonSize = atoi(optarg) * pagesize;
+ break;
+ /* filename for mmap read */
+ case 'R':
+ pReadFile = optarg;
+ break;
+ /* filename for poison read */
+ case 'P':
+ pPoisonFile = optarg;
+ break;
+ default:
+ printf("Unknown option: %c\n", c);
+ exit(1);
+ }
+ }
+
+ if (!pReadFile || !pPoisonFile) {
+ printf("Usage: \n"
+ " %s [-o mmapOffset] [-s mmapSize] [-O mmapOffset] [-S mmapSize] -R readFile -P poisonFile\n"
+ " (offset and size are both in unit of System Page Size: %ld)\n",
+ basename(argv[0]), pagesize);
+ exit(0);
+ }
+ if (poisonSize < mmapSize)
+ mmapSize = poisonSize;
+
+ /* fork and mmap files */
+ pid = fork();
+ if (pid == 0) {
+ /* handle SIGBUS */
+ signal(SIGBUS, sigbus_handler);
+ sem = sem_open("sync", O_CREAT, 0666, 0);
+
+ /* mread & do memory failure on poison file */
+ mmap_read_file_then_poison(pPoisonFile, mmapOffset, mmapSize,
+ poisonOffset, poisonSize);
+
+ sem_close(sem);
+ } else {
+ sem = sem_open("sync", O_CREAT, 0666, 0);
+
+ /* mread read file, wait for child process to be killed */
+ mmap_read_file(pReadFile, mmapOffset, mmapSize);
+ sem_close(sem);
+ }
+ exit(0);
+}
#include <mntent.h>
#include <limits.h>
-#define LOCK_TIMEOUT 10
+#define LOCK_TIMEOUT 120
#define _(x) (x)
static char *mounted = "t_mtab";
fprintf(stderr, "%s: %s\n", op, strerror(errn));
if (fd > 0)
close(fd);
- if (semid > 0 && semctl(semid, 2, IPC_RMID) == -1)
- perror("exit rmid");
exit(errn);
}
int setlk_macro = F_OFD_SETLKW;
int getlk_macro = F_OFD_GETLK;
struct timespec ts;
- key_t semkey;
+ key_t semkey = -1;
unsigned short vals[2];
union semun semu;
struct semid_ds sem_ds;
struct sembuf sop;
- int opt, ret, retry;
+ int opt, ret;
- while((opt = getopt(argc, argv, "sgrwo:l:PRWtFd")) != -1) {
+ //avoid libcap errno bug
+ errno = 0;
+ while((opt = getopt(argc, argv, "sgrwo:l:PRWtFdK:")) != -1) {
switch(opt) {
case 's':
lock_cmd = 1;
case 'd':
use_dup = 1;
break;
+ case 'K':
+ semkey = strtol(optarg, NULL, 16);
+ break;
default:
usage(argv[0]);
return -1;
err_exit("test_ofd_getlk", errno);
}
- if((semkey = ftok(argv[optind], 255)) == -1)
+ if (semkey == -1)
+ semkey = ftok(argv[optind], 255);
+ if (semkey == -1)
err_exit("ftok", errno);
/* setlk, and always init the semaphore at setlk time */
if (lock_cmd == 1) {
- /*
- * Init the semaphore, with a key related to the testfile.
- * getlk routine will wait untill this sem has been created and
- * iniialized.
- *
- * We must make sure the semaphore set is newly created, rather
- * then the one left from last run. In which case getlk will
- * exit immediately and left setlk routine waiting forever.
- * Also because newly created semaphore has zero sem_otime,
- * which is used here to sync with getlk routine.
- */
- retry = 0;
- do {
- semid = semget(semkey, 2, IPC_CREAT|IPC_EXCL);
- if (semid < 0 && errno == EEXIST) {
- /* remove sem set after one round of test */
- if (semctl(semid, 2, IPC_RMID, semu) == -1)
- err_exit("rmid 0", errno);
- retry++;
- } else if (semid < 0)
- err_exit("semget", errno);
- else
- retry = 10;
- } while (retry < 5);
- /* We can't create a new semaphore set in 5 tries */
- if (retry == 5)
+ semid = semget(semkey, 2, 0);
+ if (semid < 0)
err_exit("semget", errno);
/* Init both new sem to 1 */
semu.array = vals;
if (semctl(semid, 2, SETALL, semu) == -1)
err_exit("init sem", errno);
- /* Inc both new sem to 2 */
- sop.sem_num = 0;
- sop.sem_op = 1;
- sop.sem_flg = 0;
- ts.tv_sec = 5;
- ts.tv_nsec = 0;
- if (semtimedop(semid, &sop, 1, &ts) == -1)
- err_exit("inc sem0 2", errno);
- sop.sem_num = 1;
- sop.sem_op = 1;
- sop.sem_flg = 0;
- ts.tv_sec = 5;
- ts.tv_nsec = 0;
- if (semtimedop(semid, &sop, 1, &ts) == -1)
- err_exit("inc sem1 2", errno);
/*
- * Wait initialization complete. semctl(2) only update
- * sem_ctime, semop(2) will update sem_otime.
+ * Wait initialization complete.
*/
ret = -1;
do {
+ if (ret != -1)
+ usleep(100000);
memset(&sem_ds, 0, sizeof(sem_ds));
semu.buf = &sem_ds;
- ret = semctl(semid, 0, IPC_STAT, semu);
- } while (!(ret == 0 && sem_ds.sem_otime != 0));
+ ret = semctl(semid, 1, GETVAL, semu);
+ if (ret == -1)
+ err_exit("wait sem1 2", errno);
+ } while (ret != 2);
+
+ /* Inc sem0 to 2 */
+ sop.sem_num = 0;
+ sop.sem_op = 1;
+ sop.sem_flg = 0;
+ ts.tv_sec = 5;
+ ts.tv_nsec = 0;
+ if (semtimedop(semid, &sop, 1, &ts) == -1)
+ err_exit("inc sem0 2", errno);
/* place the lock */
if (fcntl(fd, setlk_macro, &flk) < 0)
ts.tv_nsec = 0;
if (semtimedop(semid, &sop, 1, &ts) == -1)
err_exit("wait sem1 0", errno);
-
- /* remove sem set after one round of test */
- if (semctl(semid, 2, IPC_RMID, semu) == -1)
- err_exit("rmid", errno);
close(fd);
exit(0);
}
/* getlck */
if (lock_cmd == 0) {
- /* wait sem created and initialized */
- retry = 5;
- do {
- semid = semget(semkey, 2, 0);
- if (semid != -1)
- break;
- if (errno == ENOENT && retry) {
- sleep(1);
- retry--;
- continue;
- } else {
- err_exit("getlk_semget", errno);
- }
- } while (1);
+ semid = semget(semkey, 2, 0);
+ if (semid < 0)
+ err_exit("getlk_semget", errno);
+
+ /*
+ * Wait initialization complete.
+ */
+ ret = -1;
do {
+ if (ret != -1)
+ usleep(100000);
memset(&sem_ds, 0, sizeof(sem_ds));
semu.buf = &sem_ds;
- ret = semctl(semid, 0, IPC_STAT, semu);
- } while (!(ret == 0 && sem_ds.sem_otime != 0));
+ ret = semctl(semid, 1, GETVAL, semu);
+ if (ret == -1)
+ err_exit("wait sem1 1", errno);
+ } while (ret != 1);
+
+ /* inc sem1 to 2 (initialization completed) */
+ sop.sem_num = 1;
+ sop.sem_op = 1;
+ sop.sem_flg = 0;
+ ts.tv_sec = 5;
+ ts.tv_nsec = 0;
+ if (semtimedop(semid, &sop, 1, &ts) == -1)
+ err_exit("inc sem1 2", errno);
/* wait sem0 == 0 (setlk and close fd done) */
sop.sem_num = 0;
static DIR *dir;
static int dfd;
static int ignore_error;
+static int ignore_dtype = 0;
struct dir_ops {
loff_t (*getpos)(void);
exit(1);
}
memcpy(entry, ret, sizeof(struct dirent));
+
+ /* NFS may or may not set d_type, depending on READDIRPLUS */
+ if (!ignore_dtype && entry->d_type == DT_UNKNOWN)
+ ignore_dtype = 1;
}
static off64_t kernel_getpos(void)
entry->d_reclen = lentry->d_reclen;
entry->d_type = lentry->d_type;
strcpy(entry->d_name, lentry->d_name);
+
+ /* NFS may or may not set d_type, depending on READDIRPLUS */
+ if (!ignore_dtype && entry->d_type == DT_UNKNOWN)
+ ignore_dtype = 1;
}
struct dir_ops libc_ops = {
pos = random() % count;
ops->setpos(pbuf[pos]);
ops->getentry(&entry);
+
if (dbuf[pos].d_ino != entry.d_ino ||
- dbuf[pos].d_type != entry.d_type ||
+ (!ignore_dtype && dbuf[pos].d_type != entry.d_type) ||
strcmp(dbuf[pos].d_name, entry.d_name)) {
fprintf(stderr,
"Mismatch in dir entry %u at pos %llu\n", pos,
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Race reads with reflink to see if reads continue while we're cloning.
+ * Copyright 2023 Oracle. All rights reserved.
+ */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <linux/fs.h>
+#include <time.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+
+static pid_t mypid;
+
+static FILE *outcome_fp;
+
+/* Significant timestamps. Initialized to zero */
+static double program_start, clone_start, clone_end;
+
+/* Coordinates access to timestamps */
+static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
+
+struct readinfo {
+ int fd;
+ unsigned int blocksize;
+ char *descr;
+};
+
+struct cloneinfo {
+ int src_fd, dst_fd;
+};
+
+#define MSEC_PER_SEC 1000
+#define NSEC_PER_MSEC 1000000
+
+/*
+ * Assume that it shouldn't take longer than 100ms for the FICLONE ioctl to
+ * enter the kernel and take locks on an uncontended file. This is also the
+ * required CLOCK_MONOTONIC granularity.
+ */
+#define EARLIEST_READ_MS (MSEC_PER_SEC / 10)
+
+/*
+ * We want to be reasonably sure that the FICLONE takes long enough that any
+ * read initiated after clone operation locked the source file could have
+ * completed a disk IO before the clone finishes. Therefore, we require that
+ * the clone operation must take at least 4x the maximum setup time.
+ */
+#define MINIMUM_CLONE_MS (EARLIEST_READ_MS * 5)
+
+static double timespec_to_msec(const struct timespec *ts)
+{
+ return (ts->tv_sec * (double)MSEC_PER_SEC) +
+ (ts->tv_nsec / (double)NSEC_PER_MSEC);
+}
+
+static void sleep_ms(unsigned int len)
+{
+ struct timespec time = {
+ .tv_nsec = len * NSEC_PER_MSEC,
+ };
+
+ nanosleep(&time, NULL);
+}
+
+static void outcome(const char *str)
+{
+ fprintf(outcome_fp, "%s\n", str);
+ fflush(outcome_fp);
+}
+
+static void *reader_fn(void *arg)
+{
+ struct timespec now;
+ struct readinfo *ri = arg;
+ char *buf = malloc(ri->blocksize);
+ loff_t pos = 0;
+ ssize_t copied;
+ int ret;
+
+ if (!buf) {
+ perror("alloc buffer");
+ goto kill_error;
+ }
+
+ /* Wait for the FICLONE to start */
+ pthread_mutex_lock(&lock);
+ while (clone_start < program_start) {
+ pthread_mutex_unlock(&lock);
+#ifdef DEBUG
+ printf("%s read waiting for clone to start; cs=%.2f ps=%.2f\n",
+ ri->descr, clone_start, program_start);
+ fflush(stdout);
+#endif
+ sleep_ms(1);
+ pthread_mutex_lock(&lock);
+ }
+ pthread_mutex_unlock(&lock);
+ sleep_ms(EARLIEST_READ_MS);
+
+ /* Read from the file... */
+ while ((copied = read(ri->fd, buf, ri->blocksize)) > 0) {
+ double read_completion;
+
+ ret = clock_gettime(CLOCK_MONOTONIC, &now);
+ if (ret) {
+ perror("clock_gettime");
+ goto kill_error;
+ }
+ read_completion = timespec_to_msec(&now);
+
+ /*
+ * If clone_end is still zero, the FICLONE is still running.
+ * If the read completion occurred a safe duration after the
+ * start of the ioctl, then report that as an early read
+ * completion.
+ */
+ pthread_mutex_lock(&lock);
+ if (clone_end < program_start &&
+ read_completion > clone_start + EARLIEST_READ_MS) {
+ pthread_mutex_unlock(&lock);
+
+ /* clone still running... */
+ printf("finished %s read early at %.2fms\n",
+ ri->descr,
+ read_completion - program_start);
+ fflush(stdout);
+ outcome("finished read early");
+ goto kill_done;
+ }
+ pthread_mutex_unlock(&lock);
+
+ sleep_ms(1);
+ pos += copied;
+ }
+ if (copied < 0) {
+ perror("read");
+ goto kill_error;
+ }
+
+ return NULL;
+kill_error:
+ outcome("reader error");
+kill_done:
+ kill(mypid, SIGTERM);
+ return NULL;
+}
+
+static void *clone_fn(void *arg)
+{
+ struct timespec now;
+ struct cloneinfo *ci = arg;
+ int ret;
+
+ /* Record the approximate start time of this thread */
+ ret = clock_gettime(CLOCK_MONOTONIC, &now);
+ if (ret) {
+ perror("clock_gettime clone start");
+ goto kill_error;
+ }
+ pthread_mutex_lock(&lock);
+ clone_start = timespec_to_msec(&now);
+ pthread_mutex_unlock(&lock);
+
+ printf("started clone at %.2fms\n", clone_start - program_start);
+ fflush(stdout);
+
+ /* Kernel call, only killable with a fatal signal */
+ ret = ioctl(ci->dst_fd, FICLONE, ci->src_fd);
+ if (ret) {
+ perror("FICLONE");
+ goto kill_error;
+ }
+
+ /* If the ioctl completes, note the completion time */
+ ret = clock_gettime(CLOCK_MONOTONIC, &now);
+ if (ret) {
+ perror("clock_gettime clone end");
+ goto kill_error;
+ }
+
+ pthread_mutex_lock(&lock);
+ clone_end = timespec_to_msec(&now);
+ pthread_mutex_unlock(&lock);
+
+ printf("finished clone at %.2fms\n",
+ clone_end - program_start);
+ fflush(stdout);
+
+ /* Complain if we didn't take long enough to clone. */
+ if (clone_end < clone_start + MINIMUM_CLONE_MS) {
+ printf("clone did not take enough time (%.2fms)\n",
+ clone_end - clone_start);
+ fflush(stdout);
+ outcome("clone too fast");
+ goto kill_done;
+ }
+
+ outcome("clone finished before any reads");
+ goto kill_done;
+
+kill_error:
+ outcome("clone error");
+kill_done:
+ kill(mypid, SIGTERM);
+ return NULL;
+}
+
+int main(int argc, char *argv[])
+{
+ struct cloneinfo ci;
+ struct readinfo bufio = { .descr = "buffered" };
+ struct readinfo directio = { .descr = "directio" };
+ struct timespec now;
+ pthread_t clone_thread, bufio_thread, directio_thread;
+ double clockres;
+ int ret;
+
+ if (argc != 4) {
+ printf("Usage: %s src_file dst_file outcome_file\n", argv[0]);
+ return 1;
+ }
+
+ mypid = getpid();
+
+ /* Check for sufficient clock precision. */
+ ret = clock_getres(CLOCK_MONOTONIC, &now);
+ if (ret) {
+ perror("clock_getres MONOTONIC");
+ return 2;
+ }
+ printf("CLOCK_MONOTONIC resolution: %llus %luns\n",
+ (unsigned long long)now.tv_sec,
+ (unsigned long)now.tv_nsec);
+ fflush(stdout);
+
+ clockres = timespec_to_msec(&now);
+ if (clockres > EARLIEST_READ_MS) {
+ fprintf(stderr, "insufficient CLOCK_MONOTONIC resolution\n");
+ return 2;
+ }
+
+ /*
+ * Measure program start time since the MONOTONIC time base is not
+ * all that well defined.
+ */
+ ret = clock_gettime(CLOCK_MONOTONIC, &now);
+ if (ret) {
+ perror("clock_gettime MONOTONIC");
+ return 2;
+ }
+ if (now.tv_sec == 0 && now.tv_nsec == 0) {
+ fprintf(stderr, "Uhoh, start time is zero?!\n");
+ return 2;
+ }
+ program_start = timespec_to_msec(&now);
+
+ outcome_fp = fopen(argv[3], "w");
+ if (!outcome_fp) {
+ perror(argv[3]);
+ return 2;
+ }
+
+ /* Open separate fds for each thread */
+ ci.src_fd = open(argv[1], O_RDONLY);
+ if (ci.src_fd < 0) {
+ perror(argv[1]);
+ return 2;
+ }
+
+ ci.dst_fd = open(argv[2], O_RDWR | O_CREAT, 0600);
+ if (ci.dst_fd < 0) {
+ perror(argv[2]);
+ return 2;
+ }
+
+ bufio.fd = open(argv[1], O_RDONLY);
+ if (bufio.fd < 0) {
+ perror(argv[1]);
+ return 2;
+ }
+ bufio.blocksize = 37;
+
+ directio.fd = open(argv[1], O_RDONLY | O_DIRECT);
+ if (directio.fd < 0) {
+ perror(argv[1]);
+ return 2;
+ }
+ directio.blocksize = 512;
+
+ /* Start threads */
+ ret = pthread_create(&clone_thread, NULL, clone_fn, &ci);
+ if (ret) {
+ fprintf(stderr, "create clone thread: %s\n", strerror(ret));
+ return 2;
+ }
+
+ ret = pthread_create(&bufio_thread, NULL, reader_fn, &bufio);
+ if (ret) {
+ fprintf(stderr, "create bufio thread: %s\n", strerror(ret));
+ return 2;
+ }
+
+ ret = pthread_create(&directio_thread, NULL, reader_fn, &directio);
+ if (ret) {
+ fprintf(stderr, "create directio thread: %s\n", strerror(ret));
+ return 2;
+ }
+
+ /* Wait for threads */
+ ret = pthread_join(clone_thread, NULL);
+ if (ret) {
+ fprintf(stderr, "join clone thread: %s\n", strerror(ret));
+ return 2;
+ }
+
+ ret = pthread_join(bufio_thread, NULL);
+ if (ret) {
+ fprintf(stderr, "join bufio thread: %s\n", strerror(ret));
+ return 2;
+ }
+
+ ret = pthread_join(directio_thread, NULL);
+ if (ret) {
+ fprintf(stderr, "join directio thread: %s\n", strerror(ret));
+ return 2;
+ }
+
+ printf("Program should have killed itself?\n");
+ outcome("program failed to end early");
+ return 0;
+}
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) Meta Platforms, Inc. and affiliates.
+
+#include "global.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <linux/types.h>
+#ifdef HAVE_STRUCT_BTRFS_IOCTL_VOL_ARGS_V2
+#include <linux/btrfs.h>
+#else
+#ifndef BTRFS_IOCTL_MAGIC
+#define BTRFS_IOCTL_MAGIC 0x94
+#endif
+
+#ifndef BTRFS_IOC_SNAP_CREATE_V2
+#define BTRFS_IOC_SNAP_CREATE_V2 \
+ _IOW(BTRFS_IOCTL_MAGIC, 23, struct btrfs_ioctl_vol_args_v2)
+#endif
+
+#ifndef BTRFS_IOC_SUBVOL_CREATE_V2
+#define BTRFS_IOC_SUBVOL_CREATE_V2 \
+ _IOW(BTRFS_IOCTL_MAGIC, 24, struct btrfs_ioctl_vol_args_v2)
+#endif
+
+#ifndef BTRFS_SUBVOL_NAME_MAX
+#define BTRFS_SUBVOL_NAME_MAX 4039
+#endif
+
+struct btrfs_ioctl_vol_args_v2 {
+ __s64 fd;
+ __u64 transid;
+ __u64 flags;
+ union {
+ struct {
+ __u64 size;
+ struct btrfs_qgroup_inherit *qgroup_inherit;
+ };
+ __u64 unused[4];
+ };
+ union {
+ char name[BTRFS_SUBVOL_NAME_MAX + 1];
+ __u64 devid;
+ __u64 subvolid;
+ };
+};
+#endif
+
+#if !HAVE_DECL_BTRFS_IOC_SNAP_DESTROY_V2
+#define BTRFS_IOC_SNAP_DESTROY_V2 \
+ _IOW(BTRFS_IOCTL_MAGIC, 63, struct btrfs_ioctl_vol_args_v2)
+#endif
+
+int main(int argc, char **argv)
+{
+ if (argc != 2) {
+ fprintf(stderr, "usage: %s PATH\n", argv[0]);
+ return EXIT_FAILURE;
+ }
+
+ int dirfd = open(argv[1], O_RDONLY | O_DIRECTORY);
+ if (dirfd < 0) {
+ perror(argv[1]);
+ return EXIT_FAILURE;
+ }
+
+ struct btrfs_ioctl_vol_args_v2 subvol_args = {};
+ strcpy(subvol_args.name, "subvol");
+ if (ioctl(dirfd, BTRFS_IOC_SUBVOL_CREATE_V2, &subvol_args) < 0) {
+ perror("BTRFS_IOC_SUBVOL_CREATE_V2");
+ return EXIT_FAILURE;
+ }
+
+ int subvolfd = openat(dirfd, "subvol", O_RDONLY | O_DIRECTORY);
+ if (subvolfd < 0) {
+ perror("openat");
+ return EXIT_FAILURE;
+ }
+
+ if (ioctl(dirfd, BTRFS_IOC_SNAP_DESTROY_V2, &subvol_args) < 0) {
+ perror("BTRFS_IOC_SNAP_DESTROY_V2");
+ return EXIT_FAILURE;
+ }
+
+ struct btrfs_ioctl_vol_args_v2 snap_args = { .fd = subvolfd };
+ strcpy(snap_args.name, "snap");
+ if (ioctl(dirfd, BTRFS_IOC_SNAP_CREATE_V2, &snap_args) < 0) {
+ if (errno == ENOENT)
+ return EXIT_SUCCESS;
+ perror("BTRFS_IOC_SNAP_CREATE_V2");
+ return EXIT_FAILURE;
+ }
+ fprintf(stderr, "BTRFS_IOC_SNAP_CREATE_V2 should've failed\n");
+ return EXIT_FAILURE;
+}
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2022 Google, Inc. All Rights Reserved.
+ *
+ * Test program which uses the raw ext4 set_fsuuid ioctl directly.
+ * SYNOPSIS:
+ * $0 COMMAND MOUNT_POINT [UUID]
+ *
+ * COMMAND must be either "get" or "set".
+ * The UUID must be a 16 octet sequence represented as 32 hexadecimal digits in
+ * canonical textual representation, e.g. output from `uuidgen`.
+ *
+ */
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <uuid/uuid.h>
+#include <linux/fs.h>
+
+struct fsuuid {
+ __u32 fsu_len;
+ __u32 fsu_flags;
+ __u8 fsu_uuid[];
+};
+
+#ifndef EXT4_IOC_GETFSUUID
+#define EXT4_IOC_GETFSUUID _IOR('f', 44, struct fsuuid)
+#endif
+
+#ifndef EXT4_IOC_SETFSUUID
+#define EXT4_IOC_SETFSUUID _IOW('f', 44, struct fsuuid)
+#endif
+
+int main(int argc, char **argv)
+{
+ int error, fd;
+ struct fsuuid *fsuuid = NULL;
+
+ if (argc < 3) {
+ fprintf(stderr, "Invalid arguments\n");
+ return 1;
+ }
+
+ fd = open(argv[2], O_RDONLY);
+ if (!fd) {
+ perror(argv[2]);
+ return 1;
+ }
+
+ fsuuid = malloc(sizeof(*fsuuid) + sizeof(uuid_t));
+ if (!fsuuid) {
+ perror("malloc");
+ return 1;
+ }
+ fsuuid->fsu_len = sizeof(uuid_t);
+ fsuuid->fsu_flags = 0;
+
+ if (strcmp(argv[1], "get") == 0) {
+ uuid_t uuid;
+ char uuid_str[37];
+
+ if (ioctl(fd, EXT4_IOC_GETFSUUID, fsuuid)) {
+ fprintf(stderr, "%s while trying to get fs uuid\n",
+ strerror(errno));
+ return 1;
+ }
+
+ memcpy(&uuid, fsuuid->fsu_uuid, sizeof(uuid));
+ uuid_unparse(uuid, uuid_str);
+ printf("%s\n", uuid_str);
+ } else if (strcmp(argv[1], "set") == 0) {
+ uuid_t uuid;
+
+ if (argc != 4) {
+ fprintf(stderr, "UUID argument missing.\n");
+ return 1;
+ }
+
+ error = uuid_parse(argv[3], uuid);
+ if (error < 0) {
+ fprintf(stderr, "Invalid UUID. The UUID should be in "
+ "canonical format. Example: "
+ "8c628557-6987-42b2-ba16-b7cc79ddfb43\n");
+ return 1;
+ }
+
+ memcpy(&fsuuid->fsu_uuid, uuid, sizeof(uuid));
+ if (ioctl(fd, EXT4_IOC_SETFSUUID, fsuuid)) {
+ fprintf(stderr, "%s while trying to set fs uuid\n",
+ strerror(errno));
+ return 1;
+ }
+ } else {
+ fprintf(stderr, "Invalid command\n");
+ return 1;
+ }
+
+ return 0;
+}
include $(TOPDIR)/include/builddefs
TARGETS = vfstest mount-idmapped
-CFILES_VFSTEST = vfstest.c btrfs-idmapped-mounts.c idmapped-mounts.c utils.c
+CFILES_VFSTEST = vfstest.c btrfs-idmapped-mounts.c idmapped-mounts.c utils.c tmpfs-idmapped-mounts.c
CFILES_MOUNT_IDMAPPED = mount-idmapped.c utils.c
-HFILES = missing.h utils.h btrfs-idmapped-mounts.h idmapped-mounts.h
+HFILES = missing.h utils.h btrfs-idmapped-mounts.h idmapped-mounts.h tmpfs-idmapped-mounts.h
LLDLIBS += -pthread
LDIRT = $(TARGETS)
LLDLIBS += -luring
endif
+ifeq ($(HAVE_XFS_IOC_EXCHANGE_RANGE),yes)
+LCFLAGS += -DHAVE_XFS_IOC_EXCHANGE_RANGE
+endif
+
default: depend $(TARGETS)
depend: .dep
static char t_buf[PATH_MAX];
-static int acls(const struct vfstest_info *info)
+int tcore_acls(const struct vfstest_info *info)
{
int fret = -1;
int dir1_fd = -EBADF, open_tree_fd = -EBADF;
}
/* Validate that basic file operations on idmapped mounts from a user namespace. */
-static int create_in_userns(const struct vfstest_info *info)
+int tcore_create_in_userns(const struct vfstest_info *info)
{
int fret = -1;
int file1_fd = -EBADF, open_tree_fd = -EBADF;
/* Validate that a caller whose fsids map into the idmapped mount within it's
* user namespace cannot create any device nodes.
*/
-static int device_node_in_userns(const struct vfstest_info *info)
+int tcore_device_node_in_userns(const struct vfstest_info *info)
{
int fret = -1;
int open_tree_fd = -EBADF;
return fret;
}
-static int fsids_mapped(const struct vfstest_info *info)
+int tcore_fsids_mapped(const struct vfstest_info *info)
{
int fret = -1;
int file1_fd = -EBADF, hardlink_target_fd = -EBADF, open_tree_fd = -EBADF;
die("failure: create");
/* create character device */
- if (mknodat(open_tree_fd, CHRDEV1, S_IFCHR | 0644, makedev(5, 1)))
+ if (mknodat(open_tree_fd, CHRDEV1, S_IFCHR | 0644, makedev(0, 0)))
die("failure: create");
/* create symlink */
}
/* Validate that basic file operations on idmapped mounts. */
-static int fsids_unmapped(const struct vfstest_info *info)
+int tcore_fsids_unmapped(const struct vfstest_info *info)
{
int fret = -1;
int file1_fd = -EBADF, hardlink_target_fd = -EBADF, open_tree_fd = -EBADF;
}
/* Validate that changing file ownership works correctly on idmapped mounts. */
-static int expected_uid_gid_idmapped_mounts(const struct vfstest_info *info)
+int tcore_expected_uid_gid_idmapped_mounts(const struct vfstest_info *info)
{
int fret = -1;
int file1_fd = -EBADF, open_tree_fd1 = -EBADF, open_tree_fd2 = -EBADF;
}
/* create character device */
- if (mknodat(info->t_dir1_fd, CHRDEV1, S_IFCHR | 0644, makedev(5, 1))) {
+ if (mknodat(info->t_dir1_fd, CHRDEV1, S_IFCHR | 0644, makedev(0, 0))) {
log_stderr("failure: mknodat");
goto out;
}
return fret;
}
-static int fscaps_idmapped_mounts(const struct vfstest_info *info)
+int tcore_fscaps_idmapped_mounts(const struct vfstest_info *info)
{
int fret = -1;
int file1_fd = -EBADF, file1_fd2 = -EBADF, open_tree_fd = -EBADF;
return fret;
}
-static int fscaps_idmapped_mounts_in_userns(const struct vfstest_info *info)
+int tcore_fscaps_idmapped_mounts_in_userns(const struct vfstest_info *info)
{
int fret = -1;
int file1_fd = -EBADF, file1_fd2 = -EBADF, open_tree_fd = -EBADF;
return fret;
}
-static int fscaps_idmapped_mounts_in_userns_separate_userns(const struct vfstest_info *info)
+int tcore_fscaps_idmapped_mounts_in_userns_separate_userns(const struct vfstest_info *info)
{
int fret = -1;
int file1_fd = -EBADF, file1_fd2 = -EBADF, open_tree_fd = -EBADF;
return fret;
}
-static int hardlink_crossing_idmapped_mounts(const struct vfstest_info *info)
+int tcore_hardlink_crossing_idmapped_mounts(const struct vfstest_info *info)
{
int fret = -1;
int file1_fd = -EBADF, open_tree_fd1 = -EBADF, open_tree_fd2 = -EBADF;
return fret;
}
-static int hardlink_from_idmapped_mount(const struct vfstest_info *info)
+int tcore_hardlink_from_idmapped_mount(const struct vfstest_info *info)
{
int fret = -1;
int file1_fd = -EBADF, open_tree_fd = -EBADF;
return fret;
}
-static int hardlink_from_idmapped_mount_in_userns(const struct vfstest_info *info)
+int tcore_hardlink_from_idmapped_mount_in_userns(const struct vfstest_info *info)
{
int fret = -1;
int file1_fd = -EBADF, open_tree_fd = -EBADF;
#ifdef HAVE_LIBURING_H
-static int io_uring_idmapped(const struct vfstest_info *info)
+int tcore_io_uring_idmapped(const struct vfstest_info *info)
{
int fret = -1;
int file1_fd = -EBADF, open_tree_fd = -EBADF;
* In no circumstances, even with recorded credentials can it be allowed to
* open the file.
*/
-static int io_uring_idmapped_unmapped(const struct vfstest_info *info)
+int tcore_io_uring_idmapped_unmapped(const struct vfstest_info *info)
{
int fret = -1;
int file1_fd = -EBADF, open_tree_fd = -EBADF;
return fret;
}
-static int io_uring_idmapped_userns(const struct vfstest_info *info)
+int tcore_io_uring_idmapped_userns(const struct vfstest_info *info)
{
int fret = -1;
int file1_fd = -EBADF, open_tree_fd = -EBADF;
return fret;
}
-static int io_uring_idmapped_unmapped_userns(const struct vfstest_info *info)
+int tcore_io_uring_idmapped_unmapped_userns(const struct vfstest_info *info)
{
int fret = -1;
int file1_fd = -EBADF, open_tree_fd = -EBADF;
#endif /* HAVE_LIBURING_H */
/* Validate that protected symlinks work correctly on idmapped mounts. */
-static int protected_symlinks_idmapped_mounts(const struct vfstest_info *info)
+int tcore_protected_symlinks_idmapped_mounts(const struct vfstest_info *info)
{
int fret = -1;
int dir_fd = -EBADF, fd = -EBADF, open_tree_fd = -EBADF;
/* Validate that protected symlinks work correctly on idmapped mounts inside a
* user namespace.
*/
-static int protected_symlinks_idmapped_mounts_in_userns(const struct vfstest_info *info)
+int tcore_protected_symlinks_idmapped_mounts_in_userns(const struct vfstest_info *info)
{
int fret = -1;
int dir_fd = -EBADF, fd = -EBADF, open_tree_fd = -EBADF;
return fret;
}
-static int rename_crossing_idmapped_mounts(const struct vfstest_info *info)
+int tcore_rename_crossing_idmapped_mounts(const struct vfstest_info *info)
{
int fret = -1;
int file1_fd = -EBADF, open_tree_fd1 = -EBADF, open_tree_fd2 = -EBADF;
return fret;
}
-static int rename_from_idmapped_mount(const struct vfstest_info *info)
+int tcore_rename_from_idmapped_mount(const struct vfstest_info *info)
{
int fret = -1;
int file1_fd = -EBADF, open_tree_fd = -EBADF;
return fret;
}
-static int rename_from_idmapped_mount_in_userns(const struct vfstest_info *info)
+int tcore_rename_from_idmapped_mount_in_userns(const struct vfstest_info *info)
{
int fret = -1;
int file1_fd = -EBADF, open_tree_fd = -EBADF;
return fret;
}
-static int setattr_truncate_idmapped(const struct vfstest_info *info)
+int tcore_setattr_truncate_idmapped(const struct vfstest_info *info)
{
int fret = -1;
int file1_fd = -EBADF, open_tree_fd = -EBADF;
return fret;
}
-static int setattr_truncate_idmapped_in_userns(const struct vfstest_info *info)
+int tcore_setattr_truncate_idmapped_in_userns(const struct vfstest_info *info)
{
int fret = -1;
int file1_fd = -EBADF, open_tree_fd = -EBADF;
return fret;
}
-static int setgid_create_idmapped(const struct vfstest_info *info)
+int tcore_setgid_create_idmapped(const struct vfstest_info *info)
{
int fret = -1;
int file1_fd = -EBADF, open_tree_fd = -EBADF;
return fret;
}
-static int setgid_create_idmapped_in_userns(const struct vfstest_info *info)
+int tcore_setgid_create_idmapped_in_userns(const struct vfstest_info *info)
{
int fret = -1;
int file1_fd = -EBADF, open_tree_fd = -EBADF;
}
/* Validate that setid transitions are handled correctly on idmapped mounts. */
-static int setid_binaries_idmapped_mounts(const struct vfstest_info *info)
+int tcore_setid_binaries_idmapped_mounts(const struct vfstest_info *info)
{
int fret = -1;
int file1_fd = -EBADF, exec_fd = -EBADF, open_tree_fd = -EBADF;
* running in a user namespace where the uid and gid of the setid binary have no
* mapping.
*/
-static int setid_binaries_idmapped_mounts_in_userns(const struct vfstest_info *info)
+int tcore_setid_binaries_idmapped_mounts_in_userns(const struct vfstest_info *info)
{
int fret = -1;
int file1_fd = -EBADF, exec_fd = -EBADF, open_tree_fd = -EBADF;
* running in a user namespace where the uid and gid of the setid binary have no
* mapping.
*/
-static int setid_binaries_idmapped_mounts_in_userns_separate_userns(const struct vfstest_info *info)
+int tcore_setid_binaries_idmapped_mounts_in_userns_separate_userns(const struct vfstest_info *info)
{
int fret = -1;
int file1_fd = -EBADF, exec_fd = -EBADF, open_tree_fd = -EBADF;
return fret;
}
-static int sticky_bit_unlink_idmapped_mounts(const struct vfstest_info *info)
+int tcore_sticky_bit_unlink_idmapped_mounts(const struct vfstest_info *info)
{
int fret = -1;
int dir_fd = -EBADF, open_tree_fd = -EBADF;
/* Validate that the sticky bit behaves correctly on idmapped mounts for unlink
* operations in a user namespace.
*/
-static int sticky_bit_unlink_idmapped_mounts_in_userns(const struct vfstest_info *info)
+int tcore_sticky_bit_unlink_idmapped_mounts_in_userns(const struct vfstest_info *info)
{
int fret = -1;
int dir_fd = -EBADF, open_tree_fd = -EBADF;
return fret;
}
-static int sticky_bit_rename_idmapped_mounts(const struct vfstest_info *info)
+int tcore_sticky_bit_rename_idmapped_mounts(const struct vfstest_info *info)
{
int fret = -1;
int dir_fd = -EBADF, open_tree_fd = -EBADF;
/* Validate that the sticky bit behaves correctly on idmapped mounts for unlink
* operations in a user namespace.
*/
-static int sticky_bit_rename_idmapped_mounts_in_userns(const struct vfstest_info *info)
+int tcore_sticky_bit_rename_idmapped_mounts_in_userns(const struct vfstest_info *info)
{
int fret = -1;
int dir_fd = -EBADF, open_tree_fd = -EBADF;
return fret;
}
-static int symlink_idmapped_mounts(const struct vfstest_info *info)
+int tcore_symlink_idmapped_mounts(const struct vfstest_info *info)
{
int fret = -1;
int file1_fd = -EBADF, open_tree_fd = -EBADF;
return fret;
}
-static int symlink_idmapped_mounts_in_userns(const struct vfstest_info *info)
+int tcore_symlink_idmapped_mounts_in_userns(const struct vfstest_info *info)
{
int fret = -1;
int file1_fd = -EBADF, open_tree_fd = -EBADF;
}
/* Don't write a mapping in the 4th userns. */
- list_empty(&hierarchy[4].id_map);
+ list_empty(&hierarchy[3].id_map);
/* Create the actual userns hierarchy. */
ret = create_userns_hierarchy(hierarchy);
}
if (sys_mount_setattr(fd_open_tree_level4, "", AT_EMPTY_PATH,
- &attr_level4, sizeof(attr_level4))) {
+ &attr_level4, sizeof(attr_level4)) != -1 || errno != EINVAL) {
log_stderr("failure: sys_mount_setattr");
goto out;
}
log_stderr("failure: check ownership %s", file);
goto out;
}
-
- if (!expected_uid_gid(fd_open_tree_level4, file, 0, info->t_overflowuid, info->t_overflowgid)) {
- log_stderr("failure: check ownership %s", file);
- goto out;
- }
}
/* Verify that ownership looks correct for callers in the first userns. */
*/
if (!fchownat(open_tree_fd, FILE1, 0, 0, AT_SYMLINK_NOFOLLOW))
die("failure: change ownership");
- if (errno != EINVAL)
+ if (errno != EINVAL && errno != EOVERFLOW)
die("failure: errno");
/*
*/
if (!fchownat(open_tree_fd, FILE1, 0, 0, AT_SYMLINK_NOFOLLOW))
die("failure: change ownership");
- if (errno != EINVAL)
+ if (errno != EINVAL && errno != EOVERFLOW)
die("failure: errno");
/*
return fret;
}
-static const struct test_struct t_idmapped_mounts[] = {
- { acls, true, "posix acls on regular mounts", },
- { create_in_userns, true, "create operations in user namespace", },
- { device_node_in_userns, true, "device node in user namespace", },
- { expected_uid_gid_idmapped_mounts, true, "expected ownership on idmapped mounts", },
- { fscaps_idmapped_mounts, true, "fscaps on idmapped mounts", },
- { fscaps_idmapped_mounts_in_userns, true, "fscaps on idmapped mounts in user namespace", },
- { fscaps_idmapped_mounts_in_userns_separate_userns, true, "fscaps on idmapped mounts in user namespace with different id mappings", },
- { fsids_mapped, true, "mapped fsids", },
- { fsids_unmapped, true, "unmapped fsids", },
- { hardlink_crossing_idmapped_mounts, true, "cross idmapped mount hardlink", },
- { hardlink_from_idmapped_mount, true, "hardlinks from idmapped mounts", },
- { hardlink_from_idmapped_mount_in_userns, true, "hardlinks from idmapped mounts in user namespace", },
-#ifdef HAVE_LIBURING_H
- { io_uring_idmapped, true, "io_uring from idmapped mounts", },
- { io_uring_idmapped_userns, true, "io_uring from idmapped mounts in user namespace", },
- { io_uring_idmapped_unmapped, true, "io_uring from idmapped mounts with unmapped ids", },
- { io_uring_idmapped_unmapped_userns, true, "io_uring from idmapped mounts with unmapped ids in user namespace", },
-#endif
- { protected_symlinks_idmapped_mounts, true, "following protected symlinks on idmapped mounts", },
- { protected_symlinks_idmapped_mounts_in_userns, true, "following protected symlinks on idmapped mounts in user namespace", },
- { rename_crossing_idmapped_mounts, true, "cross idmapped mount rename", },
- { rename_from_idmapped_mount, true, "rename from idmapped mounts", },
- { rename_from_idmapped_mount_in_userns, true, "rename from idmapped mounts in user namespace", },
- { setattr_truncate_idmapped, true, "setattr truncate on idmapped mounts", },
- { setattr_truncate_idmapped_in_userns, true, "setattr truncate on idmapped mounts in user namespace", },
- { setgid_create_idmapped, true, "create operations in directories with setgid bit set on idmapped mounts", },
- { setgid_create_idmapped_in_userns, true, "create operations in directories with setgid bit set on idmapped mounts in user namespace", },
- { setid_binaries_idmapped_mounts, true, "setid binaries on idmapped mounts", },
- { setid_binaries_idmapped_mounts_in_userns, true, "setid binaries on idmapped mounts in user namespace", },
- { setid_binaries_idmapped_mounts_in_userns_separate_userns, true, "setid binaries on idmapped mounts in user namespace with different id mappings", },
- { sticky_bit_unlink_idmapped_mounts, true, "sticky bit unlink operations on idmapped mounts", },
- { sticky_bit_unlink_idmapped_mounts_in_userns, true, "sticky bit unlink operations on idmapped mounts in user namespace", },
- { sticky_bit_rename_idmapped_mounts, true, "sticky bit rename operations on idmapped mounts", },
- { sticky_bit_rename_idmapped_mounts_in_userns, true, "sticky bit rename operations on idmapped mounts in user namespace", },
- { symlink_idmapped_mounts, true, "symlink from idmapped mounts", },
- { symlink_idmapped_mounts_in_userns, true, "symlink from idmapped mounts in user namespace", },
-};
+/* The current_umask() is stripped from the mode directly in the vfs if the
+ * filesystem either doesn't support acls or the filesystem has been
+ * mounted without posic acl support.
+ *
+ * If the filesystem does support acls then current_umask() stripping is
+ * deferred to posix_acl_create(). So when the filesystem calls
+ * posix_acl_create() and there are no acls set or not supported then
+ * current_umask() will be stripped.
+ *
+ * Use umask(S_IXGRP) to check whether inode strip S_ISGID works correctly
+ * in idmapped situation.
+ *
+ * Test for commit ac6800e279a2 ("fs: Add missing umask strip in vfs_tmpfile")
+ * and 1639a49ccdce ("fs: move S_ISGID stripping into the vfs_*() helpers").
+ */
+static int setgid_create_umask_idmapped(const struct vfstest_info *info)
+{
+ int fret = -1;
+ int file1_fd = -EBADF, open_tree_fd = -EBADF;
+ struct mount_attr attr = {
+ .attr_set = MOUNT_ATTR_IDMAP,
+ };
+ pid_t pid;
+ int tmpfile_fd = -EBADF;
+ bool supported = false;
+ char path[PATH_MAX];
+ mode_t mode;
-const struct test_suite s_idmapped_mounts = {
- .tests = t_idmapped_mounts,
- .nr_tests = ARRAY_SIZE(t_idmapped_mounts),
-};
+ if (!caps_supported())
+ return 0;
-static const struct test_struct t_fscaps_in_ancestor_userns[] = {
- { fscaps_idmapped_mounts_in_userns_valid_in_ancestor_userns, true, "fscaps on idmapped mounts in user namespace writing fscap valid in ancestor userns", },
-};
+ if (fchmod(info->t_dir1_fd, S_IRUSR |
+ S_IWUSR |
+ S_IRGRP |
+ S_IWGRP |
+ S_IROTH |
+ S_IWOTH |
+ S_IXUSR |
+ S_IXGRP |
+ S_IXOTH |
+ S_ISGID), 0) {
+ log_stderr("failure: fchmod");
+ goto out;
+ }
-const struct test_suite s_fscaps_in_ancestor_userns = {
- .tests = t_fscaps_in_ancestor_userns,
- .nr_tests = ARRAY_SIZE(t_fscaps_in_ancestor_userns),
-};
+ /* Verify that the sid bits got raised. */
+ if (!is_setgid(info->t_dir1_fd, "", AT_EMPTY_PATH)) {
+ log_stderr("failure: is_setgid");
+ goto out;
+ }
-static const struct test_struct t_nested_userns[] = {
- { nested_userns, T_REQUIRE_IDMAPPED_MOUNTS, "test that nested user namespaces behave correctly when attached to idmapped mounts", },
-};
+ /* Changing mount properties on a detached mount. */
+ attr.userns_fd = get_userns_fd(0, 10000, 10000);
+ if (attr.userns_fd < 0) {
+ log_stderr("failure: get_userns_fd");
+ goto out;
+ }
-const struct test_suite s_nested_userns = {
- .tests = t_nested_userns,
- .nr_tests = ARRAY_SIZE(t_nested_userns),
-};
+ open_tree_fd = sys_open_tree(info->t_dir1_fd, "",
+ AT_EMPTY_PATH |
+ AT_NO_AUTOMOUNT |
+ AT_SYMLINK_NOFOLLOW |
+ OPEN_TREE_CLOEXEC |
+ OPEN_TREE_CLONE);
+ if (open_tree_fd < 0) {
+ log_stderr("failure: sys_open_tree");
+ goto out;
+ }
-/* Test for commit 968219708108 ("fs: handle circular mappings correctly"). */
-static const struct test_struct t_setattr_fix_968219708108[] = {
- { setattr_fix_968219708108, T_REQUIRE_IDMAPPED_MOUNTS, "test that setattr works correctly", },
-};
+ if (sys_mount_setattr(open_tree_fd, "", AT_EMPTY_PATH, &attr, sizeof(attr))) {
+ log_stderr("failure: sys_mount_setattr");
+ goto out;
+ }
-const struct test_suite s_setattr_fix_968219708108 = {
- .tests = t_setattr_fix_968219708108,
- .nr_tests = ARRAY_SIZE(t_setattr_fix_968219708108),
-};
+ supported = openat_tmpfile_supported(open_tree_fd);
-/* Test for commit 705191b03d50 ("fs: fix acl translation"). */
-static const struct test_struct t_setxattr_fix_705191b03d50[] = {
- { setxattr_fix_705191b03d50, T_REQUIRE_USERNS, "test that setxattr works correctly for userns mountable filesystems", },
-};
+ pid = fork();
+ if (pid < 0) {
+ log_stderr("failure: fork");
+ goto out;
+ }
+ if (pid == 0) {
+ /* Only umask with S_IXGRP because inode strip S_ISGID will check mode
+ * whether has group execute or search permission.
+ */
+ umask(S_IXGRP);
+ mode = umask(S_IXGRP);
+ if (!(mode & S_IXGRP))
+ die("failure: umask");
-const struct test_suite s_setxattr_fix_705191b03d50 = {
- .tests = t_setxattr_fix_705191b03d50,
- .nr_tests = ARRAY_SIZE(t_setxattr_fix_705191b03d50),
+ if (!switch_ids(10000, 11000))
+ die("failure: switch fsids");
+
+ /* create regular file via open() */
+ file1_fd = openat(open_tree_fd, FILE1, O_CREAT | O_EXCL | O_CLOEXEC, S_IXGRP | S_ISGID);
+ if (file1_fd < 0)
+ die("failure: create");
+
+ /* Neither in_group_p() nor capable_wrt_inode_uidgid() so setgid
+ * bit needs to be stripped.
+ */
+ if (is_setgid(open_tree_fd, FILE1, 0))
+ die("failure: is_setgid");
+
+ if (is_ixgrp(open_tree_fd, FILE1, 0))
+ die("failure: is_ixgrp");
+
+ /* create directory */
+ if (mkdirat(open_tree_fd, DIR1, 0000))
+ die("failure: create");
+
+ if (xfs_irix_sgid_inherit_enabled(info->t_fstype)) {
+ /* We're not in_group_p(). */
+ if (is_setgid(open_tree_fd, DIR1, 0))
+ die("failure: is_setgid");
+ } else {
+ /* Directories always inherit the setgid bit. */
+ if (!is_setgid(open_tree_fd, DIR1, 0))
+ die("failure: is_setgid");
+ }
+
+ if (is_ixgrp(open_tree_fd, DIR1, 0))
+ die("failure: is_ixgrp");
+
+ /* create a special file via mknodat() vfs_create */
+ if (mknodat(open_tree_fd, FILE2, S_IFREG | S_ISGID | S_IXGRP, 0))
+ die("failure: mknodat");
+
+ if (is_setgid(open_tree_fd, FILE2, 0))
+ die("failure: is_setgid");
+
+ if (is_ixgrp(open_tree_fd, FILE2, 0))
+ die("failure: is_ixgrp");
+
+ /* create a whiteout device via mknodat() vfs_mknod */
+ if (mknodat(open_tree_fd, CHRDEV1, S_IFCHR | S_ISGID | S_IXGRP, 0))
+ die("failure: mknodat");
+
+ if (is_setgid(open_tree_fd, CHRDEV1, 0))
+ die("failure: is_setgid");
+
+ if (is_ixgrp(open_tree_fd, CHRDEV1, 0))
+ die("failure: is_ixgrp");
+
+ /*
+ * In setgid directories newly created files always inherit the
+ * gid from the parent directory. Verify that the file is owned
+ * by gid 10000, not by gid 11000.
+ */
+ if (!expected_uid_gid(open_tree_fd, FILE1, 0, 10000, 10000))
+ die("failure: check ownership");
+
+ /*
+ * In setgid directories newly created directories always
+ * inherit the gid from the parent directory. Verify that the
+ * directory is owned by gid 10000, not by gid 11000.
+ */
+ if (!expected_uid_gid(open_tree_fd, DIR1, 0, 10000, 10000))
+ die("failure: check ownership");
+
+ if (!expected_uid_gid(open_tree_fd, FILE2, 0, 10000, 10000))
+ die("failure: check ownership");
+
+ if (!expected_uid_gid(open_tree_fd, CHRDEV1, 0, 10000, 10000))
+ die("failure: check ownership");
+
+ if (unlinkat(open_tree_fd, FILE1, 0))
+ die("failure: delete");
+
+ if (unlinkat(open_tree_fd, DIR1, AT_REMOVEDIR))
+ die("failure: delete");
+
+ if (unlinkat(open_tree_fd, FILE2, 0))
+ die("failure: delete");
+
+ if (unlinkat(open_tree_fd, CHRDEV1, 0))
+ die("failure: delete");
+
+ /* create tmpfile via filesystem tmpfile api */
+ if (supported) {
+ tmpfile_fd = openat(open_tree_fd, ".", O_TMPFILE | O_RDWR, S_IXGRP | S_ISGID);
+ if (tmpfile_fd < 0)
+ die("failure: create");
+ /* link the temporary file into the filesystem, making it permanent */
+ snprintf(path, PATH_MAX, "/proc/self/fd/%d", tmpfile_fd);
+ if (linkat(AT_FDCWD, path, open_tree_fd, FILE3, AT_SYMLINK_FOLLOW))
+ die("failure: linkat");
+ if (close(tmpfile_fd))
+ die("failure: close");
+ if (is_setgid(open_tree_fd, FILE3, 0))
+ die("failure: is_setgid");
+ if (is_ixgrp(open_tree_fd, FILE3, 0))
+ die("failure: is_ixgrp");
+ if (!expected_uid_gid(open_tree_fd, FILE3, 0, 10000, 10000))
+ die("failure: check ownership");
+ if (unlinkat(open_tree_fd, FILE3, 0))
+ die("failure: delete");
+ }
+
+ exit(EXIT_SUCCESS);
+ }
+ if (wait_for_pid(pid))
+ goto out;
+
+ fret = 0;
+ log_debug("Ran test");
+out:
+ safe_close(attr.userns_fd);
+ safe_close(file1_fd);
+ safe_close(open_tree_fd);
+
+ return fret;
+}
+
+/* The current_umask() is stripped from the mode directly in the vfs if the
+ * filesystem either doesn't support acls or the filesystem has been
+ * mounted without posic acl support.
+ *
+ * If the filesystem does support acls then current_umask() stripping is
+ * deferred to posix_acl_create(). So when the filesystem calls
+ * posix_acl_create() and there are no acls set or not supported then
+ * current_umask() will be stripped.
+ *
+ * Use umask(S_IXGRP) to check whether inode strip S_ISGID works correctly
+ * in idmapped_in_userns situation.
+ *
+ * Test for commit ac6800e279a2 ("fs: Add missing umask strip in vfs_tmpfile")
+ * and 1639a49ccdce ("fs: move S_ISGID stripping into the vfs_*() helpers").
+ */
+static int setgid_create_umask_idmapped_in_userns(const struct vfstest_info *info)
+{
+ int fret = -1;
+ int file1_fd = -EBADF, open_tree_fd = -EBADF;
+ struct mount_attr attr = {
+ .attr_set = MOUNT_ATTR_IDMAP,
+ };
+ pid_t pid;
+ int tmpfile_fd = -EBADF;
+ bool supported = false;
+ char path[PATH_MAX];
+ mode_t mode;
+
+ if (!caps_supported())
+ return 0;
+
+ if (fchmod(info->t_dir1_fd, S_IRUSR |
+ S_IWUSR |
+ S_IRGRP |
+ S_IWGRP |
+ S_IROTH |
+ S_IWOTH |
+ S_IXUSR |
+ S_IXGRP |
+ S_IXOTH |
+ S_ISGID), 0) {
+ log_stderr("failure: fchmod");
+ goto out;
+ }
+
+ /* Verify that the sid bits got raised. */
+ if (!is_setgid(info->t_dir1_fd, "", AT_EMPTY_PATH)) {
+ log_stderr("failure: is_setgid");
+ goto out;
+ }
+
+ /* Changing mount properties on a detached mount. */
+ attr.userns_fd = get_userns_fd(0, 10000, 10000);
+ if (attr.userns_fd < 0) {
+ log_stderr("failure: get_userns_fd");
+ goto out;
+ }
+
+ open_tree_fd = sys_open_tree(info->t_dir1_fd, "",
+ AT_EMPTY_PATH |
+ AT_NO_AUTOMOUNT |
+ AT_SYMLINK_NOFOLLOW |
+ OPEN_TREE_CLOEXEC |
+ OPEN_TREE_CLONE);
+ if (open_tree_fd < 0) {
+ log_stderr("failure: sys_open_tree");
+ goto out;
+ }
+
+ if (sys_mount_setattr(open_tree_fd, "", AT_EMPTY_PATH, &attr, sizeof(attr))) {
+ log_stderr("failure: sys_mount_setattr");
+ goto out;
+ }
+
+ supported = openat_tmpfile_supported(open_tree_fd);
+
+ /*
+ * Below we verify that setgid inheritance for a newly created file or
+ * directory works correctly. As part of this we need to verify that
+ * newly created files or directories inherit their gid from their
+ * parent directory. So we change the parent directorie's gid to 1000
+ * and create a file with fs{g,u}id 0 and verify that the newly created
+ * file and directory inherit gid 1000, not 0.
+ */
+ if (fchownat(info->t_dir1_fd, "", -1, 1000, AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH)) {
+ log_stderr("failure: fchownat");
+ goto out;
+ }
+
+ pid = fork();
+ if (pid < 0) {
+ log_stderr("failure: fork");
+ goto out;
+ }
+ if (pid == 0) {
+ if (!caps_supported()) {
+ log_debug("skip: capability library not installed");
+ exit(EXIT_SUCCESS);
+ }
+
+ /* Only umask with S_IXGRP because inode strip S_ISGID will check mode
+ * whether has group execute or search permission.
+ */
+ umask(S_IXGRP);
+ mode = umask(S_IXGRP);
+ if (!(mode & S_IXGRP))
+ die("failure: umask");
+
+ if (!switch_userns(attr.userns_fd, 0, 0, false))
+ die("failure: switch_userns");
+
+ if (!caps_down_fsetid())
+ die("failure: caps_down_fsetid");
+
+ /* create regular file via open() */
+ file1_fd = openat(open_tree_fd, FILE1, O_CREAT | O_EXCL | O_CLOEXEC, S_IXGRP | S_ISGID);
+ if (file1_fd < 0)
+ die("failure: create");
+
+ /* Neither in_group_p() nor capable_wrt_inode_uidgid() so setgid
+ * bit needs to be stripped.
+ */
+ if (is_setgid(open_tree_fd, FILE1, 0))
+ die("failure: is_setgid");
+
+ if (is_ixgrp(open_tree_fd, FILE1, 0))
+ die("failure: is_ixgrp");
+
+ /* create directory */
+ if (mkdirat(open_tree_fd, DIR1, 0000))
+ die("failure: create");
+
+ if (xfs_irix_sgid_inherit_enabled(info->t_fstype)) {
+ /* We're not in_group_p(). */
+ if (is_setgid(open_tree_fd, DIR1, 0))
+ die("failure: is_setgid");
+ } else {
+ /* Directories always inherit the setgid bit. */
+ if (!is_setgid(open_tree_fd, DIR1, 0))
+ die("failure: is_setgid");
+ }
+
+ if (is_ixgrp(open_tree_fd, DIR1, 0))
+ die("failure: is_ixgrp");
+
+ /* create a special file via mknodat() vfs_create */
+ if (mknodat(open_tree_fd, FILE2, S_IFREG | S_ISGID | S_IXGRP, 0))
+ die("failure: mknodat");
+
+ if (is_setgid(open_tree_fd, FILE2, 0))
+ die("failure: is_setgid");
+
+ if (is_ixgrp(open_tree_fd, FILE2, 0))
+ die("failure: is_ixgrp");
+
+ /* create a whiteout device via mknodat() vfs_mknod */
+ if (mknodat(open_tree_fd, CHRDEV1, S_IFCHR | S_ISGID | S_IXGRP, 0))
+ die("failure: mknodat");
+
+ if (is_setgid(open_tree_fd, CHRDEV1, 0))
+ die("failure: is_setgid");
+
+ if (is_ixgrp(open_tree_fd, CHRDEV1, 0))
+ die("failure: is_ixgrp");
+
+ /*
+ * In setgid directories newly created files always inherit the
+ * gid from the parent directory. Verify that the file is owned
+ * by gid 1000, not by gid 0.
+ */
+ if (!expected_uid_gid(open_tree_fd, FILE1, 0, 0, 1000))
+ die("failure: check ownership");
+
+ /*
+ * In setgid directories newly created directories always
+ * inherit the gid from the parent directory. Verify that the
+ * directory is owned by gid 1000, not by gid 0.
+ */
+ if (!expected_uid_gid(open_tree_fd, DIR1, 0, 0, 1000))
+ die("failure: check ownership");
+
+ if (!expected_uid_gid(open_tree_fd, FILE2, 0, 0, 1000))
+ die("failure: check ownership");
+
+ if (!expected_uid_gid(open_tree_fd, CHRDEV1, 0, 0, 1000))
+ die("failure: check ownership");
+
+ if (unlinkat(open_tree_fd, FILE1, 0))
+ die("failure: delete");
+
+ if (unlinkat(open_tree_fd, DIR1, AT_REMOVEDIR))
+ die("failure: delete");
+
+ if (unlinkat(open_tree_fd, FILE2, 0))
+ die("failure: delete");
+
+ if (unlinkat(open_tree_fd, CHRDEV1, 0))
+ die("failure: delete");
+
+ /* create tmpfile via filesystem tmpfile api */
+ if (supported) {
+ tmpfile_fd = openat(open_tree_fd, ".", O_TMPFILE | O_RDWR, S_IXGRP | S_ISGID);
+ if (tmpfile_fd < 0)
+ die("failure: create");
+ /* link the temporary file into the filesystem, making it permanent */
+ snprintf(path, PATH_MAX, "/proc/self/fd/%d", tmpfile_fd);
+ if (linkat(AT_FDCWD, path, open_tree_fd, FILE3, AT_SYMLINK_FOLLOW))
+ die("failure: linkat");
+ if (close(tmpfile_fd))
+ die("failure: close");
+ if (is_setgid(open_tree_fd, FILE3, 0))
+ die("failure: is_setgid");
+ if (is_ixgrp(open_tree_fd, FILE3, 0))
+ die("failure: is_ixgrp");
+ if (!expected_uid_gid(open_tree_fd, FILE3, 0, 0, 1000))
+ die("failure: check ownership");
+ if (unlinkat(open_tree_fd, FILE3, 0))
+ die("failure: delete");
+ }
+
+ exit(EXIT_SUCCESS);
+ }
+ if (wait_for_pid(pid))
+ goto out;
+
+ fret = 0;
+ log_debug("Ran test");
+out:
+ safe_close(attr.userns_fd);
+ safe_close(file1_fd);
+ safe_close(open_tree_fd);
+
+ return fret;
+}
+
+/*
+ * If the parent directory has a default acl then permissions are based off
+ * of that and current_umask() is ignored. Specifically, if the ACL has an
+ * ACL_MASK entry, the group permissions correspond to the permissions of
+ * the ACL_MASK entry. Otherwise, if the ACL has no ACL_MASK entry, the
+ * group permissions correspond to the permissions of the ACL_GROUP_OBJ
+ * entry.
+ *
+ * Use setfacl to check whether inode strip S_ISGID works correctly under
+ * the above two situations when enabling idmapped.
+ *
+ * Test for commit
+ * 1639a49ccdce ("fs: move S_ISGID stripping into the vfs_*() helpers").
+ */
+static int setgid_create_acl_idmapped(const struct vfstest_info *info)
+{
+ int fret = -1;
+ int file1_fd = -EBADF, open_tree_fd = -EBADF;
+ struct mount_attr attr = {
+ .attr_set = MOUNT_ATTR_IDMAP,
+ };
+ pid_t pid;
+ int tmpfile_fd = -EBADF;
+ bool supported = false;
+ char path[PATH_MAX];
+ mode_t mode;
+
+ if (!caps_supported())
+ return 0;
+
+ if (fchmod(info->t_dir1_fd, S_IRUSR |
+ S_IWUSR |
+ S_IRGRP |
+ S_IWGRP |
+ S_IROTH |
+ S_IWOTH |
+ S_IXUSR |
+ S_IXGRP |
+ S_IXOTH |
+ S_ISGID), 0) {
+ log_stderr("failure: fchmod");
+ goto out;
+ }
+
+ /* Verify that the sid bits got raised. */
+ if (!is_setgid(info->t_dir1_fd, "", AT_EMPTY_PATH)) {
+ log_stderr("failure: is_setgid");
+ goto out;
+ }
+
+ /* Changing mount properties on a detached mount. */
+ attr.userns_fd = get_userns_fd(0, 10000, 10000);
+ if (attr.userns_fd < 0) {
+ log_stderr("failure: get_userns_fd");
+ goto out;
+ }
+
+ open_tree_fd = sys_open_tree(info->t_dir1_fd, "",
+ AT_EMPTY_PATH |
+ AT_NO_AUTOMOUNT |
+ AT_SYMLINK_NOFOLLOW |
+ OPEN_TREE_CLOEXEC |
+ OPEN_TREE_CLONE);
+ if (open_tree_fd < 0) {
+ log_stderr("failure: sys_open_tree");
+ goto out;
+ }
+
+ if (sys_mount_setattr(open_tree_fd, "", AT_EMPTY_PATH, &attr, sizeof(attr))) {
+ log_stderr("failure: sys_mount_setattr");
+ goto out;
+ }
+
+ supported = openat_tmpfile_supported(open_tree_fd);
+
+ pid = fork();
+ if (pid < 0) {
+ log_stderr("failure: fork");
+ goto out;
+ }
+ if (pid == 0) {
+ umask(S_IXGRP);
+ mode = umask(S_IXGRP);
+ if (!(mode & S_IXGRP))
+ die("failure: umask");
+
+ /* The group permissions correspond to the permissions of the
+ * ACL_MASK entry. Use setfacl to set ACL mask(m) as rw, so now
+ * the group permissions is rw. Also, umask doesn't affect
+ * group permissions because umask will be ignored if having
+ * acl.
+ */
+ snprintf(t_buf, sizeof(t_buf), "setfacl -d -m u::rwx,g::rw,o::rwx,m:rw %s/%s", info->t_mountpoint, T_DIR1);
+ if (system(t_buf))
+ die("failure: system");
+
+ if (!switch_ids(10000, 11000))
+ die("failure: switch fsids");
+
+ /* create regular file via open() */
+ file1_fd = openat(open_tree_fd, FILE1, O_CREAT | O_EXCL | O_CLOEXEC, S_IXGRP | S_ISGID);
+ if (file1_fd < 0)
+ die("failure: create");
+
+ /* Neither in_group_p() nor capable_wrt_inode_uidgid() so setgid
+ * bit needs to be stripped.
+ */
+ if (is_setgid(open_tree_fd, FILE1, 0))
+ die("failure: is_setgid");
+
+ if (is_ixgrp(open_tree_fd, FILE1, 0))
+ die("failure: is_ixgrp");
+
+ /* create directory */
+ if (mkdirat(open_tree_fd, DIR1, 0000))
+ die("failure: create");
+
+ if (xfs_irix_sgid_inherit_enabled(info->t_fstype)) {
+ /* We're not in_group_p(). */
+ if (is_setgid(open_tree_fd, DIR1, 0))
+ die("failure: is_setgid");
+ } else {
+ /* Directories always inherit the setgid bit. */
+ if (!is_setgid(open_tree_fd, DIR1, 0))
+ die("failure: is_setgid");
+ }
+
+ if (is_ixgrp(open_tree_fd, DIR1, 0))
+ die("failure: is_ixgrp");
+
+ /* create a special file via mknodat() vfs_create */
+ if (mknodat(open_tree_fd, FILE2, S_IFREG | S_ISGID | S_IXGRP, 0))
+ die("failure: mknodat");
+
+ if (is_setgid(open_tree_fd, FILE2, 0))
+ die("failure: is_setgid");
+
+ if (is_ixgrp(open_tree_fd, FILE2, 0))
+ die("failure: is_ixgrp");
+
+ /* create a whiteout device via mknodat() vfs_mknod */
+ if (mknodat(open_tree_fd, CHRDEV1, S_IFCHR | S_ISGID | S_IXGRP, 0))
+ die("failure: mknodat");
+
+ if (is_setgid(open_tree_fd, CHRDEV1, 0))
+ die("failure: is_setgid");
+
+ if (is_ixgrp(open_tree_fd, CHRDEV1, 0))
+ die("failure: is_ixgrp");
+
+ /*
+ * In setgid directories newly created files always inherit the
+ * gid from the parent directory. Verify that the file is owned
+ * by gid 10000, not by gid 11000.
+ */
+ if (!expected_uid_gid(open_tree_fd, FILE1, 0, 10000, 10000))
+ die("failure: check ownership");
+
+ /*
+ * In setgid directories newly created directories always
+ * inherit the gid from the parent directory. Verify that the
+ * directory is owned by gid 10000, not by gid 11000.
+ */
+ if (!expected_uid_gid(open_tree_fd, DIR1, 0, 10000, 10000))
+ die("failure: check ownership");
+
+ if (!expected_uid_gid(open_tree_fd, FILE2, 0, 10000, 10000))
+ die("failure: check ownership");
+
+ if (!expected_uid_gid(open_tree_fd, CHRDEV1, 0, 10000, 10000))
+ die("failure: check ownership");
+
+ if (unlinkat(open_tree_fd, FILE1, 0))
+ die("failure: delete");
+
+ if (unlinkat(open_tree_fd, DIR1, AT_REMOVEDIR))
+ die("failure: delete");
+
+ if (unlinkat(open_tree_fd, FILE2, 0))
+ die("failure: delete");
+
+ if (unlinkat(open_tree_fd, CHRDEV1, 0))
+ die("failure: delete");
+
+ /* create tmpfile via filesystem tmpfile api */
+ if (supported) {
+ tmpfile_fd = openat(open_tree_fd, ".", O_TMPFILE | O_RDWR, S_IXGRP | S_ISGID);
+ if (tmpfile_fd < 0)
+ die("failure: create");
+ /* link the temporary file into the filesystem, making it permanent */
+ snprintf(path, PATH_MAX, "/proc/self/fd/%d", tmpfile_fd);
+ if (linkat(AT_FDCWD, path, open_tree_fd, FILE3, AT_SYMLINK_FOLLOW))
+ die("failure: linkat");
+ if (close(tmpfile_fd))
+ die("failure: close");
+ if (is_setgid(open_tree_fd, FILE3, 0))
+ die("failure: is_setgid");
+ if (is_ixgrp(open_tree_fd, FILE3, 0))
+ die("failure: is_ixgrp");
+ if (!expected_uid_gid(open_tree_fd, FILE3, 0, 10000, 10000))
+ die("failure: check ownership");
+ if (unlinkat(open_tree_fd, FILE3, 0))
+ die("failure: delete");
+ }
+
+ exit(EXIT_SUCCESS);
+ }
+ if (wait_for_pid(pid))
+ goto out;
+
+ pid = fork();
+ if (pid < 0) {
+ log_stderr("failure: fork");
+ goto out;
+ }
+ if (pid == 0) {
+ umask(S_IXGRP);
+ mode = umask(S_IXGRP);
+ if (!(mode & S_IXGRP))
+ die("failure: umask");
+
+ /* The group permissions correspond to the permissions of the
+ * ACL_GROUP_OBJ entry. Don't use setfacl to set ACL_MASK, so
+ * the group permissions is equal to ACL_GROUP_OBJ(g)
+ * entry(rwx). Also, umask doesn't affect group permissions
+ * because umask will be ignored if having acl.
+ */
+ snprintf(t_buf, sizeof(t_buf), "setfacl -d -m u::rwx,g::rwx,o::rwx, %s/%s", info->t_mountpoint, T_DIR1);
+ if (system(t_buf))
+ die("failure: system");
+
+ if (!switch_ids(10000, 11000))
+ die("failure: switch fsids");
+
+ /* create regular file via open() */
+ file1_fd = openat(open_tree_fd, FILE1, O_CREAT | O_EXCL | O_CLOEXEC, S_IXGRP | S_ISGID);
+ if (file1_fd < 0)
+ die("failure: create");
+
+ /* Neither in_group_p() nor capable_wrt_inode_uidgid() so setgid
+ * bit needs to be stripped.
+ */
+ if (is_setgid(open_tree_fd, FILE1, 0))
+ die("failure: is_setgid");
+
+ if (!is_ixgrp(open_tree_fd, FILE1, 0))
+ die("failure: is_ixgrp");
+
+ /* create directory */
+ if (mkdirat(open_tree_fd, DIR1, 0000))
+ die("failure: create");
+
+ if (xfs_irix_sgid_inherit_enabled(info->t_fstype)) {
+ /* We're not in_group_p(). */
+ if (is_setgid(open_tree_fd, DIR1, 0))
+ die("failure: is_setgid");
+ } else {
+ /* Directories always inherit the setgid bit. */
+ if (!is_setgid(open_tree_fd, DIR1, 0))
+ die("failure: is_setgid");
+ }
+
+ if (is_ixgrp(open_tree_fd, DIR1, 0))
+ die("failure: is_ixgrp");
+
+ /* create a special file via mknodat() vfs_create */
+ if (mknodat(open_tree_fd, FILE2, S_IFREG | S_ISGID | S_IXGRP, 0))
+ die("failure: mknodat");
+
+ if (is_setgid(open_tree_fd, FILE2, 0))
+ die("failure: is_setgid");
+
+ if (!is_ixgrp(open_tree_fd, FILE2, 0))
+ die("failure: is_ixgrp");
+
+ /* create a whiteout device via mknodat() vfs_mknod */
+ if (mknodat(open_tree_fd, CHRDEV1, S_IFCHR | S_ISGID | S_IXGRP, 0))
+ die("failure: mknodat");
+
+ if (is_setgid(open_tree_fd, CHRDEV1, 0))
+ die("failure: is_setgid");
+
+ if (!is_ixgrp(open_tree_fd, CHRDEV1, 0))
+ die("failure: is_ixgrp");
+
+ /*
+ * In setgid directories newly created files always inherit the
+ * gid from the parent directory. Verify that the file is owned
+ * by gid 10000, not by gid 11000.
+ */
+ if (!expected_uid_gid(open_tree_fd, FILE1, 0, 10000, 10000))
+ die("failure: check ownership");
+
+ /*
+ * In setgid directories newly created directories always
+ * inherit the gid from the parent directory. Verify that the
+ * directory is owned by gid 10000, not by gid 11000.
+ */
+ if (!expected_uid_gid(open_tree_fd, DIR1, 0, 10000, 10000))
+ die("failure: check ownership");
+
+ if (!expected_uid_gid(open_tree_fd, FILE2, 0, 10000, 10000))
+ die("failure: check ownership");
+
+ if (!expected_uid_gid(open_tree_fd, CHRDEV1, 0, 10000, 10000))
+ die("failure: check ownership");
+
+ if (unlinkat(open_tree_fd, FILE1, 0))
+ die("failure: delete");
+
+ if (unlinkat(open_tree_fd, DIR1, AT_REMOVEDIR))
+ die("failure: delete");
+
+ if (unlinkat(open_tree_fd, FILE2, 0))
+ die("failure: delete");
+
+ if (unlinkat(open_tree_fd, CHRDEV1, 0))
+ die("failure: delete");
+
+ /* create tmpfile via filesystem tmpfile api */
+ if (supported) {
+ tmpfile_fd = openat(open_tree_fd, ".", O_TMPFILE | O_RDWR, S_IXGRP | S_ISGID);
+ if (tmpfile_fd < 0)
+ die("failure: create");
+ /* link the temporary file into the filesystem, making it permanent */
+ snprintf(path, PATH_MAX, "/proc/self/fd/%d", tmpfile_fd);
+ if (linkat(AT_FDCWD, path, open_tree_fd, FILE3, AT_SYMLINK_FOLLOW))
+ die("failure: linkat");
+ if (close(tmpfile_fd))
+ die("failure: close");
+ if (is_setgid(open_tree_fd, FILE3, 0))
+ die("failure: is_setgid");
+ if (!is_ixgrp(open_tree_fd, FILE3, 0))
+ die("failure: is_ixgrp");
+ if (!expected_uid_gid(open_tree_fd, FILE3, 0, 10000, 10000))
+ die("failure: check ownership");
+ if (unlinkat(open_tree_fd, FILE3, 0))
+ die("failure: delete");
+ }
+
+ exit(EXIT_SUCCESS);
+ }
+ if (wait_for_pid(pid))
+ goto out;
+
+ fret = 0;
+ log_debug("Ran test");
+out:
+ safe_close(attr.userns_fd);
+ safe_close(file1_fd);
+ safe_close(open_tree_fd);
+
+ return fret;
+}
+
+/*
+ * If the parent directory has a default acl then permissions are based off
+ * of that and current_umask() is ignored. Specifically, if the ACL has an
+ * ACL_MASK entry, the group permissions correspond to the permissions of
+ * the ACL_MASK entry. Otherwise, if the ACL has no ACL_MASK entry, the
+ * group permissions correspond to the permissions of the ACL_GROUP_OBJ
+ * entry.
+ *
+ * Use setfacl to check whether inode strip S_ISGID works correctly under
+ * the above two situations when enabling userns and idmapped feature.
+ *
+ * Test for commit
+ * 1639a49ccdce ("fs: move S_ISGID stripping into the vfs_*() helpers").
+ */
+static int setgid_create_acl_idmapped_in_userns(const struct vfstest_info *info)
+{
+ int fret = -1;
+ int file1_fd = -EBADF, open_tree_fd = -EBADF;
+ struct mount_attr attr = {
+ .attr_set = MOUNT_ATTR_IDMAP,
+ };
+ pid_t pid;
+ int tmpfile_fd = -EBADF;
+ bool supported = false;
+ char path[PATH_MAX];
+ mode_t mode;
+
+ if (!caps_supported())
+ return 0;
+
+ if (fchmod(info->t_dir1_fd, S_IRUSR |
+ S_IWUSR |
+ S_IRGRP |
+ S_IWGRP |
+ S_IROTH |
+ S_IWOTH |
+ S_IXUSR |
+ S_IXGRP |
+ S_IXOTH |
+ S_ISGID), 0) {
+ log_stderr("failure: fchmod");
+ goto out;
+ }
+
+ /* Verify that the sid bits got raised. */
+ if (!is_setgid(info->t_dir1_fd, "", AT_EMPTY_PATH)) {
+ log_stderr("failure: is_setgid");
+ goto out;
+ }
+
+ /* Changing mount properties on a detached mount. */
+ attr.userns_fd = get_userns_fd(0, 10000, 10000);
+ if (attr.userns_fd < 0) {
+ log_stderr("failure: get_userns_fd");
+ goto out;
+ }
+
+ open_tree_fd = sys_open_tree(info->t_dir1_fd, "",
+ AT_EMPTY_PATH |
+ AT_NO_AUTOMOUNT |
+ AT_SYMLINK_NOFOLLOW |
+ OPEN_TREE_CLOEXEC |
+ OPEN_TREE_CLONE);
+ if (open_tree_fd < 0) {
+ log_stderr("failure: sys_open_tree");
+ goto out;
+ }
+
+ if (sys_mount_setattr(open_tree_fd, "", AT_EMPTY_PATH, &attr, sizeof(attr))) {
+ log_stderr("failure: sys_mount_setattr");
+ goto out;
+ }
+
+ supported = openat_tmpfile_supported(open_tree_fd);
+
+ /*
+ * Below we verify that setgid inheritance for a newly created file or
+ * directory works correctly. As part of this we need to verify that
+ * newly created files or directories inherit their gid from their
+ * parent directory. So we change the parent directorie's gid to 1000
+ * and create a file with fs{g,u}id 0 and verify that the newly created
+ * file and directory inherit gid 1000, not 0.
+ */
+ if (fchownat(info->t_dir1_fd, "", -1, 1000, AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH)) {
+ log_stderr("failure: fchownat");
+ goto out;
+ }
+
+ pid = fork();
+ if (pid < 0) {
+ log_stderr("failure: fork");
+ goto out;
+ }
+ if (pid == 0) {
+ umask(S_IXGRP);
+ mode = umask(S_IXGRP);
+ if (!(mode & S_IXGRP))
+ die("failure: umask");
+
+ /* The group permissions correspond to the permissions of the
+ * ACL_MASK entry. Use setfacl to set ACL mask(m) as rw, so now
+ * the group permissions is rw. Also, umask doesn't affect
+ * group permissions because umask will be ignored if having
+ * acl.
+ */
+ snprintf(t_buf, sizeof(t_buf), "setfacl -d -m u::rwx,g::rw,o::rwx,m:rw %s/%s", info->t_mountpoint, T_DIR1);
+ if (system(t_buf))
+ die("failure: system");
+
+ if (!caps_supported()) {
+ log_debug("skip: capability library not installed");
+ exit(EXIT_SUCCESS);
+ }
+
+ if (!switch_userns(attr.userns_fd, 0, 0, false))
+ die("failure: switch_userns");
+
+ if (!caps_down_fsetid())
+ die("failure: caps_down_fsetid");
+
+ /* create regular file via open() */
+ file1_fd = openat(open_tree_fd, FILE1, O_CREAT | O_EXCL | O_CLOEXEC, S_IXGRP | S_ISGID);
+ if (file1_fd < 0)
+ die("failure: create");
+
+ /* Neither in_group_p() nor capable_wrt_inode_uidgid() so setgid
+ * bit needs to be stripped.
+ */
+ if (is_setgid(open_tree_fd, FILE1, 0))
+ die("failure: is_setgid");
+
+ if (is_ixgrp(open_tree_fd, FILE1, 0))
+ die("failure: is_ixgrp");
+
+ /* create directory */
+ if (mkdirat(open_tree_fd, DIR1, 0000))
+ die("failure: create");
+
+ if (xfs_irix_sgid_inherit_enabled(info->t_fstype)) {
+ /* We're not in_group_p(). */
+ if (is_setgid(open_tree_fd, DIR1, 0))
+ die("failure: is_setgid");
+ } else {
+ /* Directories always inherit the setgid bit. */
+ if (!is_setgid(open_tree_fd, DIR1, 0))
+ die("failure: is_setgid");
+ }
+
+ if (is_ixgrp(open_tree_fd, DIR1, 0))
+ die("failure: is_ixgrp");
+
+ /* create a special file via mknodat() vfs_create */
+ if (mknodat(open_tree_fd, FILE2, S_IFREG | S_ISGID | S_IXGRP, 0))
+ die("failure: mknodat");
+
+ if (is_setgid(open_tree_fd, FILE2, 0))
+ die("failure: is_setgid");
+
+ if (is_ixgrp(open_tree_fd, FILE2, 0))
+ die("failure: is_ixgrp");
+
+ /* create a whiteout device via mknodat() vfs_mknod */
+ if (mknodat(open_tree_fd, CHRDEV1, S_IFCHR | S_ISGID | S_IXGRP, 0))
+ die("failure: mknodat");
+
+ if (is_setgid(open_tree_fd, CHRDEV1, 0))
+ die("failure: is_setgid");
+
+ if (is_ixgrp(open_tree_fd, CHRDEV1, 0))
+ die("failure: is_ixgrp");
+
+ /*
+ * In setgid directories newly created files always inherit the
+ * gid from the parent directory. Verify that the file is owned
+ * by gid 1000, not by gid 0.
+ */
+ if (!expected_uid_gid(open_tree_fd, FILE1, 0, 0, 1000))
+ die("failure: check ownership");
+
+ /*
+ * In setgid directories newly created directories always
+ * inherit the gid from the parent directory. Verify that the
+ * directory is owned by gid 1000, not by gid 0.
+ */
+ if (!expected_uid_gid(open_tree_fd, DIR1, 0, 0, 1000))
+ die("failure: check ownership");
+
+ if (!expected_uid_gid(open_tree_fd, FILE2, 0, 0, 1000))
+ die("failure: check ownership");
+
+ if (!expected_uid_gid(open_tree_fd, CHRDEV1, 0, 0, 1000))
+ die("failure: check ownership");
+
+ if (unlinkat(open_tree_fd, FILE1, 0))
+ die("failure: delete");
+
+ if (unlinkat(open_tree_fd, DIR1, AT_REMOVEDIR))
+ die("failure: delete");
+
+ if (unlinkat(open_tree_fd, FILE2, 0))
+ die("failure: delete");
+
+ if (unlinkat(open_tree_fd, CHRDEV1, 0))
+ die("failure: delete");
+
+ /* create tmpfile via filesystem tmpfile api */
+ if (supported) {
+ tmpfile_fd = openat(open_tree_fd, ".", O_TMPFILE | O_RDWR, S_IXGRP | S_ISGID);
+ if (tmpfile_fd < 0)
+ die("failure: create");
+ /* link the temporary file into the filesystem, making it permanent */
+ snprintf(path, PATH_MAX, "/proc/self/fd/%d", tmpfile_fd);
+ if (linkat(AT_FDCWD, path, open_tree_fd, FILE3, AT_SYMLINK_FOLLOW))
+ die("failure: linkat");
+ if (close(tmpfile_fd))
+ die("failure: close");
+ if (is_setgid(open_tree_fd, FILE3, 0))
+ die("failure: is_setgid");
+ if (is_ixgrp(open_tree_fd, FILE3, 0))
+ die("failure: is_ixgrp");
+ if (!expected_uid_gid(open_tree_fd, FILE3, 0, 0, 1000))
+ die("failure: check ownership");
+ if (unlinkat(open_tree_fd, FILE3, 0))
+ die("failure: delete");
+ }
+
+ exit(EXIT_SUCCESS);
+ }
+ if (wait_for_pid(pid))
+ goto out;
+
+ pid = fork();
+ if (pid < 0) {
+ log_stderr("failure: fork");
+ goto out;
+ }
+ if (pid == 0) {
+ umask(S_IXGRP);
+ mode = umask(S_IXGRP);
+ if (!(mode & S_IXGRP))
+ die("failure: umask");
+
+ /* The group permissions correspond to the permissions of the
+ * ACL_GROUP_OBJ entry. Don't use setfacl to set ACL_MASK, so
+ * the group permissions is equal to ACL_GROUP_OBJ(g)
+ * entry(rwx). Also, umask doesn't affect group permissions
+ * because umask will be ignored if having acl.
+ */
+ snprintf(t_buf, sizeof(t_buf), "setfacl -d -m u::rwx,g::rwx,o::rwx, %s/%s", info->t_mountpoint, T_DIR1);
+ if (system(t_buf))
+ die("failure: system");
+
+ if (!caps_supported()) {
+ log_debug("skip: capability library not installed");
+ exit(EXIT_SUCCESS);
+ }
+
+ if (!switch_userns(attr.userns_fd, 0, 0, false))
+ die("failure: switch_userns");
+
+ if (!caps_down_fsetid())
+ die("failure: caps_down_fsetid");
+
+ /* create regular file via open() */
+ file1_fd = openat(open_tree_fd, FILE1, O_CREAT | O_EXCL | O_CLOEXEC, S_IXGRP | S_ISGID);
+ if (file1_fd < 0)
+ die("failure: create");
+
+ /* Neither in_group_p() nor capable_wrt_inode_uidgid() so setgid
+ * bit needs to be stripped.
+ */
+ if (is_setgid(open_tree_fd, FILE1, 0))
+ die("failure: is_setgid");
+
+ if (!is_ixgrp(open_tree_fd, FILE1, 0))
+ die("failure: is_ixgrp");
+
+ /* create directory */
+ if (mkdirat(open_tree_fd, DIR1, 0000))
+ die("failure: create");
+
+ if (xfs_irix_sgid_inherit_enabled(info->t_fstype)) {
+ /* We're not in_group_p(). */
+ if (is_setgid(open_tree_fd, DIR1, 0))
+ die("failure: is_setgid");
+ } else {
+ /* Directories always inherit the setgid bit. */
+ if (!is_setgid(open_tree_fd, DIR1, 0))
+ die("failure: is_setgid");
+ }
+
+ if (is_ixgrp(open_tree_fd, DIR1, 0))
+ die("failure: is_ixgrp");
+
+ /* create a special file via mknodat() vfs_create */
+ if (mknodat(open_tree_fd, FILE2, S_IFREG | S_ISGID | S_IXGRP, 0))
+ die("failure: mknodat");
+
+ if (is_setgid(open_tree_fd, FILE2, 0))
+ die("failure: is_setgid");
+
+ if (!is_ixgrp(open_tree_fd, FILE2, 0))
+ die("failure: is_ixgrp");
+ /* create a whiteout device via mknodat() vfs_mknod */
+ if (mknodat(open_tree_fd, CHRDEV1, S_IFCHR | S_ISGID | S_IXGRP, 0))
+ die("failure: mknodat");
+
+ if (is_setgid(open_tree_fd, CHRDEV1, 0))
+ die("failure: is_setgid");
+
+ if (!is_ixgrp(open_tree_fd, CHRDEV1, 0))
+ die("failure: is_ixgrp");
+
+ /*
+ * In setgid directories newly created files always inherit the
+ * gid from the parent directory. Verify that the file is owned
+ * by gid 1000, not by gid 0.
+ */
+ if (!expected_uid_gid(open_tree_fd, FILE1, 0, 0, 1000))
+ die("failure: check ownership");
+
+ /*
+ * In setgid directories newly created directories always
+ * inherit the gid from the parent directory. Verify that the
+ * directory is owned by gid 1000, not by gid 0.
+ */
+ if (!expected_uid_gid(open_tree_fd, DIR1, 0, 0, 1000))
+ die("failure: check ownership");
+
+ if (!expected_uid_gid(open_tree_fd, FILE2, 0, 0, 1000))
+ die("failure: check ownership");
+
+ if (!expected_uid_gid(open_tree_fd, CHRDEV1, 0, 0, 1000))
+ die("failure: check ownership");
+
+ if (unlinkat(open_tree_fd, FILE1, 0))
+ die("failure: delete");
+
+ if (unlinkat(open_tree_fd, DIR1, AT_REMOVEDIR))
+ die("failure: delete");
+
+ if (unlinkat(open_tree_fd, FILE2, 0))
+ die("failure: delete");
+
+ if (unlinkat(open_tree_fd, CHRDEV1, 0))
+ die("failure: delete");
+
+ /* create tmpfile via filesystem tmpfile api */
+ if (supported) {
+ tmpfile_fd = openat(open_tree_fd, ".", O_TMPFILE | O_RDWR, S_IXGRP | S_ISGID);
+ if (tmpfile_fd < 0)
+ die("failure: create");
+ /* link the temporary file into the filesystem, making it permanent */
+ snprintf(path, PATH_MAX, "/proc/self/fd/%d", tmpfile_fd);
+ if (linkat(AT_FDCWD, path, open_tree_fd, FILE3, AT_SYMLINK_FOLLOW))
+ die("failure: linkat");
+ if (close(tmpfile_fd))
+ die("failure: close");
+ if (is_setgid(open_tree_fd, FILE3, 0))
+ die("failure: is_setgid");
+ if (!is_ixgrp(open_tree_fd, FILE3, 0))
+ die("failure: is_ixgrp");
+ if (!expected_uid_gid(open_tree_fd, FILE3, 0, 0, 1000))
+ die("failure: check ownership");
+ if (unlinkat(open_tree_fd, FILE3, 0))
+ die("failure: delete");
+ }
+
+ exit(EXIT_SUCCESS);
+ }
+ if (wait_for_pid(pid))
+ goto out;
+
+ fret = 0;
+ log_debug("Ran test");
+out:
+ safe_close(attr.userns_fd);
+ safe_close(file1_fd);
+ safe_close(open_tree_fd);
+
+ return fret;
+}
+
+static const struct test_struct t_idmapped_mounts[] = {
+ { tcore_acls, true, "posix acls on regular mounts", },
+ { tcore_create_in_userns, true, "create operations in user namespace", },
+ { tcore_device_node_in_userns, true, "device node in user namespace", },
+ { tcore_expected_uid_gid_idmapped_mounts, true, "expected ownership on idmapped mounts", },
+ { tcore_fscaps_idmapped_mounts, true, "fscaps on idmapped mounts", },
+ { tcore_fscaps_idmapped_mounts_in_userns, true, "fscaps on idmapped mounts in user namespace", },
+ { tcore_fscaps_idmapped_mounts_in_userns_separate_userns, true, "fscaps on idmapped mounts in user namespace with different id mappings", },
+ { tcore_fsids_mapped, true, "mapped fsids", },
+ { tcore_fsids_unmapped, true, "unmapped fsids", },
+ { tcore_hardlink_crossing_idmapped_mounts, true, "cross idmapped mount hardlink", },
+ { tcore_hardlink_from_idmapped_mount, true, "hardlinks from idmapped mounts", },
+ { tcore_hardlink_from_idmapped_mount_in_userns, true, "hardlinks from idmapped mounts in user namespace", },
+#ifdef HAVE_LIBURING_H
+ { tcore_io_uring_idmapped, true, "io_uring from idmapped mounts", },
+ { tcore_io_uring_idmapped_userns, true, "io_uring from idmapped mounts in user namespace", },
+ { tcore_io_uring_idmapped_unmapped, true, "io_uring from idmapped mounts with unmapped ids", },
+ { tcore_io_uring_idmapped_unmapped_userns, true, "io_uring from idmapped mounts with unmapped ids in user namespace", },
+#endif
+ { tcore_protected_symlinks_idmapped_mounts, true, "following protected symlinks on idmapped mounts", },
+ { tcore_protected_symlinks_idmapped_mounts_in_userns, true, "following protected symlinks on idmapped mounts in user namespace", },
+ { tcore_rename_crossing_idmapped_mounts, true, "cross idmapped mount rename", },
+ { tcore_rename_from_idmapped_mount, true, "rename from idmapped mounts", },
+ { tcore_rename_from_idmapped_mount_in_userns, true, "rename from idmapped mounts in user namespace", },
+ { tcore_setattr_truncate_idmapped, true, "setattr truncate on idmapped mounts", },
+ { tcore_setattr_truncate_idmapped_in_userns, true, "setattr truncate on idmapped mounts in user namespace", },
+ { tcore_setgid_create_idmapped, true, "create operations in directories with setgid bit set on idmapped mounts", },
+ { tcore_setgid_create_idmapped_in_userns, true, "create operations in directories with setgid bit set on idmapped mounts in user namespace", },
+ { tcore_setid_binaries_idmapped_mounts, true, "setid binaries on idmapped mounts", },
+ { tcore_setid_binaries_idmapped_mounts_in_userns, true, "setid binaries on idmapped mounts in user namespace", },
+ { tcore_setid_binaries_idmapped_mounts_in_userns_separate_userns, true, "setid binaries on idmapped mounts in user namespace with different id mappings", },
+ { tcore_sticky_bit_unlink_idmapped_mounts, true, "sticky bit unlink operations on idmapped mounts", },
+ { tcore_sticky_bit_unlink_idmapped_mounts_in_userns, true, "sticky bit unlink operations on idmapped mounts in user namespace", },
+ { tcore_sticky_bit_rename_idmapped_mounts, true, "sticky bit rename operations on idmapped mounts", },
+ { tcore_sticky_bit_rename_idmapped_mounts_in_userns, true, "sticky bit rename operations on idmapped mounts in user namespace", },
+ { tcore_symlink_idmapped_mounts, true, "symlink from idmapped mounts", },
+ { tcore_symlink_idmapped_mounts_in_userns, true, "symlink from idmapped mounts in user namespace", },
+};
+
+const struct test_suite s_idmapped_mounts = {
+ .tests = t_idmapped_mounts,
+ .nr_tests = ARRAY_SIZE(t_idmapped_mounts),
+};
+
+static const struct test_struct t_fscaps_in_ancestor_userns[] = {
+ { fscaps_idmapped_mounts_in_userns_valid_in_ancestor_userns, true, "fscaps on idmapped mounts in user namespace writing fscap valid in ancestor userns", },
+};
+
+const struct test_suite s_fscaps_in_ancestor_userns = {
+ .tests = t_fscaps_in_ancestor_userns,
+ .nr_tests = ARRAY_SIZE(t_fscaps_in_ancestor_userns),
+};
+
+static const struct test_struct t_nested_userns[] = {
+ { nested_userns, T_REQUIRE_IDMAPPED_MOUNTS, "test that nested user namespaces behave correctly when attached to idmapped mounts", },
+};
+
+const struct test_suite s_nested_userns = {
+ .tests = t_nested_userns,
+ .nr_tests = ARRAY_SIZE(t_nested_userns),
+};
+
+/* Test for commit 968219708108 ("fs: handle circular mappings correctly"). */
+static const struct test_struct t_setattr_fix_968219708108[] = {
+ { setattr_fix_968219708108, T_REQUIRE_IDMAPPED_MOUNTS, "test that setattr works correctly", },
+};
+
+const struct test_suite s_setattr_fix_968219708108 = {
+ .tests = t_setattr_fix_968219708108,
+ .nr_tests = ARRAY_SIZE(t_setattr_fix_968219708108),
+};
+
+/* Test for commit 705191b03d50 ("fs: fix acl translation"). */
+static const struct test_struct t_setxattr_fix_705191b03d50[] = {
+ { setxattr_fix_705191b03d50, T_REQUIRE_USERNS, "test that setxattr works correctly for userns mountable filesystems", },
+};
+
+const struct test_suite s_setxattr_fix_705191b03d50 = {
+ .tests = t_setxattr_fix_705191b03d50,
+ .nr_tests = ARRAY_SIZE(t_setxattr_fix_705191b03d50),
+};
+
+static const struct test_struct t_setgid_create_umask_idmapped_mounts[] = {
+ { setgid_create_umask_idmapped, T_REQUIRE_IDMAPPED_MOUNTS, "create operations by using umask in directories with setgid bit set on idmapped mount", },
+ { setgid_create_umask_idmapped_in_userns, T_REQUIRE_IDMAPPED_MOUNTS, "create operations by using umask in directories with setgid bit set on idmapped mount inside userns", },
+};
+
+const struct test_suite s_setgid_create_umask_idmapped_mounts = {
+ .tests = t_setgid_create_umask_idmapped_mounts,
+ .nr_tests = ARRAY_SIZE(t_setgid_create_umask_idmapped_mounts),
+};
+
+static const struct test_struct t_setgid_create_acl_idmapped_mounts[] = {
+ { setgid_create_acl_idmapped, T_REQUIRE_IDMAPPED_MOUNTS, "create operations by using acl in directories with setgid bit set on idmapped mount", },
+ { setgid_create_acl_idmapped_in_userns, T_REQUIRE_IDMAPPED_MOUNTS, "create operations by using acl in directories with setgid bit set on idmapped mount inside userns", },
+};
+
+const struct test_suite s_setgid_create_acl_idmapped_mounts = {
+ .tests = t_setgid_create_acl_idmapped_mounts,
+ .nr_tests = ARRAY_SIZE(t_setgid_create_acl_idmapped_mounts),
};
extern const struct test_suite s_nested_userns;
extern const struct test_suite s_setattr_fix_968219708108;
extern const struct test_suite s_setxattr_fix_705191b03d50;
+extern const struct test_suite s_setgid_create_umask_idmapped_mounts;
+extern const struct test_suite s_setgid_create_acl_idmapped_mounts;
+
+/* Core tests */
+int tcore_acls(const struct vfstest_info *info);
+int tcore_create_in_userns(const struct vfstest_info *info);
+int tcore_device_node_in_userns(const struct vfstest_info *info);
+int tcore_fsids_mapped(const struct vfstest_info *info);
+int tcore_fsids_unmapped(const struct vfstest_info *info);
+int tcore_expected_uid_gid_idmapped_mounts(const struct vfstest_info *info);
+int tcore_fscaps_idmapped_mounts(const struct vfstest_info *info);
+int tcore_fscaps_idmapped_mounts_in_userns(const struct vfstest_info *info);
+int tcore_fscaps_idmapped_mounts_in_userns_separate_userns(const struct vfstest_info *info);
+int tcore_hardlink_crossing_idmapped_mounts(const struct vfstest_info *info);
+int tcore_hardlink_from_idmapped_mount(const struct vfstest_info *info);
+int tcore_hardlink_from_idmapped_mount_in_userns(const struct vfstest_info *info);
+#ifdef HAVE_LIBURING_H
+int tcore_io_uring_idmapped(const struct vfstest_info *info);
+int tcore_io_uring_idmapped_userns(const struct vfstest_info *info);
+int tcore_io_uring_idmapped_unmapped(const struct vfstest_info *info);
+int tcore_io_uring_idmapped_unmapped_userns(const struct vfstest_info *info);
+#endif
+int tcore_protected_symlinks_idmapped_mounts(const struct vfstest_info *info);
+int tcore_protected_symlinks_idmapped_mounts_in_userns(const struct vfstest_info *info);
+int tcore_rename_crossing_idmapped_mounts(const struct vfstest_info *info);
+int tcore_rename_from_idmapped_mount(const struct vfstest_info *info);
+int tcore_rename_from_idmapped_mount_in_userns(const struct vfstest_info *info);
+int tcore_setattr_truncate_idmapped(const struct vfstest_info *info);
+int tcore_setattr_truncate_idmapped_in_userns(const struct vfstest_info *info);
+int tcore_setgid_create_idmapped(const struct vfstest_info *info);
+int tcore_setgid_create_idmapped_in_userns(const struct vfstest_info *info);
+int tcore_setid_binaries_idmapped_mounts(const struct vfstest_info *info);
+int tcore_setid_binaries_idmapped_mounts_in_userns(const struct vfstest_info *info);
+int tcore_setid_binaries_idmapped_mounts_in_userns_separate_userns(const struct vfstest_info *info);
+int tcore_sticky_bit_unlink_idmapped_mounts(const struct vfstest_info *info);
+int tcore_sticky_bit_unlink_idmapped_mounts_in_userns(const struct vfstest_info *info);
+int tcore_sticky_bit_rename_idmapped_mounts(const struct vfstest_info *info);
+int tcore_sticky_bit_rename_idmapped_mounts_in_userns(const struct vfstest_info *info);
+int tcore_symlink_idmapped_mounts(const struct vfstest_info *info);
+int tcore_symlink_idmapped_mounts_in_userns(const struct vfstest_info *info);
#endif /* __IDMAPPED_MOUNTS_H */
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include "../global.h"
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <grp.h>
+#include <limits.h>
+#include <linux/limits.h>
+#include <linux/types.h>
+#include <pthread.h>
+#include <pwd.h>
+#include <sched.h>
+#include <stdbool.h>
+#include <sys/fsuid.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/xattr.h>
+#include <unistd.h>
+
+#include "missing.h"
+#include "utils.h"
+#include "vfstest.h"
+#include "idmapped-mounts.h"
+
+static int tmpfs_nested_mount_setup(const struct vfstest_info *info, int (*test)(const struct vfstest_info *info))
+{
+ char path[PATH_MAX];
+ int fret = -1;
+ struct vfstest_info nested_test_info = *info;
+
+ /* Create mapping for userns
+ * Make the mapping quite long, so all nested userns that are created by
+ * any test we call is contained here (otherwise userns creation fails).
+ */
+ struct mount_attr attr = {
+ .attr_set = MOUNT_ATTR_IDMAP,
+ .userns_fd = -EBADF,
+ };
+ attr.userns_fd = get_userns_fd(0, 10000, 200000);
+ if (attr.userns_fd < 0) {
+ log_stderr("failure: get_userns_fd");
+ goto out_close;
+ }
+
+ if (!switch_userns(attr.userns_fd, 0, 0, false)) {
+ log_stderr("failure: switch_userns");
+ goto out_close;
+ }
+
+ /* create separate mount namespace */
+ if (unshare(CLONE_NEWNS)) {
+ log_stderr("failure: create new mount namespace");
+ goto out_close;
+ }
+
+ /* We don't want this mount in the parent mount ns */
+ if (sys_mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, 0)) {
+ log_stderr("failure: mount");
+ goto out_close;
+ }
+
+ /* Create DIR0 to mount there */
+ if (mkdirat(info->t_mnt_fd, DIR0, 0777)) {
+ log_stderr("failure: mkdirat");
+ goto out_close;
+ }
+ if (fchmodat(info->t_mnt_fd, DIR0, 0777, 0)) {
+ log_stderr("failure: fchmodat");
+ goto out_rm;
+ }
+
+ snprintf(path, sizeof(path), "%s/%s", info->t_mountpoint, DIR0);
+ if (sys_mount("tmpfs", path, "tmpfs", 0, NULL)) {
+ log_stderr("failure: mount");
+ goto out_rm;
+ }
+
+ // Create a new info to use for the test we will call.
+ nested_test_info = *info;
+ nested_test_info.t_mountpoint = strdup(path);
+ if (!nested_test_info.t_mountpoint) {
+ log_stderr("failure: strdup");
+ goto out;
+ }
+ nested_test_info.t_mnt_fd = openat(-EBADF, nested_test_info.t_mountpoint, O_CLOEXEC | O_DIRECTORY);
+ if (nested_test_info.t_mnt_fd < 0) {
+ log_stderr("failure: openat");
+ goto out;
+ }
+
+ test_setup(&nested_test_info);
+
+ // Run the test.
+ if ((*test)(&nested_test_info)) {
+ log_stderr("failure: calling test");
+ goto out;
+ }
+
+ test_cleanup(&nested_test_info);
+
+ fret = 0;
+ log_debug("Ran test");
+out:
+ snprintf(path, sizeof(path), "%s/" DIR0, info->t_mountpoint);
+ sys_umount2(path, MNT_DETACH);
+out_rm:
+ if (rm_r(info->t_mnt_fd, DIR0))
+ log_stderr("failure: rm_r");
+out_close:
+ safe_close(attr.userns_fd);
+ return fret;
+}
+
+static int tmpfs_acls(const struct vfstest_info *info)
+{
+ return tmpfs_nested_mount_setup(info, tcore_acls);
+}
+static int tmpfs_create_in_userns(const struct vfstest_info *info)
+{
+ return tmpfs_nested_mount_setup(info, tcore_create_in_userns);
+}
+static int tmpfs_device_node_in_userns(const struct vfstest_info *info)
+{
+ return tmpfs_nested_mount_setup(info, tcore_device_node_in_userns);
+}
+static int tmpfs_fsids_mapped(const struct vfstest_info *info)
+{
+ return tmpfs_nested_mount_setup(info, tcore_fsids_mapped);
+}
+static int tmpfs_fsids_unmapped(const struct vfstest_info *info)
+{
+ return tmpfs_nested_mount_setup(info, tcore_fsids_unmapped);
+}
+static int tmpfs_expected_uid_gid_idmapped_mounts(const struct vfstest_info *info)
+{
+ return tmpfs_nested_mount_setup(info, tcore_expected_uid_gid_idmapped_mounts);
+}
+static int tmpfs_fscaps_idmapped_mounts(const struct vfstest_info *info)
+{
+ return tmpfs_nested_mount_setup(info, tcore_fscaps_idmapped_mounts);
+}
+static int tmpfs_fscaps_idmapped_mounts_in_userns(const struct vfstest_info *info)
+{
+ return tmpfs_nested_mount_setup(info, tcore_fscaps_idmapped_mounts_in_userns);
+}
+static int tmpfs_fscaps_idmapped_mounts_in_userns_separate_userns(const struct vfstest_info *info)
+{
+ return tmpfs_nested_mount_setup(info, tcore_fscaps_idmapped_mounts_in_userns_separate_userns);
+}
+
+static int tmpfs_hardlink_crossing_idmapped_mounts(const struct vfstest_info *info)
+{
+ return tmpfs_nested_mount_setup(info, tcore_hardlink_crossing_idmapped_mounts);
+}
+static int tmpfs_hardlink_from_idmapped_mount(const struct vfstest_info *info)
+{
+ return tmpfs_nested_mount_setup(info, tcore_hardlink_from_idmapped_mount);
+}
+static int tmpfs_hardlink_from_idmapped_mount_in_userns(const struct vfstest_info *info)
+{
+ return tmpfs_nested_mount_setup(info, tcore_hardlink_from_idmapped_mount_in_userns);
+}
+
+#ifdef HAVE_LIBURING_H
+static int tmpfs_io_uring_idmapped(const struct vfstest_info *info)
+{
+ return tmpfs_nested_mount_setup(info, tcore_io_uring_idmapped);
+}
+static int tmpfs_io_uring_idmapped_userns(const struct vfstest_info *info)
+{
+ return tmpfs_nested_mount_setup(info, tcore_io_uring_idmapped_userns);
+}
+static int tmpfs_io_uring_idmapped_unmapped(const struct vfstest_info *info)
+{
+ return tmpfs_nested_mount_setup(info, tcore_io_uring_idmapped_unmapped);
+}
+static int tmpfs_io_uring_idmapped_unmapped_userns(const struct vfstest_info *info)
+{
+ return tmpfs_nested_mount_setup(info, tcore_io_uring_idmapped_unmapped_userns);
+}
+#endif /* HAVE_LIBURING_H */
+
+static int tmpfs_protected_symlinks_idmapped_mounts(const struct vfstest_info *info)
+{
+ return tmpfs_nested_mount_setup(info, tcore_protected_symlinks_idmapped_mounts);
+}
+static int tmpfs_protected_symlinks_idmapped_mounts_in_userns(const struct vfstest_info *info)
+{
+ return tmpfs_nested_mount_setup(info, tcore_protected_symlinks_idmapped_mounts_in_userns);
+}
+static int tmpfs_rename_crossing_idmapped_mounts(const struct vfstest_info *info)
+{
+ return tmpfs_nested_mount_setup(info, tcore_rename_crossing_idmapped_mounts);
+}
+static int tmpfs_rename_from_idmapped_mount(const struct vfstest_info *info)
+{
+ return tmpfs_nested_mount_setup(info, tcore_rename_from_idmapped_mount);
+}
+static int tmpfs_rename_from_idmapped_mount_in_userns(const struct vfstest_info *info)
+{
+ return tmpfs_nested_mount_setup(info, tcore_rename_from_idmapped_mount_in_userns);
+}
+static int tmpfs_setattr_truncate_idmapped(const struct vfstest_info *info)
+{
+ return tmpfs_nested_mount_setup(info, tcore_setattr_truncate_idmapped);
+}
+static int tmpfs_setattr_truncate_idmapped_in_userns(const struct vfstest_info *info)
+{
+ return tmpfs_nested_mount_setup(info, tcore_setattr_truncate_idmapped_in_userns);
+}
+static int tmpfs_setgid_create_idmapped(const struct vfstest_info *info)
+{
+ return tmpfs_nested_mount_setup(info, tcore_setgid_create_idmapped);
+}
+static int tmpfs_setgid_create_idmapped_in_userns(const struct vfstest_info *info)
+{
+ return tmpfs_nested_mount_setup(info, tcore_setgid_create_idmapped_in_userns);
+}
+static int tmpfs_setid_binaries_idmapped_mounts(const struct vfstest_info *info)
+{
+ return tmpfs_nested_mount_setup(info, tcore_setid_binaries_idmapped_mounts);
+}
+static int tmpfs_setid_binaries_idmapped_mounts_in_userns(const struct vfstest_info *info)
+{
+ return tmpfs_nested_mount_setup(info, tcore_setid_binaries_idmapped_mounts_in_userns);
+}
+static int tmpfs_setid_binaries_idmapped_mounts_in_userns_separate_userns(const struct vfstest_info *info)
+{
+ return tmpfs_nested_mount_setup(info, tcore_setid_binaries_idmapped_mounts_in_userns_separate_userns);
+}
+static int tmpfs_sticky_bit_unlink_idmapped_mounts(const struct vfstest_info *info)
+{
+ return tmpfs_nested_mount_setup(info, tcore_sticky_bit_unlink_idmapped_mounts);
+}
+static int tmpfs_sticky_bit_unlink_idmapped_mounts_in_userns(const struct vfstest_info *info)
+{
+ return tmpfs_nested_mount_setup(info, tcore_sticky_bit_unlink_idmapped_mounts_in_userns);
+}
+static int tmpfs_sticky_bit_rename_idmapped_mounts(const struct vfstest_info *info)
+{
+ return tmpfs_nested_mount_setup(info, tcore_sticky_bit_rename_idmapped_mounts);
+}
+static int tmpfs_sticky_bit_rename_idmapped_mounts_in_userns(const struct vfstest_info *info)
+{
+ return tmpfs_nested_mount_setup(info, tcore_sticky_bit_rename_idmapped_mounts_in_userns);
+}
+static int tmpfs_symlink_idmapped_mounts(const struct vfstest_info *info)
+{
+ return tmpfs_nested_mount_setup(info, tcore_symlink_idmapped_mounts);
+}
+static int tmpfs_symlink_idmapped_mounts_in_userns(const struct vfstest_info *info)
+{
+ return tmpfs_nested_mount_setup(info, tcore_symlink_idmapped_mounts_in_userns);
+}
+
+static const struct test_struct t_tmpfs[] = {
+ { tmpfs_acls, T_REQUIRE_USERNS | T_REQUIRE_IDMAPPED_MOUNTS, "tmpfs create operations in user namespace", },
+ { tmpfs_create_in_userns, T_REQUIRE_USERNS | T_REQUIRE_IDMAPPED_MOUNTS, "tmpfs create operations in user namespace", },
+ { tmpfs_device_node_in_userns, T_REQUIRE_USERNS | T_REQUIRE_IDMAPPED_MOUNTS, "tmpfs device node in user namespace", },
+ { tmpfs_expected_uid_gid_idmapped_mounts, T_REQUIRE_USERNS | T_REQUIRE_IDMAPPED_MOUNTS, "tmpfs expected ownership on idmapped mounts", },
+ { tmpfs_fscaps_idmapped_mounts, T_REQUIRE_USERNS | T_REQUIRE_IDMAPPED_MOUNTS, "tmpfs fscaps on idmapped mounts", },
+ { tmpfs_fscaps_idmapped_mounts_in_userns, T_REQUIRE_USERNS | T_REQUIRE_IDMAPPED_MOUNTS, "tmpfs fscaps on idmapped mounts in user namespace", },
+ { tmpfs_fscaps_idmapped_mounts_in_userns_separate_userns, T_REQUIRE_USERNS | T_REQUIRE_IDMAPPED_MOUNTS, "tmpfs fscaps on idmapped mounts in user namespace with different id mappings", },
+ { tmpfs_fsids_mapped, T_REQUIRE_USERNS | T_REQUIRE_IDMAPPED_MOUNTS, "tmpfs mapped fsids", },
+ { tmpfs_fsids_unmapped, T_REQUIRE_USERNS | T_REQUIRE_IDMAPPED_MOUNTS, "tmpfs unmapped fsids", },
+ { tmpfs_hardlink_crossing_idmapped_mounts, T_REQUIRE_USERNS | T_REQUIRE_IDMAPPED_MOUNTS, "tmpfs cross idmapped mount hardlink", },
+ { tmpfs_hardlink_from_idmapped_mount, T_REQUIRE_USERNS | T_REQUIRE_IDMAPPED_MOUNTS, "tmpfs hardlinks from idmapped mounts", },
+ { tmpfs_hardlink_from_idmapped_mount_in_userns, T_REQUIRE_USERNS | T_REQUIRE_IDMAPPED_MOUNTS, "tmpfs hardlinks from idmapped mounts in user namespace", },
+#ifdef HAVE_LIBURING_H
+ { tmpfs_io_uring_idmapped, T_REQUIRE_USERNS | T_REQUIRE_IDMAPPED_MOUNTS, "tmpfs io_uring from idmapped mounts", },
+ { tmpfs_io_uring_idmapped_userns, T_REQUIRE_USERNS | T_REQUIRE_IDMAPPED_MOUNTS, "tmpfs io_uring from idmapped mounts in user namespace", },
+ { tmpfs_io_uring_idmapped_unmapped, T_REQUIRE_USERNS | T_REQUIRE_IDMAPPED_MOUNTS, "tmpfs io_uring from idmapped mounts with unmapped ids", },
+ { tmpfs_io_uring_idmapped_unmapped_userns, T_REQUIRE_USERNS | T_REQUIRE_IDMAPPED_MOUNTS, "tmpfs io_uring from idmapped mounts with unmapped ids in user namespace", },
+#endif
+ { tmpfs_protected_symlinks_idmapped_mounts, T_REQUIRE_USERNS | T_REQUIRE_IDMAPPED_MOUNTS, "tmpfs following protected symlinks on idmapped mounts", },
+ { tmpfs_protected_symlinks_idmapped_mounts_in_userns, T_REQUIRE_USERNS | T_REQUIRE_IDMAPPED_MOUNTS, "tmpfs following protected symlinks on idmapped mounts in user namespace", },
+ { tmpfs_rename_crossing_idmapped_mounts, T_REQUIRE_USERNS | T_REQUIRE_IDMAPPED_MOUNTS, "tmpfs cross idmapped mount rename", },
+ { tmpfs_rename_from_idmapped_mount, T_REQUIRE_USERNS | T_REQUIRE_IDMAPPED_MOUNTS, "tmpfs rename from idmapped mounts", },
+ { tmpfs_rename_from_idmapped_mount_in_userns, T_REQUIRE_USERNS | T_REQUIRE_IDMAPPED_MOUNTS, "tmpfs rename from idmapped mounts in user namespace", },
+ { tmpfs_setattr_truncate_idmapped, T_REQUIRE_USERNS | T_REQUIRE_IDMAPPED_MOUNTS, "tmpfs setattr truncate on idmapped mounts", },
+ { tmpfs_setattr_truncate_idmapped_in_userns, T_REQUIRE_USERNS | T_REQUIRE_IDMAPPED_MOUNTS, "tmpfs setattr truncate on idmapped mounts in user namespace", },
+ { tmpfs_setgid_create_idmapped, T_REQUIRE_USERNS | T_REQUIRE_IDMAPPED_MOUNTS, "tmpfs create operations in directories with setgid bit set on idmapped mounts", },
+ { tmpfs_setgid_create_idmapped_in_userns, T_REQUIRE_USERNS | T_REQUIRE_IDMAPPED_MOUNTS, "tmpfs create operations in directories with setgid bit set on idmapped mounts in user namespace", },
+ { tmpfs_setid_binaries_idmapped_mounts, T_REQUIRE_USERNS | T_REQUIRE_IDMAPPED_MOUNTS, "tmpfs setid binaries on idmapped mounts", },
+ { tmpfs_setid_binaries_idmapped_mounts_in_userns, T_REQUIRE_USERNS | T_REQUIRE_IDMAPPED_MOUNTS, "tmpfs setid binaries on idmapped mounts in user namespace", },
+ { tmpfs_setid_binaries_idmapped_mounts_in_userns_separate_userns, T_REQUIRE_USERNS | T_REQUIRE_IDMAPPED_MOUNTS, "tmpfs setid binaries on idmapped mounts in user namespace with different id mappings", },
+ { tmpfs_sticky_bit_unlink_idmapped_mounts, T_REQUIRE_USERNS | T_REQUIRE_IDMAPPED_MOUNTS, "tmpfs sticky bit unlink operations on idmapped mounts", },
+ { tmpfs_sticky_bit_unlink_idmapped_mounts_in_userns, T_REQUIRE_USERNS | T_REQUIRE_IDMAPPED_MOUNTS, "tmpfs sticky bit unlink operations on idmapped mounts in user namespace", },
+ { tmpfs_sticky_bit_rename_idmapped_mounts, T_REQUIRE_USERNS | T_REQUIRE_IDMAPPED_MOUNTS, "tmpfs sticky bit rename operations on idmapped mounts", },
+ { tmpfs_sticky_bit_rename_idmapped_mounts_in_userns, T_REQUIRE_USERNS | T_REQUIRE_IDMAPPED_MOUNTS, "tmpfs sticky bit rename operations on idmapped mounts in user namespace", },
+ { tmpfs_symlink_idmapped_mounts, T_REQUIRE_USERNS | T_REQUIRE_IDMAPPED_MOUNTS, "tmpfs symlink from idmapped mounts", },
+ { tmpfs_symlink_idmapped_mounts_in_userns, T_REQUIRE_USERNS | T_REQUIRE_IDMAPPED_MOUNTS, "tmpfs symlink from idmapped mounts in user namespace", },
+};
+
+
+const struct test_suite s_tmpfs_idmapped_mounts = {
+ .tests = t_tmpfs,
+ .nr_tests = ARRAY_SIZE(t_tmpfs),
+};
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __TMPFS_IDMAPPED_MOUNTS_H
+#define __TMPFS_IDMAPPED_MOUNTS_H
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include "utils.h"
+
+extern const struct test_suite s_tmpfs_idmapped_mounts;
+
+#endif /* __TMPFS_IDMAPPED_MOUNTS_H */
#include <stdlib.h>
#include <sys/eventfd.h>
#include <sys/fsuid.h>
-#include <sys/mount.h>
#include <sys/prctl.h>
#include <sys/socket.h>
#include <sys/stat.h>
static int get_userns_fd_cb(void *data)
{
- return 0;
+ for (;;)
+ pause();
+ _exit(0);
}
int wait_for_pid(pid_t pid)
fret = 0;
out:
- if (fd >= 0)
- close(fd);
- if (setgroups_fd >= 0)
- close(setgroups_fd);
+ safe_close(fd);
+ safe_close(setgroups_fd);
return fret;
}
if (setresuid(uid, uid, uid))
return syserror("failure: setresuid");
+ /* Ensure we can access proc files from processes we can ptrace. */
+ if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0))
+ return syserror("failure: make dumpable");
+
return true;
}
if (c == '1') {
if (!switch_ids(0, 0))
return syserror("failure: switch ids to 0");
-
- /* Ensure we can access proc files from processes we can ptrace. */
- ret = prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
- if (ret < 0)
- return syserror("failure: make dumpable");
}
ret = write_nointr(h->fd_event, "1", 1);
return (st.st_mode & S_ISVTX) > 0;
}
+/*is_ixgrp - check whether file or directory is S_IXGRP */
+bool is_ixgrp(int dfd, const char *path, int flags)
+{
+ int ret;
+ struct stat st;
+
+ ret = fstatat(dfd, path, &st, flags);
+ if (ret < 0)
+ return false;
+
+ errno = 0; /* Don't report misleading errno. */
+ return (st.st_mode & S_IXGRP);
+}
+
bool switch_resids(uid_t uid, gid_t gid)
{
if (setresgid(gid, gid, gid))
fd = openat(dirfd, ".", O_TMPFILE | O_RDWR, S_IXGRP | S_ISGID);
if (fd == -1) {
- if (errno == ENOTSUP)
+ if (errno == ENOTSUP) {
+ errno = 0; /* Don't report misleading errno. */
return false;
- else
+ } else {
return log_errno(false, "failure: create");
+ }
}
if (close(fd))
#define DIR2 "dir2"
#define DIR3 "dir3"
#define DIR1_RENAME "dir1_rename"
+// This directory may be used by tests that call another test.
+#define DIR0 "dir0"
#define HARDLINK1 "hardlink1"
#define SYMLINK1 "symlink1"
#define SYMLINK_USER1 "symlink_user1"
struct vfstest_info {
uid_t t_overflowuid;
gid_t t_overflowgid;
- /* path of the test device */
+ /* Filesystem type of the mountpoint */
const char *t_fstype;
/* path of the test device */
const char *t_device;
extern bool is_setid(int dfd, const char *path, int flags);
extern bool is_setgid(int dfd, const char *path, int flags);
extern bool is_sticky(int dfd, const char *path, int flags);
+extern bool is_ixgrp(int dfd, const char *path, int flags);
extern bool openat_tmpfile_supported(int dirfd);
#endif /* __IDMAP_UTILS_H */
#include <unistd.h>
#include "btrfs-idmapped-mounts.h"
+#include "tmpfs-idmapped-mounts.h"
#include "idmapped-mounts.h"
#include "missing.h"
#include "utils.h"
+static char t_buf[PATH_MAX];
+
static void init_vfstest_info(struct vfstest_info *info)
{
info->t_overflowuid = 65534;
info->t_overflowgid = atoi(buf);
}
-static void test_setup(struct vfstest_info *info)
+void test_setup(struct vfstest_info *info)
{
if (mkdirat(info->t_mnt_fd, T_DIR1, 0777))
die("failure: mkdirat");
die("failure: fchmod");
}
-static void test_cleanup(struct vfstest_info *info)
+void test_cleanup(struct vfstest_info *info)
{
safe_close(info->t_dir1_fd);
if (rm_r(info->t_mnt_fd, T_DIR1))
int fret = -1;
int file1_fd = -EBADF, open_tree_fd = -EBADF;
- if (chown_r(info->t_mnt_fd, T_DIR1, 10000, 10000)) {
+ if (chown_r(info->t_mnt_fd, T_DIR1, 10000, 10000)) {
log_stderr("failure: chown_r");
goto out;
}
return fret;
}
+/* The current_umask() is stripped from the mode directly in the vfs if the
+ * filesystem either doesn't support acls or the filesystem has been
+ * mounted without posic acl support.
+ *
+ * If the filesystem does support acls then current_umask() stripping is
+ * deferred to posix_acl_create(). So when the filesystem calls
+ * posix_acl_create() and there are no acls set or not supported then
+ * current_umask() will be stripped.
+ *
+ * Use umask(S_IXGRP) to check whether inode strip S_ISGID works correctly.
+ *
+ * test for commit ac6800e279a2 ("fs: Add missing umask strip in vfs_tmpfile")
+ * and 1639a49ccdce ("fs: move S_ISGID stripping into the vfs_*() helpers").
+ */
+static int setgid_create_umask(const struct vfstest_info *info)
+{
+ int fret = -1;
+ int file1_fd = -EBADF;
+ int tmpfile_fd = -EBADF;
+ pid_t pid;
+ bool supported = false;
+ mode_t mode;
+
+ if (!caps_supported())
+ return 0;
+
+ if (fchmod(info->t_dir1_fd, S_IRUSR |
+ S_IWUSR |
+ S_IRGRP |
+ S_IWGRP |
+ S_IROTH |
+ S_IWOTH |
+ S_IXUSR |
+ S_IXGRP |
+ S_IXOTH |
+ S_ISGID), 0) {
+ log_stderr("failure: fchmod");
+ goto out;
+ }
+
+ /* Verify that the setgid bit got raised. */
+ if (!is_setgid(info->t_dir1_fd, "", AT_EMPTY_PATH)) {
+ log_stderr("failure: is_setgid");
+ goto out;
+ }
+
+ supported = openat_tmpfile_supported(info->t_dir1_fd);
+
+ pid = fork();
+ if (pid < 0) {
+ log_stderr("failure: fork");
+ goto out;
+ }
+ if (pid == 0) {
+ umask(S_IXGRP);
+ mode = umask(S_IXGRP);
+ if (!(mode & S_IXGRP))
+ die("failure: umask");
+
+ if (!switch_ids(0, 10000))
+ die("failure: switch_ids");
+
+ if (!caps_down_fsetid())
+ die("failure: caps_down_fsetid");
+
+ /* create regular file via open() */
+ file1_fd = openat(info->t_dir1_fd, FILE1, O_CREAT | O_EXCL | O_CLOEXEC, S_IXGRP | S_ISGID);
+ if (file1_fd < 0)
+ die("failure: create");
+
+ /* Neither in_group_p() nor capable_wrt_inode_uidgid() so setgid
+ * bit needs to be stripped.
+ */
+ if (is_setgid(info->t_dir1_fd, FILE1, 0))
+ die("failure: is_setgid");
+
+ if (is_ixgrp(info->t_dir1_fd, FILE1, 0))
+ die("failure: is_ixgrp");
+
+ if (mkdirat(info->t_dir1_fd, DIR1, 0000))
+ die("failure: create");
+
+ if (xfs_irix_sgid_inherit_enabled(info->t_fstype)) {
+ /* We're not in_group_p(). */
+ if (is_setgid(info->t_dir1_fd, DIR1, 0))
+ die("failure: is_setgid");
+ } else {
+ /* Directories always inherit the setgid bit. */
+ if (!is_setgid(info->t_dir1_fd, DIR1, 0))
+ die("failure: is_setgid");
+ }
+
+ if (is_ixgrp(info->t_dir1_fd, DIR1, 0))
+ die("failure: is_ixgrp");
+
+ /* create a special file via mknodat() vfs_create */
+ if (mknodat(info->t_dir1_fd, FILE2, S_IFREG | S_ISGID | S_IXGRP, 0))
+ die("failure: mknodat");
+
+ if (is_setgid(info->t_dir1_fd, FILE2, 0))
+ die("failure: is_setgid");
+
+ if (is_ixgrp(info->t_dir1_fd, FILE2, 0))
+ die("failure: is_ixgrp");
+
+ /* create a character device via mknodat() vfs_mknod */
+ if (mknodat(info->t_dir1_fd, CHRDEV1, S_IFCHR | S_ISGID | S_IXGRP, makedev(5, 1)))
+ die("failure: mknodat");
+
+ if (is_setgid(info->t_dir1_fd, CHRDEV1, 0))
+ die("failure: is_setgid");
+
+ if (is_ixgrp(info->t_dir1_fd, CHRDEV1, 0))
+ die("failure: is_ixgrp");
+
+ /*
+ * In setgid directories newly created files always inherit the
+ * gid from the parent directory. Verify that the file is owned
+ * by gid 0, not by gid 10000.
+ */
+ if (!expected_uid_gid(info->t_dir1_fd, FILE1, 0, 0, 0))
+ die("failure: check ownership");
+
+ /*
+ * In setgid directories newly created directories always
+ * inherit the gid from the parent directory. Verify that the
+ * directory is owned by gid 0, not by gid 10000.
+ */
+ if (!expected_uid_gid(info->t_dir1_fd, DIR1, 0, 0, 0))
+ die("failure: check ownership");
+
+ if (!expected_uid_gid(info->t_dir1_fd, FILE2, 0, 0, 0))
+ die("failure: check ownership");
+
+ if (!expected_uid_gid(info->t_dir1_fd, CHRDEV1, 0, 0, 0))
+ die("failure: check ownership");
+
+ if (unlinkat(info->t_dir1_fd, FILE1, 0))
+ die("failure: delete");
+
+ if (unlinkat(info->t_dir1_fd, DIR1, AT_REMOVEDIR))
+ die("failure: delete");
+
+ if (unlinkat(info->t_dir1_fd, FILE2, 0))
+ die("failure: delete");
+
+ if (unlinkat(info->t_dir1_fd, CHRDEV1, 0))
+ die("failure: delete");
+
+ /* create tmpfile via filesystem tmpfile api */
+ if (supported) {
+ tmpfile_fd = openat(info->t_dir1_fd, ".", O_TMPFILE | O_RDWR, S_IXGRP | S_ISGID);
+ if (tmpfile_fd < 0)
+ die("failure: create");
+ /* link the temporary file into the filesystem, making it permanent */
+ if (linkat(tmpfile_fd, "", info->t_dir1_fd, FILE3, AT_EMPTY_PATH))
+ die("failure: linkat");
+ if (close(tmpfile_fd))
+ die("failure: close");
+ if (is_setgid(info->t_dir1_fd, FILE3, 0))
+ die("failure: is_setgid");
+ if (is_ixgrp(info->t_dir1_fd, FILE3, 0))
+ die("failure: is_ixgrp");
+ if (unlinkat(info->t_dir1_fd, FILE3, 0))
+ die("failure: delete");
+ }
+
+ exit(EXIT_SUCCESS);
+ }
+ if (wait_for_pid(pid))
+ goto out;
+
+ fret = 0;
+ log_debug("Ran test");
+out:
+ safe_close(file1_fd);
+ return fret;
+}
+
+/*
+ * If the parent directory has a default acl then permissions are based off
+ * of that and current_umask() is ignored. Specifically, if the ACL has an
+ * ACL_MASK entry, the group permissions correspond to the permissions of
+ * the ACL_MASK entry. Otherwise, if the ACL has no ACL_MASK entry, the
+ * group permissions correspond to the permissions of the ACL_GROUP_OBJ
+ * entry.
+ *
+ * Use setfacl to check whether inode strip S_ISGID works correctly under
+ * the above two situations.
+ *
+ * Test for commit
+ * 1639a49ccdce ("fs: move S_ISGID stripping into the vfs_*() helpers").
+ */
+static int setgid_create_acl(const struct vfstest_info *info)
+{
+ int fret = -1;
+ int file1_fd = -EBADF;
+ int tmpfile_fd = -EBADF;
+ pid_t pid;
+ bool supported = false;
+ mode_t mode;
+
+ if (!caps_supported())
+ return 0;
+
+ if (fchmod(info->t_dir1_fd, S_IRUSR |
+ S_IWUSR |
+ S_IRGRP |
+ S_IWGRP |
+ S_IROTH |
+ S_IWOTH |
+ S_IXUSR |
+ S_IXGRP |
+ S_IXOTH |
+ S_ISGID), 0) {
+ log_stderr("failure: fchmod");
+ goto out;
+ }
+
+ /* Verify that the setgid bit got raised. */
+ if (!is_setgid(info->t_dir1_fd, "", AT_EMPTY_PATH)) {
+ log_stderr("failure: is_setgid");
+ goto out;
+ }
+
+ supported = openat_tmpfile_supported(info->t_dir1_fd);
+
+ pid = fork();
+ if (pid < 0) {
+ log_stderr("failure: fork");
+ goto out;
+ }
+ if (pid == 0) {
+ umask(S_IXGRP);
+ mode = umask(S_IXGRP);
+ if (!(mode & S_IXGRP))
+ die("failure: umask");
+
+ /* The group permissions correspond to the permissions of the
+ * ACL_MASK entry. Use setfacl to set ACL mask(m) as rw, so now
+ * the group permissions is rw. Also, umask doesn't affect
+ * group permissions because umask will be ignored if having
+ * acl.
+ */
+ snprintf(t_buf, sizeof(t_buf), "setfacl -d -m u::rwx,g::rw,o::rwx,m:rw %s/%s", info->t_mountpoint, T_DIR1);
+ if (system(t_buf))
+ die("failure: system");
+
+ if (!switch_ids(0, 10000))
+ die("failure: switch_ids");
+
+ if (!caps_down_fsetid())
+ die("failure: caps_down_fsetid");
+
+ /* create regular file via open() */
+ file1_fd = openat(info->t_dir1_fd, FILE1, O_CREAT | O_EXCL | O_CLOEXEC, S_IXGRP | S_ISGID);
+ if (file1_fd < 0)
+ die("failure: create");
+
+ /* Neither in_group_p() nor capable_wrt_inode_uidgid() so setgid
+ * bit needs to be stripped.
+ */
+ if (is_setgid(info->t_dir1_fd, FILE1, 0))
+ die("failure: is_setgid");
+
+ if (is_ixgrp(info->t_dir1_fd, FILE1, 0))
+ die("failure: is_ixgrp");
+
+ /* create directory */
+ if (mkdirat(info->t_dir1_fd, DIR1, 0000))
+ die("failure: create");
+
+ if (xfs_irix_sgid_inherit_enabled(info->t_fstype)) {
+ /* We're not in_group_p(). */
+ if (is_setgid(info->t_dir1_fd, DIR1, 0))
+ die("failure: is_setgid");
+ } else {
+ /* Directories always inherit the setgid bit. */
+ if (!is_setgid(info->t_dir1_fd, DIR1, 0))
+ die("failure: is_setgid");
+ }
+
+ if (is_ixgrp(info->t_dir1_fd, DIR1, 0))
+ die("failure: is_ixgrp");
+
+ /* create a special file via mknodat() vfs_create */
+ if (mknodat(info->t_dir1_fd, FILE2, S_IFREG | S_ISGID | S_IXGRP, 0))
+ die("failure: mknodat");
+
+ if (is_setgid(info->t_dir1_fd, FILE2, 0))
+ die("failure: is_setgid");
+
+ if (is_ixgrp(info->t_dir1_fd, FILE2, 0))
+ die("failure: is_ixgrp");
+
+ /* create a character device via mknodat() vfs_mknod */
+ if (mknodat(info->t_dir1_fd, CHRDEV1, S_IFCHR | S_ISGID | S_IXGRP, makedev(5, 1)))
+ die("failure: mknodat");
+
+ if (is_setgid(info->t_dir1_fd, CHRDEV1, 0))
+ die("failure: is_setgid");
+
+ if (is_ixgrp(info->t_dir1_fd, CHRDEV1, 0))
+ die("failure: is_ixgrp");
+
+ /*
+ * In setgid directories newly created files always inherit the
+ * gid from the parent directory. Verify that the file is owned
+ * by gid 0, not by gid 10000.
+ */
+ if (!expected_uid_gid(info->t_dir1_fd, FILE1, 0, 0, 0))
+ die("failure: check ownership");
+
+ /*
+ * In setgid directories newly created directories always
+ * inherit the gid from the parent directory. Verify that the
+ * directory is owned by gid 0, not by gid 10000.
+ */
+ if (!expected_uid_gid(info->t_dir1_fd, DIR1, 0, 0, 0))
+ die("failure: check ownership");
+
+ if (!expected_uid_gid(info->t_dir1_fd, FILE2, 0, 0, 0))
+ die("failure: check ownership");
+
+ if (!expected_uid_gid(info->t_dir1_fd, CHRDEV1, 0, 0, 0))
+ die("failure: check ownership");
+
+ if (unlinkat(info->t_dir1_fd, FILE1, 0))
+ die("failure: delete");
+
+ if (unlinkat(info->t_dir1_fd, DIR1, AT_REMOVEDIR))
+ die("failure: delete");
+
+ if (unlinkat(info->t_dir1_fd, FILE2, 0))
+ die("failure: delete");
+
+ if (unlinkat(info->t_dir1_fd, CHRDEV1, 0))
+ die("failure: delete");
+
+ /* create tmpfile via filesystem tmpfile api */
+ if (supported) {
+ tmpfile_fd = openat(info->t_dir1_fd, ".", O_TMPFILE | O_RDWR, S_IXGRP | S_ISGID);
+ if (tmpfile_fd < 0)
+ die("failure: create");
+ /* link the temporary file into the filesystem, making it permanent */
+ if (linkat(tmpfile_fd, "", info->t_dir1_fd, FILE3, AT_EMPTY_PATH))
+ die("failure: linkat");
+ if (close(tmpfile_fd))
+ die("failure: close");
+ if (is_setgid(info->t_dir1_fd, FILE3, 0))
+ die("failure: is_setgid");
+ if (is_ixgrp(info->t_dir1_fd, FILE3, 0))
+ die("failure: is_ixgrp");
+ if (!expected_uid_gid(info->t_dir1_fd, FILE3, 0, 0, 0))
+ die("failure: check ownership");
+ if (unlinkat(info->t_dir1_fd, FILE3, 0))
+ die("failure: delete");
+ }
+
+ exit(EXIT_SUCCESS);
+ }
+ if (wait_for_pid(pid))
+ goto out;
+
+ pid = fork();
+ if (pid < 0) {
+ log_stderr("failure: fork");
+ goto out;
+ }
+ if (pid == 0) {
+ umask(S_IXGRP);
+ mode = umask(S_IXGRP);
+ if (!(mode & S_IXGRP))
+ die("failure: umask");
+
+ /* The group permissions correspond to the permissions of the
+ * ACL_GROUP_OBJ entry. Don't use setfacl to set ACL_MASK, so
+ * the group permissions is equal to ACL_GROUP_OBJ(g)
+ * entry(rwx). Also, umask doesn't affect group permissions
+ * because umask will be ignored if having acl.
+ */
+ snprintf(t_buf, sizeof(t_buf), "setfacl -d -m u::rwx,g::rwx,o::rwx, %s/%s", info->t_mountpoint, T_DIR1);
+ if (system(t_buf))
+ die("failure: system");
+
+ if (!switch_ids(0, 10000))
+ die("failure: switch_ids");
+
+ if (!caps_down_fsetid())
+ die("failure: caps_down_fsetid");
+
+ /* create regular file via open() */
+ file1_fd = openat(info->t_dir1_fd, FILE1, O_CREAT | O_EXCL | O_CLOEXEC, S_IXGRP | S_ISGID);
+ if (file1_fd < 0)
+ die("failure: create");
+
+ /* Neither in_group_p() nor capable_wrt_inode_uidgid() so setgid
+ * bit needs to be stripped.
+ */
+ if (is_setgid(info->t_dir1_fd, FILE1, 0))
+ die("failure: is_setgid");
+
+ if (!is_ixgrp(info->t_dir1_fd, FILE1, 0))
+ die("failure: is_ixgrp");
+
+ /* create directory */
+ if (mkdirat(info->t_dir1_fd, DIR1, 0000))
+ die("failure: create");
+
+ if (xfs_irix_sgid_inherit_enabled(info->t_fstype)) {
+ /* We're not in_group_p(). */
+ if (is_setgid(info->t_dir1_fd, DIR1, 0))
+ die("failure: is_setgid");
+ } else {
+ /* Directories always inherit the setgid bit. */
+ if (!is_setgid(info->t_dir1_fd, DIR1, 0))
+ die("failure: is_setgid");
+ }
+
+ if (is_ixgrp(info->t_dir1_fd, DIR1, 0))
+ die("failure: is_ixgrp");
+
+ /* create a special file via mknodat() vfs_create */
+ if (mknodat(info->t_dir1_fd, FILE2, S_IFREG | S_ISGID | S_IXGRP, 0))
+ die("failure: mknodat");
+
+ if (is_setgid(info->t_dir1_fd, FILE2, 0))
+ die("failure: is_setgid");
+
+ if (!is_ixgrp(info->t_dir1_fd, FILE2, 0))
+ die("failure: is_ixgrp");
+
+ /* create a character device via mknodat() vfs_mknod */
+ if (mknodat(info->t_dir1_fd, CHRDEV1, S_IFCHR | S_ISGID | S_IXGRP, makedev(5, 1)))
+ die("failure: mknodat");
+
+ if (is_setgid(info->t_dir1_fd, CHRDEV1, 0))
+ die("failure: is_setgid");
+
+ if (!is_ixgrp(info->t_dir1_fd, CHRDEV1, 0))
+ die("failure: is_ixgrp");
+
+ /*
+ * In setgid directories newly created files always inherit the
+ * gid from the parent directory. Verify that the file is owned
+ * by gid 0, not by gid 10000.
+ */
+ if (!expected_uid_gid(info->t_dir1_fd, FILE1, 0, 0, 0))
+ die("failure: check ownership");
+
+ /*
+ * In setgid directories newly created directories always
+ * inherit the gid from the parent directory. Verify that the
+ * directory is owned by gid 0, not by gid 10000.
+ */
+ if (!expected_uid_gid(info->t_dir1_fd, DIR1, 0, 0, 0))
+ die("failure: check ownership");
+
+ if (!expected_uid_gid(info->t_dir1_fd, FILE2, 0, 0, 0))
+ die("failure: check ownership");
+
+ if (!expected_uid_gid(info->t_dir1_fd, CHRDEV1, 0, 0, 0))
+ die("failure: check ownership");
+
+ if (unlinkat(info->t_dir1_fd, FILE1, 0))
+ die("failure: delete");
+
+ if (unlinkat(info->t_dir1_fd, DIR1, AT_REMOVEDIR))
+ die("failure: delete");
+
+ if (unlinkat(info->t_dir1_fd, FILE2, 0))
+ die("failure: delete");
+
+ if (unlinkat(info->t_dir1_fd, CHRDEV1, 0))
+ die("failure: delete");
+
+ /* create tmpfile via filesystem tmpfile api */
+ if (supported) {
+ tmpfile_fd = openat(info->t_dir1_fd, ".", O_TMPFILE | O_RDWR, S_IXGRP | S_ISGID);
+ if (tmpfile_fd < 0)
+ die("failure: create");
+ /* link the temporary file into the filesystem, making it permanent */
+ if (linkat(tmpfile_fd, "", info->t_dir1_fd, FILE3, AT_EMPTY_PATH))
+ die("failure: linkat");
+ if (close(tmpfile_fd))
+ die("failure: close");
+ if (is_setgid(info->t_dir1_fd, FILE3, 0))
+ die("failure: is_setgid");
+ if (!is_ixgrp(info->t_dir1_fd, FILE3, 0))
+ die("failure: is_ixgrp");
+ if (!expected_uid_gid(info->t_dir1_fd, FILE3, 0, 0, 0))
+ die("failure: check ownership");
+ if (unlinkat(info->t_dir1_fd, FILE3, 0))
+ die("failure: delete");
+ }
+
+ exit(EXIT_SUCCESS);
+ }
+ if (wait_for_pid(pid))
+ goto out;
+
+ fret = 0;
+ log_debug("Ran test");
+out:
+ safe_close(file1_fd);
+
+ return fret;
+}
+
static int setattr_truncate(const struct vfstest_info *info)
{
int fret = -1;
fprintf(stderr, "--test-fscaps-regression Run fscap regression tests\n");
fprintf(stderr, "--test-nested-userns Run nested userns idmapped mount testsuite\n");
fprintf(stderr, "--test-btrfs Run btrfs specific idmapped mount testsuite\n");
+ fprintf(stderr, "--test-tmpfs Run tmpfs specific idmapped mount testsuite\n");
fprintf(stderr, "--test-setattr-fix-968219708108 Run setattr regression tests\n");
fprintf(stderr, "--test-setxattr-fix-705191b03d50 Run setxattr regression tests\n");
+ fprintf(stderr, "--test-setgid-create-umask Run setgid with umask tests\n");
+ fprintf(stderr, "--test-setgid-create-acl Run setgid with acl tests\n");
_exit(EXIT_SUCCESS);
}
{"test-btrfs", no_argument, 0, 'b'},
{"test-setattr-fix-968219708108", no_argument, 0, 'i'},
{"test-setxattr-fix-705191b03d50", no_argument, 0, 'j'},
+ {"test-setgid-create-umask", no_argument, 0, 'u'},
+ {"test-setgid-create-acl", no_argument, 0, 'l'},
+ {"test-tmpfs", no_argument, 0, 't'},
{NULL, 0, 0, 0},
};
.nr_tests = ARRAY_SIZE(t_basic),
};
+static const struct test_struct t_setgid_create_umask[] = {
+ { setgid_create_umask, 0, "create operations in directories with setgid bit set under umask", },
+};
+
+static const struct test_suite s_setgid_create_umask = {
+ .tests = t_setgid_create_umask,
+ .nr_tests = ARRAY_SIZE(t_setgid_create_umask),
+};
+
+static const struct test_struct t_setgid_create_acl[] = {
+ { setgid_create_acl, 0, "create operations in directories with setgid bit set under posix acl", },
+};
+
+static const struct test_suite s_setgid_create_acl = {
+ .tests = t_setgid_create_acl,
+ .nr_tests = ARRAY_SIZE(t_setgid_create_acl),
+};
+
static bool run_test(struct vfstest_info *info, const struct test_struct suite[], size_t suite_size)
{
int i;
bool idmapped_mounts_supported = false, test_btrfs = false,
test_core = false, test_fscaps_regression = false,
test_nested_userns = false, test_setattr_fix_968219708108 = false,
- test_setxattr_fix_705191b03d50 = false;
+ test_setxattr_fix_705191b03d50 = false, test_tmpfs = false,
+ test_setgid_create_umask = false, test_setgid_create_acl = false;
init_vfstest_info(&info);
case 'j':
test_setxattr_fix_705191b03d50 = true;
break;
+ case 'u':
+ test_setgid_create_umask = true;
+ break;
+ case 'l':
+ test_setgid_create_acl = true;
+ break;
+ case 't':
+ test_tmpfs = true;
+ break;
case 'h':
/* fallthrough */
default:
!run_suite(&info, &s_setxattr_fix_705191b03d50))
goto out;
+ if (test_setgid_create_umask) {
+ if (!run_suite(&info, &s_setgid_create_umask))
+ goto out;
+
+ if (!run_suite(&info, &s_setgid_create_umask_idmapped_mounts))
+ goto out;
+ }
+
+ if (test_setgid_create_acl) {
+ if (!run_suite(&info, &s_setgid_create_acl))
+ goto out;
+
+ if (!run_suite(&info, &s_setgid_create_acl_idmapped_mounts))
+ goto out;
+ }
+
+ if (test_tmpfs) {
+ if (!run_suite(&info, &s_tmpfs_idmapped_mounts))
+ goto out;
+ }
+
fret = EXIT_SUCCESS;
out:
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __VFSTEST_H
+#define __VFSTEST_H
+
+void test_setup(struct vfstest_info *info);
+void test_cleanup(struct vfstest_info *info);
+
+
+#endif /* __VFSTEST_H */
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * find(1) but with special predicates for finding XFS attributes.
+ * Copyright (C) 2022 Oracle.
+ */
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <ftw.h>
+#include <linux/fs.h>
+#include <xfs/xfs.h>
+
+#include "global.h"
+
+static int want_anyfile;
+static int want_datafile;
+static int want_attrfile;
+static int want_dir;
+static int want_regfile;
+static int want_sharedfile;
+static int report_errors = 1;
+
+static int
+check_datafile(
+ const char *path,
+ int fd)
+{
+ off_t off;
+
+ off = lseek(fd, 0, SEEK_DATA);
+ if (off >= 0)
+ return 1;
+
+ if (errno == ENXIO)
+ return 0;
+
+ if (report_errors)
+ perror(path);
+
+ return -1;
+}
+
+static int
+check_attrfile(
+ const char *path,
+ int fd)
+{
+ struct fsxattr fsx;
+ int ret;
+
+ ret = ioctl(fd, XFS_IOC_FSGETXATTR, &fsx);
+ if (ret) {
+ if (report_errors)
+ perror(path);
+ return -1;
+ }
+
+ if (want_attrfile && (fsx.fsx_xflags & XFS_XFLAG_HASATTR))
+ return 1;
+ return 0;
+}
+
+#define BMAP_NR 33
+static struct getbmapx bmaps[BMAP_NR];
+
+static int
+check_sharedfile(
+ const char *path,
+ int fd)
+{
+ struct getbmapx *key = &bmaps[0];
+ unsigned int i;
+ int ret;
+
+ memset(key, 0, sizeof(struct getbmapx));
+ key->bmv_length = ULLONG_MAX;
+ /* no holes and don't flush dirty pages */
+ key->bmv_iflags = BMV_IF_DELALLOC | BMV_IF_NO_HOLES;
+ key->bmv_count = BMAP_NR;
+
+ while ((ret = ioctl(fd, XFS_IOC_GETBMAPX, bmaps)) == 0) {
+ struct getbmapx *p = &bmaps[1];
+ xfs_off_t new_off;
+
+ for (i = 0; i < key->bmv_entries; i++, p++) {
+ if (p->bmv_oflags & BMV_OF_SHARED)
+ return 1;
+ }
+
+ if (key->bmv_entries == 0)
+ break;
+ p = key + key->bmv_entries;
+ if (p->bmv_oflags & BMV_OF_LAST)
+ return 0;
+
+ new_off = p->bmv_offset + p->bmv_length;
+ key->bmv_length -= new_off - key->bmv_offset;
+ key->bmv_offset = new_off;
+ }
+ if (ret < 0) {
+ if (report_errors)
+ perror(path);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void
+print_help(
+ const char *name)
+{
+ printf("Usage: %s [OPTIONS] path\n", name);
+ printf("\n");
+ printf("Print all file paths matching any of the given predicates.\n");
+ printf("\n");
+ printf("-a Match files with xattrs.\n");
+ printf("-b Match files with data blocks.\n");
+ printf("-d Match directories.\n");
+ printf("-q Ignore errors while walking directory tree.\n");
+ printf("-r Match regular files.\n");
+ printf("-s Match files with shared blocks.\n");
+ printf("\n");
+ printf("If no matching options are given, match all files found.\n");
+}
+
+static int
+visit(
+ const char *path,
+ const struct stat *sb,
+ int typeflag,
+ struct FTW *ftwbuf)
+{
+ int printme = 1;
+ int fd = -1;
+ int retval = FTW_CONTINUE;
+
+ if (want_anyfile)
+ goto out;
+ if (want_regfile && typeflag == FTW_F)
+ goto out;
+ if (want_dir && typeflag == FTW_D)
+ goto out;
+
+ /*
+ * We can only open directories and files; screen out everything else.
+ * Note that nftw lies and reports FTW_F for device files, so check the
+ * statbuf mode too.
+ */
+ if (typeflag != FTW_F && typeflag != FTW_D) {
+ printme = 0;
+ goto out;
+ }
+
+ if (!S_ISREG(sb->st_mode) && !S_ISDIR(sb->st_mode)) {
+ printme = 0;
+ goto out;
+ }
+
+ fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ if (report_errors) {
+ perror(path);
+ return FTW_STOP;
+ }
+
+ return FTW_CONTINUE;
+ }
+
+ if (want_datafile && typeflag == FTW_F) {
+ int ret = check_datafile(path, fd);
+ if (ret < 0 && report_errors) {
+ printme = 0;
+ retval = FTW_STOP;
+ goto out_fd;
+ }
+
+ if (ret == 1)
+ goto out_fd;
+ }
+
+ if (want_attrfile) {
+ int ret = check_attrfile(path, fd);
+ if (ret < 0 && report_errors) {
+ printme = 0;
+ retval = FTW_STOP;
+ goto out_fd;
+ }
+
+ if (ret == 1)
+ goto out_fd;
+ }
+
+ if (want_sharedfile) {
+ int ret = check_sharedfile(path, fd);
+ if (ret < 0 && report_errors) {
+ printme = 0;
+ retval = FTW_STOP;
+ goto out_fd;
+ }
+
+ if (ret == 1)
+ goto out_fd;
+ }
+
+ printme = 0;
+out_fd:
+ close(fd);
+out:
+ if (printme)
+ printf("%s\n", path);
+ return retval;
+}
+
+static void
+handle_sigabrt(
+ int signal,
+ siginfo_t *info,
+ void *ucontext)
+{
+ fprintf(stderr, "Signal %u, exiting.\n", signal);
+ exit(2);
+}
+
+int
+main(
+ int argc,
+ char *argv[])
+{
+ struct rlimit rlimit;
+ struct sigaction abrt = {
+ .sa_sigaction = handle_sigabrt,
+ .sa_flags = SA_SIGINFO,
+ };
+ int c;
+ int ret;
+
+ while ((c = getopt(argc, argv, "abdqrs")) >= 0) {
+ switch (c) {
+ case 'a': want_attrfile = 1; break;
+ case 'b': want_datafile = 1; break;
+ case 'd': want_dir = 1; break;
+ case 'q': report_errors = 0; break;
+ case 'r': want_regfile = 1; break;
+ case 's': want_sharedfile = 1; break;
+ default:
+ print_help(argv[0]);
+ return 1;
+ }
+ }
+
+ ret = getrlimit(RLIMIT_NOFILE, &rlimit);
+ if (ret) {
+ perror("RLIMIT_NOFILE");
+ return 1;
+ }
+
+ if (!want_attrfile && !want_datafile && !want_dir && !want_regfile &&
+ !want_sharedfile)
+ want_anyfile = 1;
+
+ /*
+ * nftw is known to abort() if a directory it is walking disappears out
+ * from under it. Handle this with grace if the caller wants us to run
+ * quietly.
+ */
+ if (!report_errors) {
+ ret = sigaction(SIGABRT, &abrt, NULL);
+ if (ret) {
+ perror("SIGABRT handler");
+ return 1;
+ }
+ }
+
+ for (c = optind; c < argc; c++) {
+ ret = nftw(argv[c], visit, rlimit.rlim_cur - 5,
+ FTW_ACTIONRETVAL | FTW_CHDIR | FTW_MOUNT |
+ FTW_PHYS);
+ if (ret && report_errors) {
+ perror(argv[c]);
+ break;
+ }
+ }
+
+ if (ret)
+ return 1;
+ return 0;
+}
_devmgt_remove ${removed_dev_htl} $ds
dev_removed=1
- $BTRFS_UTIL_PROG filesystem show $SCRATCH_DEV | grep "Some devices missing" >> $seqres.full || _fail \
- "btrfs did not report device missing"
+ $BTRFS_UTIL_PROG filesystem show $SCRATCH_DEV | \
+ grep -ie '\bmissing\b' >> $seqres.full || \
+ _fail "btrfs did not report device missing"
# add a new disk to btrfs
ds=${devs[@]:$(($n)):1}
# We check to end up back at the original file with the correct offset.
#
. ./common/preamble
-_begin_fstest auto rw metadata
+_begin_fstest auto rw metadata fiemap logical_resolve compress
noise_pid=0
cnt=0
errcnt=0
dir="$SCRATCH_MNT/$snap_name/"
- for file in `find $dir -name f\* -size +0 | sort -R`; do
+ for file in `find $dir -name f\* -size +0 | shuf`; do
extents=`_check_file_extents $file`
ret=$?
if [ $ret -ne 0 ]; then
# Btrfs Online defragmentation tests
#
. ./common/preamble
-_begin_fstest auto defrag
+_begin_fstest auto defrag compress
cnt=119
filesize=48000
# performed, a btrfsck run, and finally the filesystem is remounted.
#
. ./common/preamble
-_begin_fstest auto replace volume
+_begin_fstest auto replace volume scrub
noise_pid=0
_require_scratch_dev_pool_equal_size
_require_scratch_size $((10 * 1024 * 1024)) #kB
_require_command "$WIPEFS_PROG" wipefs
+_btrfs_get_profile_configs dup
rm -f $tmp.*
fill_scratch()
{
local fssize=$1
+ local with_cancel=$2
local filler_pid
# Fill inline extents.
$XFS_IO_PROG -f -d -c "pwrite -b 64k 0 1E" "$SCRATCH_MNT/t_filler" &>\
$tmp.filler_result &
filler_pid=$!
- sleep $((2 * $wait_time))
+ if [ "${with_cancel}" = "cancel" ]; then
+ sleep $((10 * $wait_time))
+ else
+ sleep $((2 * $wait_time))
+ fi
kill -KILL $filler_pid &> /dev/null
wait $filler_pid &> /dev/null
_scratch_mount
_require_fs_space $SCRATCH_MNT $((2 * 512 * 1024)) #2.5G
- fill_scratch $fssize
+ fill_scratch $fssize $with_cancel
_run_btrfs_util_prog filesystem show -m $SCRATCH_MNT
echo -e "Replace from $source_dev to $SPARE_DEV\\n" >> $seqres.full
_run_btrfs_util_prog filesystem show -m $SCRATCH_MNT
# Skip -r test for configs without mirror OR replace cancel
- if echo $mkfs_options | egrep -qv "raid1|raid5|raid6|raid10" || \
+ if echo $mkfs_options | grep -E -qv "raid1|raid5|raid6|raid10" || \
[ "${with_cancel}Q" = "cancelQ" ]; then
_scratch_unmount > /dev/null 2>&1
_scratch_dev_pool_put
fi
}
-workout "-m single -d single" 1 no 64
-# Mixed BG & RAID/DUP profiles are not supported on zoned btrfs
-if ! _scratch_btrfs_is_zoned; then
- workout "-m dup -d single" 1 no 64
- workout "-m dup -d single" 1 cancel 1024
- workout "-m raid0 -d raid0" 2 no 64
- workout "-m raid1 -d raid1" 2 no 2048
- workout "-m raid10 -d raid10" 4 no 64
- workout "-m single -d single -M" 1 no 64
- workout "-m dup -d dup -M" 1 no 64
- workout "-m raid5 -d raid5" 2 no 64
- workout "-m raid6 -d raid6" 3 no 64
-fi
+for t in "-m single -d single:1 no 64" \
+ "-m dup -d single:1 no 64" \
+ "-m dup -d single:1 cancel 1024" \
+ "-m raid0 -d raid0:2 no 64" \
+ "-m raid1 -d raid1:2 no 2048" \
+ "-m raid10 -d raid10:4 no 64" \
+ "-m single -d single -M:1 no 64" \
+ "-m dup -d dup -M:1 no 64" \
+ "-m raid5 -d raid5:2 no 64" \
+ "-m raid6 -d raid6:3 no 64"; do
+ mkfs_option=${t%:*}
+ workout_option=${t#*:}
+ if [[ "${_btrfs_profile_configs[@]}" =~ "${mkfs_option/ -M}"( |$) ]]; then
+ workout "$mkfs_option" $workout_option
+ fi
+done
echo "*** done"
status=0
# ext4 does not support zoned block device
_require_non_zoned_device "${SCRATCH_DEV}"
_require_loop
+_require_extra_fs ext4
+SOURCE_DIR=/etc
+BASENAME=$(basename $SOURCE_DIR)
BLOCK_SIZE=`_get_block_size $TEST_DIR`
# Create & populate an ext4 filesystem
# Manual mount so we don't use -t btrfs or selinux context
mount -t ext4 $SCRATCH_DEV $SCRATCH_MNT
-_require_fs_space $SCRATCH_MNT $(du -s /lib/modules/`uname -r` | ${AWK_PROG} '{print $1}')
+_require_fs_space $SCRATCH_MNT $(du -s $SOURCE_DIR | ${AWK_PROG} '{print $1}')
-cp -aR /lib/modules/`uname -r`/ $SCRATCH_MNT
+$TIMEOUT_PROG 10 cp -aRP $SOURCE_DIR $SCRATCH_MNT
_scratch_unmount
# Convert it to btrfs, mount it, verify the data
_fail "btrfs-convert failed"
_try_scratch_mount || _fail "Could not mount new btrfs fs"
# (Ignore the symlinks which may be broken/nonexistent)
-diff -r /lib/modules/`uname -r`/ $SCRATCH_MNT/`uname -r`/ 2>&1 | grep -vw "source\|build"
+diff --no-dereference -r $SOURCE_DIR $SCRATCH_MNT/$BASENAME/ 2>&1
# Old ext4 image file should exist & be consistent
$E2FSCK_PROG -fn $SCRATCH_MNT/ext2_saved/image >> $seqres.full 2>&1 || \
mount -o loop $SCRATCH_MNT/ext2_saved/image $SCRATCH_MNT/mnt || \
_fail "could not loop mount saved ext4 image"
# Ignore the symlinks which may be broken/nonexistent
-diff -r /lib/modules/`uname -r`/ $SCRATCH_MNT/mnt/`uname -r`/ 2>&1 | grep -vw "source\|build"
+diff --no-dereference -r $SOURCE_DIR $SCRATCH_MNT/mnt/$BASENAME/ 2>&1
umount $SCRATCH_MNT/mnt
# Now put some fresh data on the btrfs fs
mkdir -p $SCRATCH_MNT/new
-cp -aR /lib/modules/`uname -r`/ $SCRATCH_MNT/new
+$TIMEOUT_PROG 10 cp -aRP $SOURCE_DIR $SCRATCH_MNT/new
_scratch_unmount
# Mount the un-converted ext4 device & check the contents
mount -t ext4 $SCRATCH_DEV $SCRATCH_MNT
# (Ignore the symlinks which may be broken/nonexistent)
-diff -r /lib/modules/`uname -r`/ $SCRATCH_MNT/`uname -r`/ 2>&1 | grep -vw "source\|build"
+diff --no-dereference -r $SOURCE_DIR $SCRATCH_MNT/$BASENAME/ 2>&1
_scratch_unmount
# dmesg to see if there was a csum error.
#
. ./common/preamble
-_begin_fstest auto quick balance
+_begin_fstest auto quick balance prealloc
# Import common functions.
. ./common/filter
# readonly and remounting rw.
#
. ./common/preamble
-_begin_fstest auto quick snapshot
+_begin_fstest auto quick snapshot remount
# Import common functions.
. ./common/filter
# btrfs send hole punch test
#
. ./common/preamble
-_begin_fstest auto quick send
+_begin_fstest auto quick send prealloc
tmp=`mktemp -d`
tmp_dir=send_temp_$seq
-# Override the default cleanup function.
-_cleanup()
-{
- $BTRFS_UTIL_PROG subvolume delete $TEST_DIR/$tmp_dir/snap > /dev/null 2>&1
- $BTRFS_UTIL_PROG subvolume delete $TEST_DIR/$tmp_dir/snap1 > /dev/null 2>&1
- $BTRFS_UTIL_PROG subvolume delete $TEST_DIR/$tmp_dir/send > /dev/null 2>&1
- rm -rf $TEST_DIR/$tmp_dir
- rm -f $tmp.*
-}
-
# Import common functions.
. ./common/filter
# real QA test starts here
_supported_fs btrfs
-_require_test
_require_scratch
_require_fssum
_require_xfs_io_command "falloc"
_scratch_mkfs > /dev/null 2>&1
-
-#receive needs to be able to setxattrs, including the selinux context, if we use
-#the normal nfs context thing it screws up our ability to set the
-#security.selinux xattrs so we need to disable this for this test
-export SELINUX_MOUNT_OPTIONS=""
-
_scratch_mount
-mkdir $TEST_DIR/$tmp_dir
-$BTRFS_UTIL_PROG subvolume create $TEST_DIR/$tmp_dir/send \
+mkdir $SCRATCH_MNT/$tmp_dir
+$BTRFS_UTIL_PROG subvolume create $SCRATCH_MNT/$tmp_dir/send \
> $seqres.full 2>&1 || _fail "failed subvolume create"
-_ddt of=$TEST_DIR/$tmp_dir/send/foo bs=1M count=10 >> $seqres.full \
+_ddt of=$SCRATCH_MNT/$tmp_dir/send/foo bs=1M count=10 >> $seqres.full \
2>&1 || _fail "dd failed"
-$BTRFS_UTIL_PROG subvolume snapshot -r $TEST_DIR/$tmp_dir/send \
- $TEST_DIR/$tmp_dir/snap >> $seqres.full 2>&1 || _fail "failed snap"
-$XFS_IO_PROG -c "fpunch 1m 1m" $TEST_DIR/$tmp_dir/send/foo
-$BTRFS_UTIL_PROG subvolume snapshot -r $TEST_DIR/$tmp_dir/send \
- $TEST_DIR/$tmp_dir/snap1 >> $seqres.full 2>&1 || _fail "failed snap"
+$BTRFS_UTIL_PROG subvolume snapshot -r $SCRATCH_MNT/$tmp_dir/send \
+ $SCRATCH_MNT/$tmp_dir/snap >> $seqres.full 2>&1 || _fail "failed snap"
+$XFS_IO_PROG -c "fpunch 1m 1m" $SCRATCH_MNT/$tmp_dir/send/foo
+$BTRFS_UTIL_PROG subvolume snapshot -r $SCRATCH_MNT/$tmp_dir/send \
+ $SCRATCH_MNT/$tmp_dir/snap1 >> $seqres.full 2>&1 || _fail "failed snap"
-$FSSUM_PROG -A -f -w $tmp/fssum.snap $TEST_DIR/$tmp_dir/snap >> $seqres.full \
+# -A disable access time check.
+# And -T disable xattrs to prevent SELinux changes causing false alerts, and the
+# test case only cares about hole punching.
+$FSSUM_PROG -AT -f -w $tmp/fssum.snap $SCRATCH_MNT/$tmp_dir/snap >> $seqres.full \
2>&1 || _fail "fssum gen failed"
-$FSSUM_PROG -A -f -w $tmp/fssum.snap1 $TEST_DIR/$tmp_dir/snap1 >> $seqres.full \
+$FSSUM_PROG -AT -f -w $tmp/fssum.snap1 $SCRATCH_MNT/$tmp_dir/snap1 >> $seqres.full \
2>&1 || _fail "fssum gen failed"
-$BTRFS_UTIL_PROG send -f $tmp/send.snap $TEST_DIR/$tmp_dir/snap >> \
+$BTRFS_UTIL_PROG send -f $tmp/send.snap $SCRATCH_MNT/$tmp_dir/snap >> \
$seqres.full 2>&1 || _fail "failed send"
-$BTRFS_UTIL_PROG send -p $TEST_DIR/$tmp_dir/snap \
- -f $tmp/send.snap1 $TEST_DIR/$tmp_dir/snap1 \
+$BTRFS_UTIL_PROG send -p $SCRATCH_MNT/$tmp_dir/snap \
+ -f $tmp/send.snap1 $SCRATCH_MNT/$tmp_dir/snap1 \
>> $seqres.full 2>&1 || _fail "failed send"
+_scratch_unmount
+_scratch_mkfs > /dev/null 2>&1
+_scratch_mount
+
$BTRFS_UTIL_PROG receive -f $tmp/send.snap $SCRATCH_MNT >> $seqres.full 2>&1 \
|| _fail "failed recv"
$BTRFS_UTIL_PROG receive -f $tmp/send.snap1 $SCRATCH_MNT >> $seqres.full 2>&1 \
|| _fail "fssum failed"
echo "Silence is golden"
-status=0 ; exit
+status=0
+exit
_supported_fs btrfs
_require_scratch
+_require_scratch_qgroup
_require_cloner
# Currently in btrfs the node/leaf size can not be smaller than the page
sleep 0.5
+ # In new versions of btrfs-progs (6.0+), the defrag command outputs to
+ # stdout the path of the files it operates on. So ignore that.
find $SCRATCH_MNT -type f -print0 | xargs -0 \
- $BTRFS_UTIL_PROG filesystem defrag -f
+ $BTRFS_UTIL_PROG filesystem defrag -f > /dev/null
sync
wait
#
# FS QA Test No. 022
#
-# Test the basic functionality of qgroups
+# Test the basic qgroup exceed case
#
. ./common/preamble
_begin_fstest auto qgroup limit
_supported_fs btrfs
_require_scratch
+_require_qgroup_rescan
_require_btrfs_qgroup_report
-# Test to make sure we can actually turn it on and it makes sense
-_basic_test()
-{
- echo "=== basic test ===" >> $seqres.full
- _run_btrfs_util_prog subvolume create $SCRATCH_MNT/a
- _run_btrfs_util_prog quota enable $SCRATCH_MNT/a
- _run_btrfs_util_prog quota rescan -w $SCRATCH_MNT
- subvolid=$(_btrfs_get_subvolid $SCRATCH_MNT a)
- $BTRFS_UTIL_PROG qgroup show $units $SCRATCH_MNT | grep $subvolid >> \
- $seqres.full 2>&1
- [ $? -eq 0 ] || _fail "couldn't find our subvols quota group"
- run_check $FSSTRESS_PROG -d $SCRATCH_MNT/a -w -p 1 -n 2000 \
- $FSSTRESS_AVOID
- _run_btrfs_util_prog subvolume snapshot $SCRATCH_MNT/a \
- $SCRATCH_MNT/b
-
- # the shared values of both the original subvol and snapshot should
- # match
- a_shared=$($BTRFS_UTIL_PROG qgroup show $units $SCRATCH_MNT | grep "0/$subvolid")
- a_shared=$(echo $a_shared | $AWK_PROG '{ print $2 }')
- echo "subvol a id=$subvolid" >> $seqres.full
- subvolid=$(_btrfs_get_subvolid $SCRATCH_MNT b)
- echo "subvol b id=$subvolid" >> $seqres.full
- b_shared=$($BTRFS_UTIL_PROG qgroup show $units $SCRATCH_MNT | grep "0/$subvolid")
- b_shared=$(echo $b_shared | $AWK_PROG '{ print $2 }')
- $BTRFS_UTIL_PROG qgroup show $units $SCRATCH_MNT >> $seqres.full
- [ $b_shared -eq $a_shared ] || _fail "shared values don't match"
-}
-
-#enable quotas, do some work, check our values and then rescan and make sure we
-#come up with the same answer
-_rescan_test()
-{
- echo "=== rescan test ===" >> $seqres.full
- # first with a blank subvol
- _run_btrfs_util_prog subvolume create $SCRATCH_MNT/a
- _run_btrfs_util_prog quota enable $SCRATCH_MNT/a
- subvolid=$(_btrfs_get_subvolid $SCRATCH_MNT a)
- run_check $FSSTRESS_PROG -d $SCRATCH_MNT/a -w -p 1 -n 2000 \
- $FSSTRESS_AVOID
- sync
- output=$($BTRFS_UTIL_PROG qgroup show $units $SCRATCH_MNT | grep "0/$subvolid")
- echo "qgroup values before rescan: $output" >> $seqres.full
- refer=$(echo $output | $AWK_PROG '{ print $2 }')
- excl=$(echo $output | $AWK_PROG '{ print $3 }')
- _run_btrfs_util_prog quota rescan -w $SCRATCH_MNT
- output=$($BTRFS_UTIL_PROG qgroup show $units $SCRATCH_MNT | grep "0/$subvolid")
- echo "qgroup values after rescan: $output" >> $seqres.full
- [ $refer -eq $(echo $output | $AWK_PROG '{ print $2 }') ] || \
- _fail "reference values don't match after rescan"
- [ $excl -eq $(echo $output | $AWK_PROG '{ print $3 }') ] || \
- _fail "exclusive values don't match after rescan"
-}
+# This test requires specific data usage, skip if we have compression enabled
+_require_no_compress
#basic exceed limit testing
_limit_test_exceed()
_ddt of=$SCRATCH_MNT/a/file bs=10M count=1 >> $seqres.full 2>&1
[ $? -ne 0 ] || _fail "quota should have limited us"
}
-
-#basic noexceed limit testing
-_limit_test_noexceed()
-{
- echo "=== limit not exceed test ===" >> $seqres.full
- _run_btrfs_util_prog subvolume create $SCRATCH_MNT/a
- _run_btrfs_util_prog quota enable $SCRATCH_MNT
- subvolid=$(_btrfs_get_subvolid $SCRATCH_MNT a)
- _run_btrfs_util_prog qgroup limit 5M 0/$subvolid $SCRATCH_MNT
- _ddt of=$SCRATCH_MNT/a/file bs=4M count=1 >> $seqres.full 2>&1
- [ $? -eq 0 ] || _fail "should have been allowed to write"
-}
-
units=`_btrfs_qgroup_units`
-_scratch_mkfs > /dev/null 2>&1
-_scratch_mount
-_basic_test
-_scratch_unmount
-_check_scratch_fs
-
-_scratch_mkfs > /dev/null 2>&1
-_scratch_mount
-_rescan_test
-_scratch_unmount
-_check_scratch_fs
-
_scratch_mkfs > /dev/null 2>&1
_scratch_mount
_limit_test_exceed
_scratch_unmount
_check_scratch_fs
-_scratch_mkfs > /dev/null 2>&1
-_scratch_mount
-_limit_test_noexceed
-
# success, all done
echo "Silence is golden"
status=0
# real QA test starts here
_supported_fs btrfs
_require_scratch
+_require_btrfs_no_nodatacow
__workout()
{
# causing the receive command to abort immediately.
#
. ./common/preamble
-_begin_fstest auto quick send clone
+_begin_fstest auto quick send clone prealloc
tmp=`mktemp -d`
# Test replace of a missing device on various data and metadata profiles.
#
. ./common/preamble
-_begin_fstest auto replace volume
+_begin_fstest auto replace volume scrub
# Import common functions.
. ./common/filter
_scratch_mount
_run_btrfs_util_prog quota enable $SCRATCH_MNT
-_run_btrfs_util_prog quota rescan -w $SCRATCH_MNT
+_qgroup_rescan $SCRATCH_MNT
# Increase the probability of generating de-refer extent, and decrease
# other.
-f fsync=10 -n 100000 -p 2 \
-d $SCRATCH_MNT/stress_dir`
echo "Run fsstress $args" >>$seqres.full
-$FSSTRESS_PROG $args >/dev/null 2>&1 &
+$FSSTRESS_PROG $args >>$seqres.full &
fsstress_pid=$!
echo "Start balance" >>$seqres.full
# bad detection of file holes.
#
. ./common/preamble
-_begin_fstest auto quick send
+_begin_fstest auto quick send prealloc
tmp=`mktemp -d`
# Btrfs: fix data corruption when reading/updating compressed extents
#
. ./common/preamble
-_begin_fstest auto quick compress
+_begin_fstest auto quick compress prealloc
tmp=`mktemp -d`
# Btrfs: send, fix data corruption due to incorrect hole detection
#
. ./common/preamble
-_begin_fstest auto quick send
+_begin_fstest auto quick send preallocrw
tmp=`mktemp -d`
# btrfs: fix zstd compression parameter
#
. ./common/preamble
-_begin_fstest auto quick
+_begin_fstest auto quick compress subvol snapshot send
# Override the default cleanup function.
_cleanup()
_require_scratch
_require_btrfs_command "property"
_require_btrfs_command inspect-internal dump-super
+_require_btrfs_no_nodatacow
send_files_dir=$TEST_DIR/btrfs-test-$seq
swapfile="$SCRATCH_MNT/swap"
_scratch_pool_mkfs >/dev/null
_scratch_mount
-_format_swapfile "$swapfile" $(($(get_page_size) * 10)) >/dev/null
+_format_swapfile "$swapfile" $(($(_get_page_size) * 10)) >/dev/null
check_exclusive_ops()
{
-f write=10 -f creat=10 \
-n 1000 -p 2 -d $SCRATCH_MNT/stress_dir`
echo "Run fsstress $args" >>$seqres.full
-$FSSTRESS_PROG $args >/dev/null 2>&1
+$FSSTRESS_PROG $args >>$seqres.full
# Start and pause balance to ensure it will be restored on remount
echo "Start balance" >>$seqres.full
# file.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone compress
# Override the default cleanup function.
_cleanup()
rm -fr $send_files_dir
mkdir $send_files_dir
-_scratch_mkfs "-l $leaf_size" >/dev/null 2>&1
+_scratch_mkfs "--nodesize $leaf_size" >> $seqres.full 2>&1 || _fail "mkfs failed"
_scratch_mount
echo "hello world" > $SCRATCH_MNT/foobar
_scratch_unmount
_check_scratch_fs
-_scratch_mkfs "-l $leaf_size" >/dev/null 2>&1
+_scratch_mkfs "--nodesize $leaf_size" >> $seqres.full 2>&1 || _fail "mkfs failed"
_scratch_mount
_run_btrfs_util_prog receive -f $send_files_dir/1.snap $SCRATCH_MNT
# Btrfs: make fsync work after cloning into a file
#
. ./common/preamble
-_begin_fstest auto quick clone log
+_begin_fstest auto quick clone log compress
# Override the default cleanup function.
_cleanup()
# real QA test starts here
_supported_fs btrfs
_require_scratch
+_require_qgroup_rescan
_scratch_mkfs_sized $((1024 * 1024 * 1024)) >> $seqres.full 2>&1
# Btrfs: add missing compression property remove in btrfs_ioctl_setflags
#
. ./common/preamble
-_begin_fstest auto quick
+_begin_fstest auto quick compress
# Override the default cleanup function.
_cleanup()
_require_test
_require_scratch
_require_btrfs_command "property"
+_require_btrfs_no_nodatacow
_scratch_mkfs >> $seqres.full 2>&1
_scratch_mount
# with fsstress running in background.
#
. ./common/preamble
-_begin_fstest auto balance subvol
+_begin_fstest auto balance subvol scrub
# Import common functions.
. ./common/filter
args=`_scale_fsstress_args -p 20 -n 100 $FSSTRESS_AVOID -d $SCRATCH_MNT/stressdir`
echo "Run fsstress $args" >>$seqres.full
- $FSSTRESS_PROG $args >/dev/null 2>&1 &
+ $FSSTRESS_PROG $args >>$seqres.full &
fsstress_pid=$!
echo -n "Start balance worker: " >>$seqres.full
args=`_scale_fsstress_args -p 20 -n 100 $FSSTRESS_AVOID -d $SCRATCH_MNT/stressdir`
echo "Run fsstress $args" >>$seqres.full
- $FSSTRESS_PROG $args >/dev/null 2>&1 &
+ $FSSTRESS_PROG $args >>$seqres.full &
fsstress_pid=$!
echo -n "Start balance worker: " >>$seqres.full
# running in background.
#
. ./common/preamble
-_begin_fstest auto balance defrag compress
+_begin_fstest auto balance defrag compress scrub
# Import common functions.
. ./common/filter
args=`_scale_fsstress_args -p 20 -n 100 $FSSTRESS_AVOID -d $SCRATCH_MNT/stressdir`
echo "Run fsstress $args" >>$seqres.full
- $FSSTRESS_PROG $args >/dev/null 2>&1 &
+ $FSSTRESS_PROG $args >>$seqres.full &
fsstress_pid=$!
echo -n "Start balance worker: " >>$seqres.full
# simultaneously, with fsstress running in background.
#
. ./common/preamble
-_begin_fstest auto balance remount compress
+_begin_fstest auto balance remount compress scrub
# Import common functions.
. ./common/filter
args=`_scale_fsstress_args -p 20 -n 100 $FSSTRESS_AVOID -d $SCRATCH_MNT/stressdir`
echo "Run fsstress $args" >>$seqres.full
- $FSSTRESS_PROG $args >/dev/null 2>&1 &
+ $FSSTRESS_PROG $args >>$seqres.full &
fsstress_pid=$!
echo -n "Start balance worker: " >>$seqres.full
# run simultaneously. One of them is expected to fail when the other is running.
. ./common/preamble
-_begin_fstest auto balance replace volume
+_begin_fstest auto balance replace volume scrub
# Import common functions.
. ./common/filter
args=`_scale_fsstress_args -p 20 -n 100 $FSSTRESS_AVOID -d $SCRATCH_MNT/stressdir`
echo "Run fsstress $args" >>$seqres.full
- $FSSTRESS_PROG $args >/dev/null 2>&1 &
+ $FSSTRESS_PROG $args >>$seqres.full &
fsstress_pid=$!
# Start both balance and replace in the background.
# operation simultaneously, with fsstress running in background.
#
. ./common/preamble
-_begin_fstest auto subvol replace volume
+_begin_fstest auto subvol replace volume scrub
# Import common functions.
. ./common/filter
args=`_scale_fsstress_args -p 20 -n 100 $FSSTRESS_AVOID -d $SCRATCH_MNT/stressdir`
echo "Run fsstress $args" >>$seqres.full
- $FSSTRESS_PROG $args >/dev/null 2>&1 &
+ $FSSTRESS_PROG $args >>$seqres.full &
fsstress_pid=$!
# make sure the stop sign is not there
args=`_scale_fsstress_args -p 20 -n 100 $FSSTRESS_AVOID -d $SCRATCH_MNT/stressdir`
echo "Run fsstress $args" >>$seqres.full
- $FSSTRESS_PROG $args >/dev/null 2>&1 &
+ $FSSTRESS_PROG $args >>$seqres.full &
fsstress_pid=$!
# make sure the stop sign is not there
# operation simultaneously, with fsstress running in background.
#
. ./common/preamble
-_begin_fstest auto subvol defrag compress
+_begin_fstest auto subvol defrag compress scrub
# Import common functions.
. ./common/filter
args=`_scale_fsstress_args -p 20 -n 100 $FSSTRESS_AVOID -d $SCRATCH_MNT/stressdir`
echo "Run fsstress $args" >>$seqres.full
- $FSSTRESS_PROG $args >/dev/null 2>&1 &
+ $FSSTRESS_PROG $args >>$seqres.full &
fsstress_pid=$!
# make sure the stop sign is not there
# in background.
#
. ./common/preamble
-_begin_fstest auto subvol remount compress
+_begin_fstest auto subvol remount compress scrub
# Import common functions.
. ./common/filter
args=`_scale_fsstress_args -p 20 -n 100 $FSSTRESS_AVOID -d $SCRATCH_MNT/stressdir`
echo "Run fsstress $args" >>$seqres.full
- $FSSTRESS_PROG $args >/dev/null 2>&1 &
+ $FSSTRESS_PROG $args >>$seqres.full &
fsstress_pid=$!
# make sure the stop sign is not there
args=`_scale_fsstress_args -p 20 -n 100 $FSSTRESS_AVOID -d $SCRATCH_MNT/stressdir`
echo "Run fsstress $args" >>$seqres.full
- $FSSTRESS_PROG $args >/dev/null 2>&1 &
+ $FSSTRESS_PROG $args >>$seqres.full &
fsstress_pid=$!
echo -n "Start replace worker: " >>$seqres.full
# running in background.
#
. ./common/preamble
-_begin_fstest auto replace defrag compress volume
+_begin_fstest auto replace defrag compress volume scrub
# Import common functions.
. ./common/filter
args=`_scale_fsstress_args -p 20 -n 100 $FSSTRESS_AVOID -d $SCRATCH_MNT/stressdir`
echo "Run fsstress $args" >>$seqres.full
- $FSSTRESS_PROG $args >/dev/null 2>&1 &
+ $FSSTRESS_PROG $args >>$seqres.full &
fsstress_pid=$!
echo -n "Start replace worker: " >>$seqres.full
# algorithms simultaneously with fsstress running in background.
#
. ./common/preamble
-_begin_fstest auto replace remount compress volume
+_begin_fstest auto replace remount compress volume scrub
# Import common functions.
. ./common/filter
args=`_scale_fsstress_args -p 20 -n 100 $FSSTRESS_AVOID -d $SCRATCH_MNT/stressdir`
echo "Run fsstress $args" >>$seqres.full
- $FSSTRESS_PROG $args >/dev/null 2>&1 &
+ $FSSTRESS_PROG $args >>$seqres.full &
fsstress_pid=$!
echo -n "Start replace worker: " >>$seqres.full
args=`_scale_fsstress_args -p 20 -n 100 $FSSTRESS_AVOID -d $SCRATCH_MNT/stressdir`
echo "Run fsstress $args" >>$seqres.full
- $FSSTRESS_PROG $args >/dev/null 2>&1 &
+ $FSSTRESS_PROG $args >>$seqres.full &
fsstress_pid=$!
echo -n "Start scrub worker: " >>$seqres.full
args=`_scale_fsstress_args -p 20 -n 100 $FSSTRESS_AVOID -d $SCRATCH_MNT/stressdir`
echo "Run fsstress $args" >>$seqres.full
- $FSSTRESS_PROG $args >/dev/null 2>&1 &
+ $FSSTRESS_PROG $args >>$seqres.full &
fsstress_pid=$!
echo -n "Start scrub worker: " >>$seqres.full
# simultaneously with fsstress running in background.
#
. ./common/preamble
-_begin_fstest auto defrag remount compress
+_begin_fstest auto defrag remount compress scrub
# Import common functions.
. ./common/filter
args=`_scale_fsstress_args -p 20 -n 100 $FSSTRESS_AVOID -d $SCRATCH_MNT/stressdir`
echo "Run fsstress $args" >>$seqres.full
- $FSSTRESS_PROG $args >/dev/null 2>&1 &
+ $FSSTRESS_PROG $args >>$seqres.full &
fsstress_pid=$!
echo -n "Start defrag worker: " >>$seqres.full
# FS QA Test No. btrfs/076
#
# Regression test for btrfs incorrect inode ratio detection.
-# This was fixed in the following linux kernel patch:
-#
-# Btrfs: fix incorrect compression ratio detection
#
+
. ./common/preamble
_begin_fstest auto quick compress
_supported_fs btrfs
_require_test
_require_scratch
+_fixed_by_kernel_commit 4bcbb3325513 \
+ "Btrfs: fix incorrect compression ratio detection"
+
+# An extent size can be up to BTRFS_MAX_UNCOMPRESSED
+max_extent_size=$(( 128 * 1024 ))
+if _scratch_btrfs_is_zoned; then
+ zone_append_max=$(cat "/sys/block/$(_short_dev $SCRATCH_DEV)/queue/zone_append_max_bytes")
+ if [[ $zone_append_max -gt 0 && $zone_append_max -lt $max_extent_size ]]; then
+ # Round down to PAGE_SIZE
+ max_extent_size=$(( $zone_append_max / 4096 * 4096 ))
+ fi
+fi
+file_size=$(( 10 * 1024 * 1024 ))
+expect=$(( (file_size + max_extent_size - 1) / max_extent_size ))
_scratch_mkfs >> $seqres.full 2>&1
_scratch_mount "-o compress=lzo"
$XFS_IO_PROG -f -c "pwrite 0 10M" -c "fsync" \
$SCRATCH_MNT/data >> $seqres.full 2>&1
-_extent_count $SCRATCH_MNT/data
+res=$(_extent_count $SCRATCH_MNT/data)
+if [[ $res -ne $expect ]]; then
+ _fail "Expected $expect extents, got $res"
+fi
$XFS_IO_PROG -f -c "pwrite 0 $((4096*33))" -c "fsync" \
$SCRATCH_MNT/data >> $seqres.full 2>&1
$XFS_IO_PROG -f -c "pwrite 0 10M" -c "fsync" \
$SCRATCH_MNT/data >> $seqres.full 2>&1
-_extent_count $SCRATCH_MNT/data
+res=$(_extent_count $SCRATCH_MNT/data)
+if [[ $res -ne $expect ]]; then
+ _fail "Expected $expect extents, got $res"
+fi
+echo "Silence is golden"
status=0
exit
QA output created by 076
-80
-80
+Silence is golden
# btrfs: Fix the wrong condition judgment about subset extent map
#
. ./common/preamble
-_begin_fstest auto rw metadata
+_begin_fstest auto rw metadata fiemap prealloc
# Override the default cleanup function.
_cleanup()
# created fs doesn't get that feature enabled. With it enabled, the below fsck
# call wouldn't fail. This feature hasn't been enabled by default since it was
# introduced, but be safe and explicitly disable it.
-_scratch_mkfs -O list-all 2>&1 | grep -q '\bno\-holes\b'
+_scratch_mkfs -O list-all 2>&1 | grep -q '\bno-holes\b'
if [ $? -eq 0 ]; then
mkfs_options="-O ^no-holes"
fi
# Import common functions.
. ./common/filter
+. ./common/fail_make_request
# real QA test starts here
_supported_fs btrfs
_require_scratch
_require_fail_make_request
-SYSFS_BDEV=`_sysfs_dev $SCRATCH_DEV`
-
enable_io_failure()
{
- echo 100 > $DEBUGFS_MNT/fail_make_request/probability
- echo 1000 > $DEBUGFS_MNT/fail_make_request/times
- echo 0 > $DEBUGFS_MNT/fail_make_request/verbose
- echo 1 > $SYSFS_BDEV/make-it-fail
+ _allow_fail_make_request 100 1000 > /dev/null
+ _start_fail_scratch_dev > /dev/null
}
disable_io_failure()
{
- echo 0 > $SYSFS_BDEV/make-it-fail
- echo 0 > $DEBUGFS_MNT/fail_make_request/probability
- echo 0 > $DEBUGFS_MNT/fail_make_request/times
+ _stop_fail_scratch_dev > /dev/null
+ _disallow_fail_make_request > /dev/null
}
# We will abort a btrfs transaction later, which always produces a warning in
_supported_fs btrfs
_require_scratch
_require_cp_reflink
+_require_scratch_qgroup
# use largest node/leaf size (64K) to allow the test to be run on arch with
# page size > 4k.
_run_btrfs_util_prog subvolume create $SCRATCH_MNT/subv3
_run_btrfs_util_prog quota enable $SCRATCH_MNT
-_run_btrfs_util_prog quota rescan -w $SCRATCH_MNT
+_qgroup_rescan $SCRATCH_MNT
$XFS_IO_PROG -f -c "pwrite 0 256K" $SCRATCH_MNT/subv1/file1 | _filter_xfs_io
cp --reflink $SCRATCH_MNT/subv1/file1 $SCRATCH_MNT/subv2/file1
# Btrfs: incremental send, fix clone operations for compressed extents
#
. ./common/preamble
-_begin_fstest auto quick send
+_begin_fstest auto quick send compress
# Override the default cleanup function.
_cleanup()
# The regression was introduced in the 4.2-rc1 Linux kernel.
#
. ./common/preamble
-_begin_fstest auto quick metadata log
+_begin_fstest auto quick metadata log preallocrw
# Override the default cleanup function.
_cleanup()
$BTRFS_UTIL_PROG filesystem show -m $SCRATCH_MNT | _filter_btrfs_filesystem_show
error_devid=`$BTRFS_UTIL_PROG filesystem show -m $SCRATCH_MNT |\
- egrep $DMERROR_DEV | $AWK_PROG '{print $2}'`
+ grep -E $DMERROR_DEV | $AWK_PROG '{print $2}'`
snapshot_cmd="$BTRFS_UTIL_PROG subvolume snapshot -r $SCRATCH_MNT"
snapshot_cmd="$snapshot_cmd $SCRATCH_MNT/snap_\`date +'%H_%M_%S_%N'\`"
$BTRFS_UTIL_PROG filesystem show -m $SCRATCH_MNT | _filter_btrfs_filesystem_show
error_devid=`$BTRFS_UTIL_PROG filesystem show -m $SCRATCH_MNT |\
- egrep $DMERROR_DEV | $AWK_PROG '{print $2}'`
+ grep -E $DMERROR_DEV | $AWK_PROG '{print $2}'`
snapshot_cmd="$BTRFS_UTIL_PROG subvolume snapshot -r $SCRATCH_MNT"
snapshot_cmd="$snapshot_cmd $SCRATCH_MNT/snap_\`date +'%H_%M_%S_%N'\`"
# Enable qgroups now that we have our filesystem prepared. This
# will kick off a scan which we will have to wait for.
_run_btrfs_util_prog quota enable $SCRATCH_MNT
-_run_btrfs_util_prog quota rescan -w $SCRATCH_MNT
+_qgroup_rescan $SCRATCH_MNT
# Remount to clear cache, force everything to disk
_scratch_cycle_mount
_scratch_mkfs >>$seqres.full 2>&1
_scratch_mount $mount_opts
- PAGE_SIZE=$(get_page_size)
+ PAGE_SIZE=$(_get_page_size)
# Create our test file with 16 pages worth of data in a single extent
# that is going to be compressed no matter which compression algorithm
$CLONER_PROG -s 0 -d $((16 * $PAGE_SIZE)) -l $((16 * $PAGE_SIZE)) \
$SCRATCH_MNT/foo $SCRATCH_MNT/foo
- echo "File contents before unmount:"
- od -t x1 $SCRATCH_MNT/foo | _filter_od
+ echo "Hash before unmount:" >> $seqres.full
+ old_md5=$(_md5_checksum "$SCRATCH_MNT/foo")
+ echo "$old_md5" >> $seqres.full
# Remount the fs or clear the page cache to trigger the bug in btrfs.
# Because the extent has an uncompressed length that is a multiple of 16
# correctly.
_scratch_cycle_mount
- echo "File contents after remount:"
+ echo "Hash after remount:" >> $seqres.full
# Must match the digest we got before.
- od -t x1 $SCRATCH_MNT/foo | _filter_od
+ new_md5=$(_md5_checksum "$SCRATCH_MNT/foo")
+ echo "$new_md5" >> $seqres.full
+ if [ "$old_md5" != "$new_md5" ]; then
+ echo "Hash mismatches after remount"
+ else
+ echo "Hash matches after remount"
+ fi
}
echo -e "\nTesting with zlib compression..."
Testing with zlib compression...
Pages modified: [0 - 15]
-File contents before unmount:
-0 aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa
-*
-40
-File contents after remount:
-0 aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa
-*
-40
+Hash matches after remount
Testing with lzo compression...
Pages modified: [0 - 15]
-File contents before unmount:
-0 aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa
-*
-40
-File contents after remount:
-0 aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa
-*
-40
+Hash matches after remount
# corruption or data loss.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc compress
# Import common functions.
. ./common/filter
_scratch_mkfs >/dev/null
_scratch_mount
_run_btrfs_util_prog quota enable $SCRATCH_MNT
-# The qgroup '1/10' does not exist and should be silently ignored
-_run_btrfs_util_prog subvolume snapshot -i 1/10 $SCRATCH_MNT $SCRATCH_MNT/snap1
+# The qgroup '1/10' does not exist. The kernel should either gives an error
+# (newer kernel with invalid qgroup detection) or ignore it (older kernel with
+# above fix).
+# Either way, we just ignore the output completely, and we will check if the fs
+# is still RW later.
+$BTRFS_UTIL_PROG subvolume snapshot -i 1/10 $SCRATCH_MNT $SCRATCH_MNT/snap1 >> $seqres.full 2>&1
+
+touch $SCRATCH_MNT/foobar
echo "Silence is golden"
_require_scratch
_require_btrfs_qgroup_report
-# Force a small leaf size to make it easier to blow out our root
-# subvolume tree
-_scratch_mkfs "--nodesize 16384" >/dev/null
+_scratch_mkfs >> $seqres.full
_scratch_mount
_run_btrfs_util_prog quota enable $SCRATCH_MNT
# enable quota and rescan to get correct number
_run_btrfs_util_prog quota enable $SCRATCH_MNT
-_run_btrfs_util_prog quota rescan -w $SCRATCH_MNT
+_qgroup_rescan $SCRATCH_MNT
# now balance data block groups to corrupt qgroup
_run_btrfs_balance_start -d $SCRATCH_MNT >> $seqres.full
# Verify if all three checkpoints match
#
. ./common/preamble
-_begin_fstest replace volume balance
+_begin_fstest replace volume balance auto quick
# Override the default cleanup function.
_cleanup()
_scratch_mount "-o enospc_debug"
_run_btrfs_util_prog quota enable $SCRATCH_MNT
-_run_btrfs_util_prog quota rescan -w $SCRATCH_MNT
+_qgroup_rescan $SCRATCH_MNT
_run_btrfs_util_prog qgroup limit 512K 0/5 $SCRATCH_MNT
# The amount of written data may change due to different nodesize at mkfs time,
_require_btrfs_fs_feature free_space_tree
# Zoned btrfs does not support space_cache(v1)
_require_non_zoned_device "${SCRATCH_DEV}"
+# Block group tree does not support space_cache(v1)
+_require_btrfs_no_block_group_tree
+
+_scratch_mkfs >/dev/null 2>&1
+[ "$(_get_page_size)" -gt "$(_scratch_btrfs_sectorsize)" ] && \
+ _notrun "cannot run with subpage sectorsize"
mkfs_v1()
{
mkdir -p $data_path
args=`_scale_fsstress_args -p 20 -n 100 $FSSTRESS_AVOID -d $data_path`
echo "Run fsstress $args" >>$seqres.full
- $FSSTRESS_PROG $args >/dev/null 2>&1 &
+ $FSSTRESS_PROG $args >>$seqres.full &
fsstress_pid=$!
wait $fsstress_pid
}
# Test that both incremental and full send operations preserve file holes.
#
. ./common/preamble
-_begin_fstest auto quick send
+_begin_fstest auto quick send fiemap
# Override the default cleanup function.
_cleanup()
_supported_fs btrfs
_require_scratch
_require_btrfs_command property
+_require_btrfs_no_nodatacow
algos=($(_btrfs_compression_algos))
_run_btrfs_util_prog subvolume create $SUBVOL
_run_btrfs_util_prog quota enable $SCRATCH_MNT
-_run_btrfs_util_prog quota rescan -w $SCRATCH_MNT
+_qgroup_rescan $SCRATCH_MNT
_run_btrfs_util_prog qgroup limit -e 1G $SUBVOL
# Write and delete files within 1G limits, multiple times
# commit 2e949b0a5592 ("Btrfs: fix invalid dereference in btrfs_retry_endio")
#
. ./common/preamble
-_begin_fstest auto quick read_repair
+_begin_fstest auto quick read_repair fiemap
# Import common functions.
. ./common/filter
# Modify as appropriate.
_supported_fs btrfs
_require_scratch_dev_pool 2
-
+# No data checksums for NOCOW case, so can't detect corruption and repair data.
+_require_btrfs_no_nodatacow
_require_btrfs_command inspect-internal dump-tree
-_require_command "$FILEFRAG_PROG" filefrag
_require_odirect
# Overwriting data is forbidden on a zoned block device
_require_non_zoned_device "${SCRATCH_DEV}"
echo "step 2......corrupt file extent" >>$seqres.full
${FILEFRAG_PROG} -v $SCRATCH_MNT/foobar >> $seqres.full
-logical_in_btrfs=`${FILEFRAG_PROG} -v $SCRATCH_MNT/foobar | _filter_filefrag | cut -d '#' -f 1`
+logical_in_btrfs=$(_btrfs_get_first_logical $SCRATCH_MNT/foobar)
physical=$(get_physical ${logical_in_btrfs} 1)
devid=$(get_devid ${logical_in_btrfs} 1)
devpath=$(get_device_path ${devid})
# step 3, 128k dio read (this read can repair bad copy)
echo "step 3......repair the bad copy" >>$seqres.full
-# since raid1 consists of two copies, and the bad copy was put on stripe #1
-# while the good copy lies on stripe #0, the bad copy only gets access when the
-# reader's pid % 2 == 1 is true
-while true; do
- $XFS_IO_PROG -d -c "pread -b 128K 0 128K" "$SCRATCH_MNT/foobar" > /dev/null &
- pid=$!
- wait
- [ $((pid % 2)) == 1 ] && break
-done
+_btrfs_direct_read_on_mirror 1 2 "$SCRATCH_MNT/foobar" 0 128K
_scratch_unmount
# Modify as appropriate.
_supported_fs btrfs
+# No data checksums for NOCOW case, so can't detect corruption and repair data.
+_require_btrfs_no_nodatacow
_require_scratch_dev_pool 2
_require_btrfs_command inspect-internal dump-tree
-_require_command "$FILEFRAG_PROG" filefrag
get_physical()
{
# one in $SCRATCH_DEV_POOL
echo "step 2......corrupt file extent" >>$seqres.full
-${FILEFRAG_PROG} -v $SCRATCH_MNT/foobar >> $seqres.full
-logical_in_btrfs=`${FILEFRAG_PROG} -v $SCRATCH_MNT/foobar | _filter_filefrag | cut -d '#' -f 1`
+logical_in_btrfs=$(_btrfs_get_first_logical $SCRATCH_MNT/foobar)
physical=$(get_physical ${logical_in_btrfs} 1)
devid=$(get_devid ${logical_in_btrfs} 1)
devpath=$(get_device_path ${devid})
# step 3, 128k buffered read (this read can repair bad copy)
echo "step 3......repair the bad copy" >>$seqres.full
-# since raid1 consists of two copies, and the bad copy was put on stripe #1
-# while the good copy lies on stripe #0, the bad copy only gets access when the
-# reader's pid % 2 == 1 is true
-while true; do
- echo 3 > /proc/sys/vm/drop_caches
- $XFS_IO_PROG -c "pread -b 128K 0 128K" "$SCRATCH_MNT/foobar" > /dev/null &
- pid=$!
- wait
- [ $((pid % 2)) == 1 ] && break
-done
+_btrfs_buffered_read_on_mirror 1 2 "$SCRATCH_MNT/foobar" 0 128K
_scratch_unmount
_require_dm_target dust
_require_btrfs_command inspect-internal dump-tree
-_require_command "$FILEFRAG_PROG" filefrag
_scratch_dev_pool_get 2
# step 1, create a raid1 btrfs which contains one 128k file.
# step 2, corrupt the first 64k of stripe #1
echo "step 2......corrupt file extent" >>$seqres.full
-${FILEFRAG_PROG} -v $SCRATCH_MNT/foobar >> $seqres.full
-logical_in_btrfs=`${FILEFRAG_PROG} -v $SCRATCH_MNT/foobar | _filter_filefrag | cut -d '#' -f 1`
+logical_in_btrfs=$(_btrfs_get_first_logical $SCRATCH_MNT/foobar)
echo "Logical offset is $logical_in_btrfs" >>$seqres.full
_scratch_unmount
$DMSETUP_PROG message dust-test 0 addbadblock $((physical / 512))
$DMSETUP_PROG message dust-test 0 enable
-while [[ -z $( (( BASHPID % 2 == stripe )) &&
- exec $XFS_IO_PROG -d -c "pread -b 128K 0 128K" "$SCRATCH_MNT/foobar") ]]; do
- :
-done
+
+_btrfs_direct_read_on_mirror $stripe 2 "$SCRATCH_MNT/foobar" 0 128K
_cleanup_dust
_require_dm_target dust
_require_btrfs_command inspect-internal dump-tree
-_require_command "$FILEFRAG_PROG" filefrag
_scratch_dev_pool_get 2
# step 1, create a raid1 btrfs which contains one 128k file.
# step 2, corrupt the first 64k of stripe #1
echo "step 2......corrupt file extent" >>$seqres.full
-${FILEFRAG_PROG} -v $SCRATCH_MNT/foobar >> $seqres.full
-logical_in_btrfs=`${FILEFRAG_PROG} -v $SCRATCH_MNT/foobar | _filter_filefrag | cut -d '#' -f 1`
+logical_in_btrfs=$(_btrfs_get_first_logical $SCRATCH_MNT/foobar)
echo "Logical offset is $logical_in_btrfs" >>$seqres.full
_scratch_unmount
$DMSETUP_PROG message dust-test 0 addbadblock $((physical / 512))
$DMSETUP_PROG message dust-test 0 enable
-while [[ -z $( (( BASHPID % 2 == stripe )) &&
- exec $XFS_IO_PROG -c "pread -b 128K 0 128K" "$SCRATCH_MNT/foobar") ]]; do
- :
-done
+
+_btrfs_buffered_read_on_mirror $stripe 2 "$SCRATCH_MNT/foobar" 0 128K
_cleanup_dust
# Test that direct IO writes work on RAID5 and RAID6 filesystems.
#
. ./common/preamble
-_begin_fstest auto quick rw
+_begin_fstest auto quick rw scrub
# Import common functions.
. ./common/filter
# Btrfs: fix kernel oops while reading compressed data
#
. ./common/preamble
-_begin_fstest auto quick dangerous read_repair
+_begin_fstest auto quick dangerous read_repair compress
# Import common functions.
. ./common/filter
+. ./common/fail_make_request
# real QA test starts here
_require_scratch_dev_pool 2
_scratch_dev_pool_get 2
-SYSFS_BDEV=`_sysfs_dev $SCRATCH_DEV`
enable_io_failure()
{
- echo 100 > $DEBUGFS_MNT/fail_make_request/probability
- echo 1000 > $DEBUGFS_MNT/fail_make_request/times
- echo 0 > $DEBUGFS_MNT/fail_make_request/verbose
+ _allow_fail_make_request 100 1000 > /dev/null
echo 1 > $DEBUGFS_MNT/fail_make_request/task-filter
- echo 1 > $SYSFS_BDEV/make-it-fail
+ _start_fail_scratch_dev > /dev/null
}
disable_io_failure()
{
- echo 0 > $DEBUGFS_MNT/fail_make_request/probability
- echo 0 > $DEBUGFS_MNT/fail_make_request/times
+ _disallow_fail_make_request > /dev/null
echo 0 > $DEBUGFS_MNT/fail_make_request/task-filter
- echo 0 > $SYSFS_BDEV/make-it-fail
+ _stop_fail_scratch_dev > /dev/null
}
_check_minimal_fs_size $(( 1024 * 1024 * 1024 ))
# Test for leaking quota reservations on preallocated files.
#
. ./common/preamble
-_begin_fstest auto quick qgroup limit
+_begin_fstest auto quick qgroup limit preallocrw
# Import common functions.
. ./common/filter
_scratch_mount
_run_btrfs_util_prog quota enable $SCRATCH_MNT
-_run_btrfs_util_prog quota rescan -w $SCRATCH_MNT
+_qgroup_rescan $SCRATCH_MNT
_run_btrfs_util_prog qgroup limit 100M 0/5 $SCRATCH_MNT
testfile1=$SCRATCH_MNT/testfile1
_supported_fs btrfs
_require_scratch
-_require_command $PYTHON2_PROG python2
+_require_command $PYTHON3_PROG python3
# Currently in btrfs the node/leaf size can not be smaller than the page
# size (but it can be greater than the page size). So use the largest
# ...
#
-$PYTHON2_PROG $here/src/btrfs_crc32c_forged_name.py -d $SCRATCH_MNT -c 310
+$PYTHON3_PROG $here/src/btrfs_crc32c_forged_name.py -d $SCRATCH_MNT -c 310
echo "Silence is golden"
# success, all done
# Modify as appropriate.
_supported_fs btrfs
+# No data checksums for NOCOW case, so can't detect corruption and repair data.
+_require_btrfs_no_nodatacow
_require_scratch_dev_pool 4
_require_btrfs_command inspect-internal dump-tree
_require_btrfs_fs_feature raid56
$XFS_IO_PROG -f -d -c "pwrite -S 0xaa 0 128K" -c "fsync" \
"$SCRATCH_MNT/foobar" | _filter_xfs_io
-logical=`${FILEFRAG_PROG} -v $SCRATCH_MNT/foobar | _filter_filefrag | cut -d '#' -f 1`
+logical=$(_btrfs_get_first_logical $SCRATCH_MNT/foobar)
_scratch_unmount
phy0=$(get_physical 0)
# Modify as appropriate.
_supported_fs btrfs
+# No data checksums for NOCOW case, so can't detect corruption and repair data.
+_require_btrfs_no_nodatacow
_require_scratch_dev_pool 4
_require_btrfs_command inspect-internal dump-tree
_require_btrfs_fs_feature raid56
# c6a5d954950c btrfs: fail replace of seed device
. ./common/preamble
-_begin_fstest auto quick volume seed
+_begin_fstest auto quick volume seed remount
# Override the default cleanup function.
_cleanup()
# Remount RW
# Run device delete on the seed device
. ./common/preamble
-_begin_fstest auto quick volume
+_begin_fstest auto quick volume remount
# Override the default cleanup function.
_cleanup()
# ac0b4145d662 ("btrfs: scrub: Don't use inode pages for device replace")
#
. ./common/preamble
-_begin_fstest auto quick replace volume
+_begin_fstest auto quick replace volume remount compress
# Import common functions.
. ./common/filter
# in a section of that prealloc extent.
#
. ./common/preamble
-_begin_fstest auto quick send
+_begin_fstest auto quick send prealloc punch
# Override the default cleanup function.
_cleanup()
# subvolume, after a clean shutdown the data was not lost.
#
. ./common/preamble
-_begin_fstest auto quick snapshot
+_begin_fstest auto quick snapshot prealloc
# Import common functions.
. ./common/filter
"$SCRATCH_MNT/snapshot" > /dev/null
$BTRFS_UTIL_PROG quota enable "$SCRATCH_MNT" > /dev/null
-$BTRFS_UTIL_PROG quota rescan -w "$SCRATCH_MNT" > /dev/null
+_qgroup_rescan $SCRATCH_MNT > /dev/null
# Create high level qgroup
$BTRFS_UTIL_PROG qgroup create 1/0 "$SCRATCH_MNT"
# Above assignment will mark qgroup inconsistent due to the shared extents
# between subvol/snapshot/high level qgroup, do rescan here.
-$BTRFS_UTIL_PROG quota rescan -w "$SCRATCH_MNT" > /dev/null
+_qgroup_rescan $SCRATCH_MNT > /dev/null
# Now remove the qgroup relationship and make 1/0 childless
# Due to the shared extent outside of 1/0, we will mark qgroup inconsistent
2>&1 | _filter_btrfs_qgroup_assign_warnings
# Above removal also marks qgroup inconsistent, rescan again
-$BTRFS_UTIL_PROG quota rescan -w "$SCRATCH_MNT" > /dev/null
+_qgroup_rescan $SCRATCH_MNT > /dev/null
# After the test, btrfs check will verify qgroup numbers to catch any
# corruption.
. ./common/preamble
_begin_fstest auto quick log replay recoveryloop
+_cleanup()
+{
+ cd /
+ _log_writes_cleanup &> /dev/null
+ rm -f $tmp.*
+}
+
# Import common functions.
. ./common/filter
. ./common/dmlogwrites
_require_log_writes
_require_xfs_io_command "sync_range"
+# block-group-tree requires no-holes
+_require_btrfs_no_block_group_tree
+
_log_writes_init $SCRATCH_DEV
_log_writes_mkfs "-O ^no-holes" >> $seqres.full 2>&1
# CoW file nor compressed file.
#
. ./common/preamble
-_begin_fstest auto quick swap
+_begin_fstest auto quick swap compress
. ./common/filter
# unset it after the swap file has been created.
rm -f "$SCRATCH_MNT/swap"
touch "$SCRATCH_MNT/swap"
+# Make sure we have a COW file if we were mounted with "-o nodatacow".
+if _normalize_mount_options "$MOUNT_OPTIONS" | grep -q "nodatacow"; then
+ _require_chattr C
+ $CHATTR_PROG -C "$SCRATCH_MNT/swap"
+fi
chmod 0600 "$SCRATCH_MNT/swap"
-_pwrite_byte 0x61 0 $(($(get_page_size) * 10)) "$SCRATCH_MNT/swap" >> $seqres.full
+_pwrite_byte 0x61 0 $(($(_get_page_size) * 10)) "$SCRATCH_MNT/swap" >> $seqres.full
$MKSWAP_PROG "$SCRATCH_MNT/swap" >> $seqres.full
swapon "$SCRATCH_MNT/swap" 2>&1 | _filter_scratch
swapoff "$SCRATCH_MNT/swap" >/dev/null 2>&1
echo "Compressed file"
rm -f "$SCRATCH_MNT/swap"
-_format_swapfile "$SCRATCH_MNT/swap" $(($(get_page_size) * 10)) > /dev/null
+_format_swapfile "$SCRATCH_MNT/swap" $(($(_get_page_size) * 10)) > /dev/null
$CHATTR_PROG +c "$SCRATCH_MNT/swap" 2>&1 | grep -o "Invalid argument while setting flags"
status=0
# specific to Btrfs.
#
. ./common/preamble
-_begin_fstest auto quick swap
+_begin_fstest auto quick swap compress
. ./common/filter
$BTRFS_UTIL_PROG subvolume create "$SCRATCH_MNT/swapvol" >> $seqres.full
swapfile="$SCRATCH_MNT/swapvol/swap"
-_format_swapfile "$swapfile" $(($(get_page_size) * 10)) > /dev/null
+_format_swapfile "$swapfile" $(($(_get_page_size) * 10)) > /dev/null
swapon "$swapfile"
# Turning off nocow doesn't do anything because the file is not empty, not
_check_minimal_fs_size $((1024 * 1024 * 1024))
cycle_swapfile() {
- local sz=${1:-$(($(get_page_size) * 10))}
+ local sz=${1:-$(($(_get_page_size) * 10))}
_format_swapfile "$SCRATCH_MNT/swap" "$sz" > /dev/null
swapon "$SCRATCH_MNT/swap" 2>&1 | _filter_scratch
swapoff "$SCRATCH_MNT/swap" > /dev/null 2>&1
_scratch_mount
# Create the swap file, then add the device. That way we know it's all on one
# device.
-_format_swapfile "$SCRATCH_MNT/swap" $(($(get_page_size) * 10)) > /dev/null
+_format_swapfile "$SCRATCH_MNT/swap" $(($(_get_page_size) * 10)) > /dev/null
scratch_dev2="$(echo "${SCRATCH_DEV_POOL}" | $AWK_PROG '{ print $2 }')"
$BTRFS_UTIL_PROG device add -f "$scratch_dev2" "$SCRATCH_MNT" >> $seqres.full
swapon "$SCRATCH_MNT/swap" 2>&1 | _filter_scratch
echo "Remove device"
_scratch_mkfs >> $seqres.full 2>&1
_scratch_mount
-_format_swapfile "$SCRATCH_MNT/swap" $(($(get_page_size) * 10)) > /dev/null
+_format_swapfile "$SCRATCH_MNT/swap" $(($(_get_page_size) * 10)) > /dev/null
$BTRFS_UTIL_PROG device add -f "$scratch_dev2" "$SCRATCH_MNT" >> $seqres.full
swapon "$SCRATCH_MNT/swap" 2>&1 | _filter_scratch
# We know the swap file is on device 1 because we added device 2 after it was
echo "Replace device"
_scratch_mkfs >> $seqres.full 2>&1
_scratch_mount
-_format_swapfile "$SCRATCH_MNT/swap" $(($(get_page_size) * 10)) > /dev/null
+_format_swapfile "$SCRATCH_MNT/swap" $(($(_get_page_size) * 10)) > /dev/null
$BTRFS_UTIL_PROG device add -f "$scratch_dev2" "$SCRATCH_MNT" >> $seqres.full
swapon "$SCRATCH_MNT/swap" 2>&1 | _filter_scratch
# Again, we know the swap file is on device 1.
_begin_fstest auto quick swap balance
. ./common/filter
-. ./common/btrfs
# Modify as appropriate.
_supported_fs btrfs
mkdir -p "$SCRATCH_MNT/snapshots"
$BTRFS_UTIL_PROG subvolume create "$SCRATCH_MNT/src" > /dev/null
$BTRFS_UTIL_PROG quota enable "$SCRATCH_MNT" > /dev/null
-$BTRFS_UTIL_PROG quota rescan -w "$SCRATCH_MNT" > /dev/null
+_qgroup_rescan "$SCRATCH_MNT" > /dev/null
fill_workload()
{
# Randomly remove some files for every 5 loop
if [ $(( $i % 5 )) -eq 0 ]; then
- victim=$(ls "$SCRATCH_MNT/src" | sort -R | head -n1)
- rm "$SCRATCH_MNT/src/$victim"
+ victim=$(_random_file "$SCRATCH_MNT/src")
+ rm "$victim"
fi
i=$((i + 1))
done
trap "wait; exit" SIGTERM
while true; do
sleep $((sleep_time * 2))
- victim=$(ls "$SCRATCH_MNT/snapshots" | sort -R | head -n1)
+ victim=$(_random_file "$SCRATCH_MNT/snapshots")
if [ -z "$victim" ]; then
# No snapshots available, sleep and retry later.
continue
fi
- $BTRFS_UTIL_PROG subvolume delete \
- "$SCRATCH_MNT/snapshots/$victim" > /dev/null
+ $BTRFS_UTIL_PROG subvolume delete "$victim" > /dev/null
done
}
# "btrfs: qgroup: Make qgroup async transaction commit more aggressive"
#
. ./common/preamble
-_begin_fstest auto quick qgroup limit
+_begin_fstest auto quick qgroup limit prealloc
# Import common functions.
. ./common/filter
_scratch_mount
$BTRFS_UTIL_PROG quota enable "$SCRATCH_MNT" > /dev/null
-$BTRFS_UTIL_PROG quota rescan -w "$SCRATCH_MNT" > /dev/null
+_qgroup_rescan "$SCRATCH_MNT" > /dev/null
$BTRFS_UTIL_PROG qgroup limit -e 1G "$SCRATCH_MNT"
$XFS_IO_PROG -f -c "falloc 0 900M" "$SCRATCH_MNT/padding"
# a9261d4125c9 ("btrfs: harden agaist duplicate fsid on scanned devices")
#
. ./common/preamble
-_begin_fstest volume
+_begin_fstest volume auto quick
mnt=$TEST_DIR/$seq.mnt
# Override the default cleanup function.
_cleanup()
{
+ $UMOUNT_PROG $mnt > /dev/null 2>&1
rm -rf $mnt > /dev/null 2>&1
cd /
rm -f $tmp.*
_supported_fs btrfs
_require_scratch_dev_pool 2
_scratch_dev_pool_get 2
+_fixed_by_kernel_commit a9261d4125c9 \
+ "btrfs: harden agaist duplicate fsid on scanned devices"
device_1=$(echo $SCRATCH_DEV_POOL | $AWK_PROG '{print $1}')
device_2=$(echo $SCRATCH_DEV_POOL | $AWK_PROG '{print $2}')
echo ..:$? >> $seqres.full
done
-# Original device is mounted, scan of its clone should fail
+# Original device is mounted, scan of its clone must not alter the
+# filesystem device path
$BTRFS_UTIL_PROG device scan $device_2 >> $seqres.full 2>&1
-[[ $? != 1 ]] && _fail "cloned device scan should fail"
[[ $(findmnt $mnt | grep -v TARGET | $AWK_PROG '{print $2}') != $device_1 ]] && \
_fail "mounted device changed"
# BTRFS error (device sdc): parent transid verify failed on 32243712 wanted 24 \
# found 27
#
-_dmesg_since_test_start | egrep -e '\bBTRFS error \(device .*?\):'
+_dmesg_since_test_start | grep -E -e '\bBTRFS error \(device .*?\):'
status=0
exit
_log_writes_mount
$BTRFS_UTIL_PROG quota enable $SCRATCH_MNT >> $seqres.full
-$BTRFS_UTIL_PROG quota rescan -w $SCRATCH_MNT >> $seqres.full
+_qgroup_rescan $SCRATCH_MNT >> $seqres.full
# Create enough metadata for later balance
for ((i = 0; i < $nr_files; i++)); do
_require_attrs
# We require a 4K nodesize to ensure the test isn't too slow
-if [ $(get_page_size) -ne 4096 ]; then
+if [ $(_get_page_size) -ne 4096 ]; then
_notrun "This test doesn't support non-4K page size yet"
fi
mkdir -p $SCRATCH_MNT/snapshots
mkdir -p $SCRATCH_MNT/src/padding
-random_file()
-{
- local basedir=$1
- echo "$basedir/$(ls $basedir | sort -R | tail -1)"
-}
-
snapshot_workload()
{
trap "wait; exit" SIGTERM
$SCRATCH_MNT/src $SCRATCH_MNT/snapshots/$i \
> /dev/null
# Do something small to make snapshots different
- rm -f "$(random_file $SCRATCH_MNT/src/padding)"
- rm -f "$(random_file $SCRATCH_MNT/src/padding)"
- touch "$(random_file $SCRATCH_MNT/src/padding)"
+ rm -f "$(_random_file $SCRATCH_MNT/src/padding)"
+ rm -f "$(_random_file $SCRATCH_MNT/src/padding)"
+ touch "$(_random_file $SCRATCH_MNT/src/padding)"
touch "$SCRATCH_MNT/src/padding/random_$RANDOM"
i=$(($i + 1))
while true; do
sleep 2
$BTRFS_UTIL_PROG subvolume delete \
- "$(random_file $SCRATCH_MNT/snapshots)" \
+ "$(_random_file $SCRATCH_MNT/snapshots)" \
> /dev/null 2>&1
done
}
--replay $blkdev --check $check_point --fsck "$fsck_command" \
&> $tmp.full_fsck
ret=$?
- tail -n 150 $tmp.full_fsck > $seqres.full
+ tail -n 150 $tmp.full_fsck >> $seqres.full
[ $ret -ne 0 ] && _fail "fsck failed during replay"
}
delete_workload &
pid2=$!
-"$FSSTRESS_PROG" $fsstress_args > /dev/null &
+"$FSSTRESS_PROG" $fsstress_args >> $seqres.full &
sleep $runtime
"$KILLALL_PROG" -q "$FSSTRESS_PROG" &> /dev/null
# "btrfs: qgroup: Fix the wrong target io_tree when freeing reserved data space"
#
. ./common/preamble
-_begin_fstest auto quick qgroup enospc limit
+_begin_fstest auto quick qgroup enospc limit prealloc
# Import common functions.
. ./common/filter
_scratch_mount
$BTRFS_UTIL_PROG quota enable "$SCRATCH_MNT" > /dev/null
-$BTRFS_UTIL_PROG quota rescan -w "$SCRATCH_MNT" > /dev/null
+_qgroup_rescan "$SCRATCH_MNT" > /dev/null
$BTRFS_UTIL_PROG qgroup limit -e 256M "$SCRATCH_MNT"
# Create a file with the following layout:
# source profiles just rely on being able to read the data and metadata.
#
. ./common/preamble
-_begin_fstest auto volume balance
+_begin_fstest auto volume balance scrub
# Import common functions.
. ./common/filter
# Zoned btrfs only supports SINGLE profile
_require_non_zoned_device "${SCRATCH_DEV}"
+# Load up the available configs
+_btrfs_get_profile_configs
+
declare -a TEST_VECTORS=(
# $nr_dev_min:$data:$metadata:$data_convert:$metadata_convert
"4:single:raid1"
src_type=${args[1]}
dst_type=${args[2]}
+ if [[ ! "${_btrfs_profile_configs[@]}" =~ "$dst_type" ]]; then
+ echo "=== Skipping test: $1 ===" >> $seqres.full
+ return
+ fi
+
_scratch_dev_pool_get $num_disks
echo "=== Running test: $1 ===" >> $seqres.full
#
# FS QA Test 198
#
-# Test stale and alien non-btrfs device in the fs devices list.
-# Bug fixed in:
-# btrfs: remove identified alien device in open_fs_devices
+# Test outdated and foreign non-btrfs devices in the device listing.
#
. ./common/preamble
-_begin_fstest quick volume
+_begin_fstest auto quick volume
# Import common functions.
. ./common/filter
_require_scratch_dev_pool 4
# Zoned btrfs only supports SINGLE profile
_require_non_zoned_device ${SCRATCH_DEV}
+_fixed_by_kernel_commit 96c2e067ed3e3e \
+ "btrfs: skip devices without magic signature when mounting"
workout()
{
# There is a long existing bug that btrfs doesn't discard all space for
# above mentioned case.
#
-# The fix is: "btrfs: extent-tree: Ensure we trim ranges across block group
-# boundary"
-#
. ./common/preamble
-_begin_fstest auto quick trim
+_begin_fstest auto quick trim fiemap
# Override the default cleanup function.
_cleanup()
# Modify as appropriate.
_supported_fs btrfs
+_fixed_by_kernel_commit 6b7faadd985c \
+ "btrfs: Ensure we trim ranges across block group boundary"
+
_require_loop
_require_xfs_io_command "fiemap"
# operations for extents that are shared between the same file.
#
. ./common/preamble
-_begin_fstest auto quick send clone
+_begin_fstest auto quick send clone fiemap
# Override the default cleanup function.
_cleanup()
. ./common/filter
_supported_fs btrfs
+_require_scratch
_scratch_mkfs >> $seqres.full 2>&1
_scratch_mount
# "Btrfs: implement full reflink support for inline extents"
#
. ./common/preamble
-_begin_fstest auto quick clone compress
+_begin_fstest auto quick clone compress prealloc
# Import common functions.
. ./common/filter
# btrfs: replace all uses of btrfs_ordered_update_i_size
#
. ./common/preamble
-_begin_fstest auto quick log replay recoveryloop
+_begin_fstest auto quick log replay recoveryloop punch prealloc
+
+_cleanup()
+{
+ cd /
+ _log_writes_cleanup &> /dev/null
+ rm -f $tmp.*
+}
# Import common functions.
. ./common/filter
_require_xfs_io_command "falloc" "-k"
_require_xfs_io_command "fpunch"
+# block-group-tree requires no-holes
+_require_btrfs_no_block_group_tree
+
_log_writes_init $SCRATCH_DEV
_log_writes_mkfs "-O ^no-holes" >> $seqres.full 2>&1
local msg="$2"
SUBVOLID=$(_btrfs_get_subvolid $SCRATCH_MNT "$subvol_name")
- $BTRFS_UTIL_PROG subvolume delete --subvolid $SUBVOLID $SCRATCH_MNT | _filter_scratch
+ $BTRFS_UTIL_PROG subvolume delete --subvolid $SUBVOLID $SCRATCH_MNT | _filter_btrfs_subvol_delete
echo "$msg"
$BTRFS_UTIL_PROG subvolume list $SCRATCH_MNT | $AWK_PROG '{ print $NF }'
subvol1
subvol2
subvol3
-Delete subvolume (no-commit): 'SCRATCH_MNT/subvol1'
+Delete subvolume 'SCRATCH_MNT/subvol1'
After deleting one subvolume:
subvol2
subvol3
-Delete subvolume (no-commit): 'SCRATCH_MNT/subvol3'
+Delete subvolume 'SCRATCH_MNT/subvol3'
Last remaining subvolume:
subvol2
-Delete subvolume (no-commit): 'SCRATCH_MNT/subvol2'
+Delete subvolume 'SCRATCH_MNT/subvol2'
All subvolumes removed.
# by qgroup
sync
$BTRFS_UTIL_PROG quota enable "$SCRATCH_MNT"
-$BTRFS_UTIL_PROG quota rescan -w "$SCRATCH_MNT" > /dev/null
+_qgroup_rescan "$SCRATCH_MNT" > /dev/null
$BTRFS_UTIL_PROG qgroup create 1/0 "$SCRATCH_MNT"
# Create a snapshot with qgroup inherit
# the NO_HOLES feature.
#
. ./common/preamble
-_begin_fstest auto quick log prealloc
+_begin_fstest auto quick log prealloc fiemap
# Override the default cleanup function.
_cleanup()
# Test if canceling a running balance can lead to dead looping balance
#
. ./common/preamble
-_begin_fstest auto balance dangerous
+_begin_fstest auto balance
# Override the default cleanup function.
_cleanup()
_require_scratch
_require_xfs_io_command pwrite -D
+_fixed_by_kernel_commit 1dae7e0e58b4 \
+ "btrfs: reloc: clear DEAD_RELOC_TREE bit for orphan roots to prevent runaway balance"
+
_scratch_mkfs >> $seqres.full
_scratch_mount
-runtime=4
+max_space=$(_get_total_space $SCRATCH_MNT)
+max_space=$(( max_space / 2 ))
-# Create enough IO so that we need around $runtime seconds to relocate it.
-#
-# Here we don't want any wrapper, as we want full control of the process.
-$XFS_IO_PROG -f -c "pwrite -D -b 1M 0 1024T" "$SCRATCH_MNT/file" &> /dev/null &
-write_pid=$!
-sleep $runtime
-kill $write_pid
-wait $write_pid
+# Create enough IO so that we need around 8 seconds to relocate it.
+start_ts=$(_wallclock)
+$TIMEOUT_PROG 8s $XFS_IO_PROG -f -c "pwrite -D -b 1M 0 $max_space" \
+ "$SCRATCH_MNT/file" > /dev/null 2>&1
+stop_ts=$(_wallclock)
+
+runtime=$(( stop_ts - start_ts ))
+
+# Unmount and mount again the fs to clear any cached data and metadata, so that
+# it's less likely balance has already finished when we try to cancel it below.
+_scratch_cycle_mount
# Now balance should take at least $runtime seconds, we can cancel it at
-# $runtime/2 to ensure a success cancel.
+# $runtime/4 to ensure a success cancel.
_run_btrfs_balance_start -d --bg "$SCRATCH_MNT"
-sleep $(($runtime / 2))
-$BTRFS_UTIL_PROG balance cancel "$SCRATCH_MNT"
+sleep $(($runtime / 4))
+# It's possible that balance has already completed. It's unlikely but often
+# it may happen due to virtualization, caching and other factors, so ignore
+# any error about no balance currently running.
+$BTRFS_UTIL_PROG balance cancel "$SCRATCH_MNT" 2>&1 | grep -iq 'not in progress'
+if [ $? -eq 0 ]; then
+ _notrun "balance finished before we could cancel it"
+fi
# Now check if we can finish relocating metadata, which should finish very
# quickly.
# Modify as appropriate.
_supported_fs btrfs
+_require_scratch
+# No data checksums for NOCOW case, so can't detect corruption and repair data.
+_require_btrfs_no_nodatacow
# Overwriting data is forbidden on a zoned block device
_require_non_zoned_device $SCRATCH_DEV
# blobk group
_scratch_mount $(_btrfs_no_v1_cache_opt)
-pagesize=$(get_page_size)
+pagesize=$(_get_page_size)
blocksize=$(_get_block_size $SCRATCH_MNT)
# For subpage case, since we still do read in full page size, if have 8 corrupted
#create an 8 block file
$XFS_IO_PROG -d -f -c "pwrite -S 0xbb -b $filesize 0 $filesize" "$SCRATCH_MNT/foobar" > /dev/null
-logical_extent=$($FILEFRAG_PROG -v "$SCRATCH_MNT/foobar" | _filter_filefrag | cut -d '#' -f 1)
+logical_extent=$(_btrfs_get_first_logical $SCRATCH_MNT/foobar)
physical_extent=$(get_physical $logical_extent)
echo "logical = $logical_extent physical=$physical_extent" >> $seqres.full
#
# Test if the show_devname() returns sprout device instead of seed device.
#
-# Fixed in kernel patch:
-# btrfs: btrfs_show_devname don't traverse into the seed fsid
. ./common/preamble
_begin_fstest auto quick seed
# real QA test starts here
_supported_fs btrfs
+_fixed_by_kernel_commit 4faf55b03823 \
+ "btrfs: don't traverse into the seed devices in show_devname"
_require_scratch_dev_pool 2
_scratch_dev_pool_get 2
#
# FS QA Test 218
#
-# Regression test for the problem fixed by the patch
-#
-# btrfs: init device stats for seed devices
-#
# Make a seed device, add a sprout to it, and then make sure we can still read
# the device stats for both devices after we remount with the new sprout device.
#
# Modify as appropriate.
_supported_fs btrfs
+_fixed_by_kernel_commit 124604eb50f8 \
+ "btrfs: init device stats for seed devices"
_require_test
_require_scratch_dev_pool 2
#
# Test a variety of stale device usecases. We cache the device and generation
# to make sure we do not allow stale devices, which can end up with some wonky
-# behavior for loop back devices. This was changed with
-#
-# btrfs: allow single disk devices to mount with older generations
-#
-# But I've added a few other test cases so it's clear what we expect to happen
+# behavior for loop back devices.
+# And, added a few other test cases so it's clear what we expect to happen
# currently.
#
{
cd /
rm -f $tmp.*
- if [ ! -z "$loop_mnt" ]; then
- $UMOUNT_PROG $loop_mnt
- rm -rf $loop_mnt
- fi
- [ ! -z "$loop_mnt1" ] && rm -rf $loop_mnt1
- [ ! -z "$fs_img1" ] && rm -rf $fs_img1
- [ ! -z "$fs_img2" ] && rm -rf $fs_img2
- [ ! -z "$loop_dev" ] && _destroy_loop_device $loop_dev
+
+ # The variables are set before the test case can fail.
+ $UMOUNT_PROG ${loop_mnt1} &> /dev/null
+ $UMOUNT_PROG ${loop_mnt2} &> /dev/null
+ rm -rf $loop_mnt1
+ rm -rf $loop_mnt2
+
+ [ ! -z $loop_dev1 ] && _destroy_loop_device $loop_dev1
+ [ ! -z $loop_dev1 ] && _destroy_loop_device $loop_dev2
+
+ rm -f $fs_img1
+ rm -f $fs_img2
+
_btrfs_rescan_devices
}
# real QA test starts here
_supported_fs btrfs
+
+loop_mnt1=$TEST_DIR/$seq/mnt1
+loop_mnt2=$TEST_DIR/$seq/mnt2
+fs_img1=$TEST_DIR/$seq/img1
+fs_img2=$TEST_DIR/$seq/img2
+loop_dev1=""
+loop_dev2=""
+
_require_test
_require_loop
+_require_btrfs_fs_sysfs
_require_btrfs_forget_or_module_loadable
+_fixed_by_kernel_commit 5f58d783fd78 \
+ "btrfs: free device in btrfs_close_devices for a single device filesystem"
-loop_mnt=$TEST_DIR/$seq.mnt
-loop_mnt1=$TEST_DIR/$seq.mnt1
-fs_img1=$TEST_DIR/$seq.img1
-fs_img2=$TEST_DIR/$seq.img2
-
-mkdir $loop_mnt
-mkdir $loop_mnt1
+mkdir -p $loop_mnt1
+mkdir -p $loop_mnt2
$XFS_IO_PROG -f -c "truncate 256m" $fs_img1 >>$seqres.full 2>&1
_mkfs_dev $fs_img1 >>$seqres.full 2>&1
cp $fs_img1 $fs_img2
+loop_dev1=`_create_loop_device $fs_img1`
+loop_dev2=`_create_loop_device $fs_img2`
+
# Normal single device case, should pass just fine
-_mount -o loop $fs_img1 $loop_mnt > /dev/null 2>&1 || \
+_mount $loop_dev1 $loop_mnt1 > /dev/null 2>&1 || \
_fail "Couldn't do initial mount"
-$UMOUNT_PROG $loop_mnt
+$UMOUNT_PROG $loop_mnt1
_btrfs_forget_or_module_reload
# Now mount the new version again to get the higher generation cached, umount
# and try to mount the old version. Mount the new version again just for good
# measure.
-loop_dev=`_create_loop_device $fs_img1`
-
-_mount $loop_dev $loop_mnt > /dev/null 2>&1 || \
+_mount $loop_dev1 $loop_mnt1 > /dev/null 2>&1 || \
_fail "Failed to mount the second time"
-$UMOUNT_PROG $loop_mnt
+$UMOUNT_PROG $loop_mnt1
-_mount -o loop $fs_img2 $loop_mnt > /dev/null 2>&1 || \
+_mount $loop_dev2 $loop_mnt2 > /dev/null 2>&1 || \
_fail "We couldn't mount the old generation"
-$UMOUNT_PROG $loop_mnt
+$UMOUNT_PROG $loop_mnt2
-_mount $loop_dev $loop_mnt > /dev/null 2>&1 || \
+_mount $loop_dev1 $loop_mnt1 > /dev/null 2>&1 || \
_fail "Failed to mount the second time"
-$UMOUNT_PROG $loop_mnt
+$UMOUNT_PROG $loop_mnt1
-# Now we definitely can't mount them at the same time, because we're still tied
-# to the limitation of one fs_devices per fsid.
+# Now try mount them at the same time, if kernel does not support
+# temp-fsid feature then mount will fail.
_btrfs_forget_or_module_reload
-_mount $loop_dev $loop_mnt > /dev/null 2>&1 || \
+_mount $loop_dev1 $loop_mnt1 > /dev/null 2>&1 || \
_fail "Failed to mount the third time"
-_mount -o loop $fs_img2 $loop_mnt1 > /dev/null 2>&1 && \
- _fail "We were allowed to mount when we should have failed"
+if ! _has_btrfs_sysfs_feature_attr temp_fsid; then
+ _mount $loop_dev2 $loop_mnt2 > /dev/null 2>&1 && \
+ _fail "We were allowed to mount when we should have failed"
+fi
_btrfs_rescan_devices
# success, all done
# * device= argument is already being test by btrfs/125
# * space cache test already covered by test btrfs/131
. ./common/preamble
-_begin_fstest auto quick
+_begin_fstest auto quick remount
_register_cleanup "cleanup"
# nologreplay should be used only with readonly
test_should_fail "nologreplay"
- # norecovery should be used only with readonly.
- # This options is an alias to nologreplay
- test_should_fail "norecovery"
-
if [ "$enable_rescue_nologreplay" = true ]; then
#rescue=nologreplay should be used only with readonly
test_should_fail "rescue=nologreplay"
test_mount_opt "nologreplay,ro" "ro,rescue=nologreplay"
- test_mount_opt "norecovery,ro" "ro,rescue=nologreplay"
test_mount_opt "rescue=nologreplay,ro" "ro,rescue=nologreplay"
else
test_mount_opt "nologreplay,ro" "ro,nologreplay"
- test_mount_opt "norecovery,ro" "ro,nologreplay"
fi
test_mount_opt "rescan_uuid_tree" "rescan_uuid_tree"
test_roundtrip_mount "compress=zlib:20" "compress=zlib:9" "compress=zstd:16" "compress=zstd:15"
test_roundtrip_mount "compress-force=lzo" "compress-force=lzo" "compress-force=zlib:4" "compress-force=zlib:4"
- # on remount, if we only pass datacow after nodatacow was used it will remain with nodatasum
- test_roundtrip_mount "nodatacow" "nodatasum,nodatacow" "datacow,datasum" "$DEFAULT_OPTS"
- # nodatacow disabled compression
- test_roundtrip_mount "compress-force" "compress-force=zlib:3" "nodatacow" "nodatasum,nodatacow"
+ if ! _scratch_btrfs_is_zoned; then
+ # on remount, if we only pass datacow after nodatacow was used it will remain with nodatasum
+ test_roundtrip_mount "nodatacow" "nodatasum,nodatacow" "datacow,datasum" "$DEFAULT_OPTS"
+ # nodatacow disabled compression
+ test_roundtrip_mount "compress-force" "compress-force=zlib:3" "nodatacow" "nodatasum,nodatacow"
- # nodatacow disabled both datacow and datasum, and datasum enabled datacow and datasum
- test_roundtrip_mount "nodatacow" "nodatasum,nodatacow" "datasum" "$DEFAULT_OPTS"
- test_roundtrip_mount "nodatasum" "nodatasum" "datasum" "$DEFAULT_OPTS"
+ # nodatacow disabled both datacow and datasum, and datasum enabled datacow and datasum
+ test_roundtrip_mount "nodatacow" "nodatasum,nodatacow" "datasum" "$DEFAULT_OPTS"
+ test_roundtrip_mount "nodatasum" "nodatasum" "datasum" "$DEFAULT_OPTS"
+ fi
test_should_fail "discard=invalid"
if [ "$enable_discard_sync" = true ]; then
test_roundtrip_mount "discard" "discard" "discard=sync" "discard"
- test_roundtrip_mount "discard=async" "discard=async" "discard=sync" "discard"
- test_roundtrip_mount "discard=sync" "discard" "nodiscard" "$DEFAULT_OPTS"
+ if ! _scratch_btrfs_is_zoned; then
+ test_roundtrip_mount "discard=async" "discard=async" "discard=sync" "discard"
+ fi
+ test_roundtrip_mount "discard=sync" "discard" "nodiscard" "$DEFAULT_NODISCARD_OPTS"
else
test_roundtrip_mount "discard" "discard" "discard" "discard"
- test_roundtrip_mount "discard" "discard" "nodiscard" "$DEFAULT_OPTS"
+ test_roundtrip_mount "discard" "discard" "nodiscard" "$DEFAULT_NODISCARD_OPTS"
fi
test_roundtrip_mount "enospc_debug" "enospc_debug" "noenospc_debug" "$DEFAULT_OPTS"
DEFAULT_OPTS=$(cat /proc/self/mounts | grep $SCRATCH_MNT | \
$AWK_PROG '{ print $4 }')
+# Since 63a7cb130718 ("btrfs: auto enable discard=async when possible"),
+# "discard=async" will be automatically enabled if the device supports.
+# This can screw up our test against nodiscard options, thus remove the
+# default "discard=async" mount option for "nodiscard" tests.
+DEFAULT_NODISCARD_OPTS=$(echo -n "$DEFAULT_OPTS" | $SED_PROG 's/,discard=async//')
+
$BTRFS_UTIL_PROG subvolume create "$SCRATCH_MNT/vol1" > /dev/null
touch "$SCRATCH_MNT/vol1/file.txt"
_scratch_unmount
echo "=== qgroup assign shared test ===" >> $seqres.full
$BTRFS_UTIL_PROG quota enable $SCRATCH_MNT
- $BTRFS_UTIL_PROG quota rescan -w $SCRATCH_MNT >> $seqres.full
+ _qgroup_rescan $SCRATCH_MNT >> $seqres.full
$BTRFS_UTIL_PROG subvolume create $SCRATCH_MNT/a >> $seqres.full
$BTRFS_UTIL_PROG subvolume create $SCRATCH_MNT/b >> $seqres.full
echo "=== qgroup assign no shared test ===" >> $seqres.full
$BTRFS_UTIL_PROG quota enable $SCRATCH_MNT
- $BTRFS_UTIL_PROG quota rescan -w $SCRATCH_MNT >> $seqres.full
+ _qgroup_rescan $SCRATCH_MNT >> $seqres.full
$BTRFS_UTIL_PROG subvolume create $SCRATCH_MNT/a >> $seqres.full
$BTRFS_UTIL_PROG subvolume create $SCRATCH_MNT/b >> $seqres.full
_check_scratch_fs
}
-# Test snapshot with assigning qgroup for submodule
+# Test snapshot with assigning qgroup for higher level qgroup
snapshot_test()
{
_scratch_mkfs > /dev/null 2>&1
echo "=== qgroup snapshot test ===" >> $seqres.full
$BTRFS_UTIL_PROG quota enable $SCRATCH_MNT
- $BTRFS_UTIL_PROG quota rescan -w $SCRATCH_MNT >> $seqres.full
+ _qgroup_rescan $SCRATCH_MNT >> $seqres.full
$BTRFS_UTIL_PROG subvolume create $SCRATCH_MNT/a >> $seqres.full
+ $BTRFS_UTIL_PROG qgroup create 1/0 $SCRATCH_MNT >> $seqres.full
_ddt of="$SCRATCH_MNT"/a/file1 bs=1M count=1 >> $seqres.full 2>&1
- subvolid=$(_btrfs_get_subvolid $SCRATCH_MNT a)
- $BTRFS_UTIL_PROG subvolume snapshot -i 0/$subvolid $SCRATCH_MNT/a $SCRATCH_MNT/b >> $seqres.full
+ $BTRFS_UTIL_PROG subvolume snapshot -i 1/0 $SCRATCH_MNT/a $SCRATCH_MNT/b >> $seqres.full
_scratch_unmount
_check_scratch_fs
# FS QA Test 225
#
# Test for seed device-delete on a sprouted FS.
-# Requires kernel patch
-# b5ddcffa3777 btrfs: fix put of uninitialized kobject after seed device delete
#
# Steps:
# Create a seed FS. Add a RW device to make it sprout FS and then delete
# Modify as appropriate.
_supported_fs btrfs
+_fixed_by_kernel_commit b5ddcffa3777 \
+ "btrfs: fix put of uninitialized kobject after seed device delete"
_require_test
_require_scratch_dev_pool 2
_require_btrfs_forget_or_module_loadable
$BTRFS_UTIL_PROG subvolume create $SCRATCH_MNT/newvol >> $seqres.full 2>&1 \
|| _fail "couldn't create subvol"
+# Subvolume creation used to commit the transaction used to create it, but after
+# the patch "btrfs: don't commit transaction for every subvol create", that
+# changed, so sync the fs to commit any open transaction.
+$BTRFS_UTIL_PROG filesystem sync $SCRATCH_MNT
+
$BTRFS_UTIL_PROG inspect-internal dump-tree -t1 $SCRATCH_DEV \
| grep -q "256 ROOT_ITEM" || _fail "First subvol with id 256 doesn't exist"
sync
$BTRFS_UTIL_PROG quota enable $SCRATCH_MNT
-$BTRFS_UTIL_PROG quota rescan -w $SCRATCH_MNT >> $seqres.full
+_qgroup_rescan $SCRATCH_MNT >> $seqres.full
# Set the limit to just 512MiB, which is way below the existing usage
$BTRFS_UTIL_PROG qgroup limit 512M 0/5 $SCRATCH_MNT
while true; do
args=`_scale_fsstress_args -p 20 -n 1000 $FSSTRESS_AVOID -d $SCRATCH_MNT/stressdir`
- $FSSTRESS_PROG $args >/dev/null 2>&1
+ $FSSTRESS_PROG $args >> $seqres.full
done
}
sync
$BTRFS_UTIL_PROG quota enable $SCRATCH_MNT
-$BTRFS_UTIL_PROG quota rescan -w $SCRATCH_MNT >> $seqres.full
+_qgroup_rescan $SCRATCH_MNT >> $seqres.full
# set the limit to 1 g, leaving us just 100mb of slack space
$BTRFS_UTIL_PROG qgroup limit 1G 0/5 $SCRATCH_MNT
# performed.
#
. ./common/preamble
-_begin_fstest auto quick subvol
+_begin_fstest auto quick subvol remount
# Override the default cleanup function.
_cleanup()
# Import common functions.
. ./common/filter
+. ./common/filter.btrfs
. ./common/dmflakey
# real QA test starts here
# open, delete the subvolume, then 'sync' to durably persist the orphan
# item for the subvolume.
exec 73< $SCRATCH_MNT/testsv/foobar
- $BTRFS_UTIL_PROG subvolume delete $SCRATCH_MNT/testsv | _filter_scratch
+ $BTRFS_UTIL_PROG subvolume delete $SCRATCH_MNT/testsv | _filter_btrfs_subvol_delete
sync
# Now silently drop writes on the device, close the file descriptor and
QA output created by 233
Create subvolume 'SCRATCH_MNT/testsv'
-Delete subvolume (no-commit): 'SCRATCH_MNT/testsv'
+Delete subvolume 'SCRATCH_MNT/testsv'
Create subvolume 'SCRATCH_MNT/testsv'
-Delete subvolume (no-commit): 'SCRATCH_MNT/testsv'
+Delete subvolume 'SCRATCH_MNT/testsv'
_supported_fs btrfs
_require_scratch
_require_odirect
+_require_btrfs_no_nodatacow
_require_chattr c
_scratch_mkfs >>$seqres.full 2>&1
_begin_fstest auto quick zone balance
# Import common functions.
-. ./common/filter
+. ./common/zoned
# real QA test starts here
grep -Eo 'offset [[:digit:]]+'| cut -d ' ' -f 2
}
-_scratch_mkfs >/dev/null 2>&1
+sdev="$(_short_dev $SCRATCH_DEV)"
+zone_size=$(($(cat /sys/block/${sdev}/queue/chunk_sectors) << 9))
+fssize=$((zone_size * 16))
+devsize=$(($(_get_device_size $SCRATCH_DEV) * 1024))
+# Create a minimal FS to kick the reclaim process
+if [[ $devsize -gt $fssize ]]; then
+ _scratch_mkfs_sized $fssize >> $seqres.full 2>&1
+else
+ _scratch_mkfs >> $seqres.full 2>&1
+fi
_scratch_mount -o commit=1 # 1s commit time to speed up test
uuid=$($BTRFS_UTIL_PROG filesystem show $SCRATCH_DEV |grep uuid: |\
start_data_bg_phy=$(get_data_bg_physical)
start_data_bg_phy=$((start_data_bg_phy >> 9))
-
-size=$($BLKZONE_PROG report -o $start_data_bg_phy -l 1 $SCRATCH_DEV |\
- _filter_blkzone_report |\
- grep -Po "cap 0x[[:xdigit:]]+" | cut -d ' ' -f 2)
-size=$((size << 9))
+size=$(_zone_capacity $start_data_bg_phy)
reclaim_threshold=75
-echo $reclaim_threshold > /sys/fs/btrfs/"$uuid"/bg_reclaim_threshold
+if [[ -f /sys/fs/btrfs/"$uuid"/allocation/data/bg_reclaim_threshold ]]; then
+ fs_fill=1
+ echo $fs_fill > /sys/fs/btrfs/"$uuid"/bg_reclaim_threshold ||
+ _notrun "Need CONFIG_BTRFS_DEBUG to lower the reclaim threshold"
+ echo $reclaim_threshold > /sys/fs/btrfs/"$uuid"/allocation/data/bg_reclaim_threshold
+else
+ echo $reclaim_threshold > /sys/fs/btrfs/"$uuid"/bg_reclaim_threshold
+fi
+
fill_percent=$((reclaim_threshold + 2))
rest_percent=$((90 - fill_percent)) # make sure we're not creating a new BG
fill_size=$((size * fill_percent / 100))
#
# Check seed device integrity after fstrim on the sprout device.
#
-# Kernel bug is fixed by the commit:
-# btrfs: fix unmountable seed device after fstrim
-
. ./common/preamble
_begin_fstest auto quick seed trim
# Modify as appropriate.
_supported_fs btrfs
+_fixed_by_kernel_commit 5e753a817b2d \
+ "btrfs: fix unmountable seed device after fstrim"
_require_command "$BTRFS_TUNE_PROG" btrfstune
_require_fstrim
_require_scratch_dev_pool 2
#
mkdir $SCRATCH_MNT/testdir/dira
+# This fsync is for the zoned mode. On the zoned mode, we use a dedicated block
+# group for tree-log. That block group is created on-demand or assigned to a
+# metadata block group if there is none. On the first mount of a file system, we
+# need to create one because there is only one metadata block group available
+# for the regular metadata. That creation of a new block group forces tree-log
+# to be a full commit on that transaction, which prevents logging "testdir" and
+# "dira" and screws up the result.
+#
+# Calling fsync here will create the dedicated block group, and let them be
+# logged.
+$XFS_IO_PROG -c "fsync" $SCRATCH_MNT
+
# Make sure everything done so far is durably persisted.
sync
_supported_fs btrfs
_require_scratch
+# If it's subpage case, we don't support inline extents creation for now.
+_require_btrfs_inline_extents_creation
+
_scratch_mkfs > /dev/null
_scratch_mount -o compress,max_inline=2048
# Create a sprout filesystem (an rw device on top of a seed device)
# Dump 'btrfs filesystem usage', check it didn't fail
#
-# Tests btrfs-progs bug fixed by the kernel patch and a btrfs-prog patch
-# btrfs: sysfs add devinfo/fsid to retrieve fsid from the device
-# btrfs-progs: read fsid from the sysfs in device_is_seed
. ./common/preamble
_begin_fstest auto quick seed volume
_require_scratch_dev_pool 3
_require_command "$WIPEFS_PROG" wipefs
_require_btrfs_forget_or_module_loadable
+_wants_kernel_commit a26d60dedf9a \
+ "btrfs: sysfs: add devinfo/fsid to retrieve actual fsid from the device"
+_fixed_by_git_commit btrfs-progs 32c2e57c65b9 \
+ "btrfs-progs: read fsid from the sysfs in device_is_seed"
_scratch_dev_pool_get 2
# use the scratch devices as seed devices
_supported_fs btrfs
_require_scratch
-pagesize=$(get_page_size)
+pagesize=$(_get_page_size)
# Read the content from urandom to a known safe location
$XFS_IO_PROG -f -c "pwrite -i /dev/urandom 0 $pagesize" "$tmp.good" > /dev/null
# case, so we don't have a mismatch with the golden output in case we
# run with a non default $LOAD_FACTOR (default is 1). We only want the
# mismatch with the golden output in case there's a checksum failure.
- $FSSUM_PROG -r "$snap_csum" "$snap_copy" | egrep -v '^OK$'
+ $FSSUM_PROG -r "$snap_csum" "$snap_copy" | grep -E -v '^OK$'
done
echo "Silence is golden"
_supported_fs btrfs
_require_test
_require_scratch
+# The chunk size on zoned mode is fixed to the zone size
+_require_non_zoned_device "$SCRATCH_DEV"
# Delete log file if it exists.
rm -f "${seqres}.full"
# Get chunk sizes.
echo "Capture default chunk sizes."
-FIRST_DATA_CHUNK_SIZE_B=$(_get_fs_sysfs_attr ${SCRATCH_BDEV} allocation/data/chunk_size)
+
+# The sysfs interface has only exported chunk_size (10G by default),
+# but no stripe_size (1G by default) exported.
+#
+# Btrfs calculate the real chunk to be allocated using both limits,
+# Thus the default 10G chunk size can only be fulfilled by something
+# like 10 disk RAID0
+#
+# The proper solution should be exporting the stripe_size limit too,
+# but that may take several cycles, here we just use hard coded 1G
+# as the expected chunk size.
+FIRST_DATA_CHUNK_SIZE_B=$((1024 * 1024 * 1024))
+
+# For metadata, we are safe to use the exported value, as the default
+# metadata chunk size limit is already smaller than its stripe size.
FIRST_METADATA_CHUNK_SIZE_B=$(_get_fs_sysfs_attr ${SCRATCH_BDEV} allocation/metadata/chunk_size)
echo "Orig Data chunk size = ${FIRST_DATA_CHUNK_SIZE_B}" >> ${seqres}.full
# if [[ $(expr ${FIRST_DATA_CHUNK_SIZE_MB} + ${DATA_SIZE_START_MB}) -ne $(expr ${FIRST_DATA_SIZE_MB}) ]]; then
if [[ $(expr ${FIRST_DATA_CHUNK_SIZE_MB} + ${DATA_SIZE_START_MB}) -ne ${FIRST_DATA_SIZE_MB} ]]; then
- echo "tInitial data allocation not correct."
+ echo "Initial data allocation not correct."
fi
if [[ $(expr ${FIRST_METADATA_CHUNK_SIZE_MB} + ${METADATA_SIZE_START_MB}) -ne ${FIRST_METADATA_SIZE_MB} ]]; then
#
# Test if the kernel can free the stale device entries.
#
-# Tests bug fixed by the kernel patch:
-# btrfs: harden identification of the stale device
-#
. ./common/preamble
_begin_fstest auto quick volume
_require_btrfs_forget_or_module_loadable
_require_scratch_nocheck
_require_command "$WIPEFS_PROG" wipefs
+_check_minimal_fs_size $((1024 * 1024 * 1024))
+
+_fixed_by_kernel_commit 770c79fb6550 \
+ "btrfs: harden identification of a stale device"
_scratch_dev_pool_get 3
# Now defrag each file.
for sz in ${file_sizes[@]}; do
echo "Defragging file with $sz bytes..." >> $seqres.full
- $BTRFS_UTIL_PROG filesystem defragment "$SCRATCH_MNT/f_$sz"
+ # In new versions of btrfs-progs (6.0+), the defrag command outputs to
+ # stdout the path of the files it operates on. So ignore that.
+ $BTRFS_UTIL_PROG filesystem defragment "$SCRATCH_MNT/f_$sz" > /dev/null
done
# Verify the checksums after the defrag operations.
#
. ./common/preamble
-_begin_fstest auto quick defrag prealloc
+_begin_fstest auto quick defrag prealloc fiemap
# Override the default cleanup function.
# _cleanup()
_supported_fs btrfs
_require_scratch
+# We rely on specific extent layout, don't run on compress
+_require_btrfs_no_compress
+
# Needs 4K sectorsize
_require_btrfs_support_sectorsize 4096
_require_xfs_io_command "falloc"
+_require_xfs_io_command "fiemap"
_scratch_mkfs -s 4k >> $seqres.full 2>&1
# in the middle
#
. ./common/preamble
-_begin_fstest auto defrag quick
+_begin_fstest auto defrag quick fiemap remount
-. ./common/btrfs
. ./common/filter
# real QA test starts here
# Modify as appropriate.
_supported_fs generic
_require_scratch
+_require_xfs_io_command "fiemap"
# Needs 4K sectorsize, as larger sectorsize can change the file layout.
_require_btrfs_support_sectorsize 4096
# at their max capacity.
#
. ./common/preamble
-_begin_fstest auto quick defrag
+_begin_fstest auto quick defrag fiemap compress
# Import common functions.
. ./common/filter
-. ./common/btrfs
# real QA test starts here
# Modify as appropriate.
_supported_fs btrfs
_require_scratch
+_require_xfs_io_command "fiemap"
_scratch_mkfs >> $seqres.full
new_csum=$(_md5_checksum $SCRATCH_MNT/foobar)
-echo "=== File extent layout before defrag ===" >> $seqres.full
+echo "=== File extent layout after defrag ===" >> $seqres.full
$XFS_IO_PROG -c "fiemap -v" "$SCRATCH_MNT/foobar" >> $seqres.full
$XFS_IO_PROG -c "fiemap -v" "$SCRATCH_MNT/foobar" > $tmp.after
# algorithm of all regular extents.
#
. ./common/preamble
-_begin_fstest auto quick defrag compress prealloc
+_begin_fstest auto quick defrag compress prealloc fiemap
# Override the default cleanup function.
# _cleanup()
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2022 SUSE Linux Products GmbH. All Rights Reserved.
+#
+# FS QA Test 261
+#
+# Make sure btrfs raid profiles can handling one corrupted device
+# without affecting the consistency of the fs.
+#
+. ./common/preamble
+_begin_fstest auto volume raid scrub
+
+_supported_fs btrfs
+_require_scratch_dev_pool 4
+_btrfs_get_profile_configs replace-missing
+_require_fssum
+
+prepare_fs()
+{
+ local mkfs_opts=$1
+
+ # We don't want too large fs which can take too long to populate
+ # And the extra redirection of stderr is to avoid the RAID56 warning
+ # message to polluate the golden output
+ _scratch_pool_mkfs $mkfs_opts -b 1G >> $seqres.full 2>&1
+ if [ $? -ne 0 ]; then
+ _fail "mkfs $mkfs_opts failed"
+ fi
+
+ # Disable compression, as compressed read repair is known to have problems
+ _scratch_mount -o compress=no
+
+ # Fill some part of the fs first
+ $XFS_IO_PROG -f -c "pwrite -S 0xfe 0 400M" $SCRATCH_MNT/garbage > /dev/null 2>&1
+
+ # Then use fsstress to generate some extra contents.
+ # Disable setattr related operations, as it may set NODATACOW which will
+ # not allow us to use btrfs checksum to verify the content.
+ $FSSTRESS_PROG -f setattr=0 -d $SCRATCH_MNT -w -n 3000 >> $seqres.full
+ sync
+
+ # Save the fssum of this fs
+ $FSSUM_PROG -A -f -w $tmp.saved_fssum $SCRATCH_MNT
+ _scratch_unmount
+}
+
+workload()
+{
+ local mkfs_opts=$1
+ local num_devs=$2
+
+ _scratch_dev_pool_get 4
+ echo "=== Testing profile $mkfs_opts ===" >> $seqres.full
+ rm -f -- $tmp.saved_fssum
+ prepare_fs "$mkfs_opts"
+
+ # $SCRATCH_DEV is always the first device of dev pool.
+ # Corrupt the disk but keep the primary superblock.
+ $XFS_IO_PROG -c "pwrite 1M 1023M" $SCRATCH_DEV > /dev/null 2>&1
+
+ _scratch_mount
+
+ # All content should be fine
+ $FSSUM_PROG -r $tmp.saved_fssum $SCRATCH_MNT > /dev/null
+
+ # Scrub to fix the fs, this is known to report various correctable
+ # errors
+ $BTRFS_UTIL_PROG scrub start -B $SCRATCH_MNT >> $seqres.full 2>&1
+
+ # Make sure above scrub fixed the fs
+ # Redirect the stderr to seqres.full as well to avoid warnings if
+ # /var/lib filesystem is readonly, as scrub fails to write status.
+ $BTRFS_UTIL_PROG scrub start -Br $SCRATCH_MNT >> $seqres.full 2>&1
+ if [ $? -ne 0 ]; then
+ echo "scrub failed to fix the fs for profile $mkfs_opts"
+ fi
+ _scratch_unmount
+ _scratch_dev_pool_put
+}
+
+for t in "${_btrfs_profile_configs[@]}"; do
+ workload "$t"
+done
+
+echo "Silence is golden"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 261
+Silence is golden
# defragmentation.
#
. ./common/preamble
-_begin_fstest auto quick defrag
+_begin_fstest auto quick defrag fiemap remount
# Import common functions.
. ./common/filter
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2017 Liu Bo. All Rights Reserved.
+# Copyright (c) 2022 Christoph Hellwig.
+#
+# FS QA Test 265
+#
+# Test that btrfs raid repair on a raid1c3 profile can repair corruption on two
+# mirrors for the same logical offset.
+#
+. ./common/preamble
+_begin_fstest auto quick read_repair
+
+# Import common functions.
+. ./common/filter
+
+# real QA test starts here
+
+_supported_fs btrfs
+_require_scratch_dev_pool 3
+
+# No data checksums for NOCOW case, so can't detect corruption and repair data.
+_require_btrfs_no_nodatacow
+_require_odirect
+# Overwriting data is forbidden on a zoned block device
+_require_non_zoned_device "${SCRATCH_DEV}"
+
+_scratch_dev_pool_get 3
+# step 1, create a raid1 btrfs which contains one 128k file.
+echo "step 1......mkfs.btrfs"
+
+mkfs_opts="-d raid1c3 -b 1G"
+_scratch_pool_mkfs $mkfs_opts >>$seqres.full 2>&1
+
+_scratch_mount
+
+$XFS_IO_PROG -f -d -c "pwrite -S 0xaa -b 128K 0 128K" \
+ "$SCRATCH_MNT/foobar" | \
+ _filter_xfs_io_offset
+
+# step 2, corrupt the first 64k of one copy (on SCRATCH_DEV which is the first
+# one in $SCRATCH_DEV_POOL
+echo "step 2......corrupt file extent"
+
+# ensure btrfs-map-logical sees the tree updates
+sync
+
+logical=$(_btrfs_get_first_logical $SCRATCH_MNT/foobar)
+
+physical1=$(_btrfs_get_physical ${logical} 1)
+devpath1=$(_btrfs_get_device_path ${logical} 1)
+
+physical2=$(_btrfs_get_physical ${logical} 2)
+devpath2=$(_btrfs_get_device_path ${logical} 2)
+
+_scratch_unmount
+
+echo " corrupt stripe #1, devpath $devpath1 physical $physical1" \
+ >> $seqres.full
+$XFS_IO_PROG -d -c "pwrite -S 0xbf -b 64K $physical1 64K" $devpath1 \
+ > /dev/null
+
+echo " corrupt stripe #2, devpath $devpath2 physical $physical2" \
+ >> $seqres.full
+$XFS_IO_PROG -d -c "pwrite -S 0xbf -b 64K $physical2 64K" $devpath2 \
+ > /dev/null
+
+_scratch_mount
+
+# step 3, 128k dio read (this read can repair bad copy)
+echo "step 3......repair the bad copy"
+
+_btrfs_direct_read_on_mirror 0 3 "$SCRATCH_MNT/foobar" 0 128K
+_btrfs_direct_read_on_mirror 1 3 "$SCRATCH_MNT/foobar" 0 128K
+
+_scratch_unmount
+
+echo "step 4......check if the repair worked"
+$XFS_IO_PROG -d -c "pread -v -b 512 $physical1 512" $devpath1 |\
+ _filter_xfs_io_offset
+$XFS_IO_PROG -d -c "pread -v -b 512 $physical2 512" $devpath2 |\
+ _filter_xfs_io_offset
+
+_scratch_dev_pool_put
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 265
+step 1......mkfs.btrfs
+wrote 131072/131072 bytes
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+step 2......corrupt file extent
+step 3......repair the bad copy
+step 4......check if the repair worked
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+read 512/512 bytes
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+read 512/512 bytes
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2017 Liu Bo. All Rights Reserved.
+# Copyright (c) 2022 Christoph Hellwig.
+#
+# FS QA Test 266
+#
+# Test that btrfs raid repair on a raid1c3 profile can repair interleaving
+# errors on all mirrors.
+#
+
+. ./common/preamble
+_begin_fstest auto quick read_repair
+
+# Import common functions.
+. ./common/filter
+
+# real QA test starts here
+
+_supported_fs btrfs
+# No data checksums for NOCOW case, so can't detect corruption and repair data.
+_require_btrfs_no_nodatacow
+_require_scratch_dev_pool 3
+
+_require_odirect
+# Overwriting data is forbidden on a zoned block device
+_require_non_zoned_device "${SCRATCH_DEV}"
+
+_scratch_dev_pool_get 3
+# step 1, create a raid1 btrfs which contains one 128k file.
+echo "step 1......mkfs.btrfs"
+
+mkfs_opts="-d raid1c3 -b 1G"
+_scratch_pool_mkfs $mkfs_opts >>$seqres.full 2>&1
+
+_scratch_mount
+
+$XFS_IO_PROG -f -d -c "pwrite -S 0xaa -b 256K 0 256K" \
+ "$SCRATCH_MNT/foobar" | \
+ _filter_xfs_io_offset
+
+# step 2, corrupt 64k in each copy
+echo "step 2......corrupt file extent"
+
+# ensure btrfs-map-logical sees the tree updates
+sync
+
+logical=$(_btrfs_get_first_logical $SCRATCH_MNT/foobar)
+
+physical1=$(_btrfs_get_physical ${logical} 1)
+devpath1=$(_btrfs_get_device_path ${logical} 1)
+
+physical2=$(_btrfs_get_physical ${logical} 2)
+devpath2=$(_btrfs_get_device_path ${logical} 2)
+
+physical3=$(_btrfs_get_physical ${logical} 3)
+devpath3=$(_btrfs_get_device_path ${logical} 3)
+
+_scratch_unmount
+
+$XFS_IO_PROG -d -c "pwrite -S 0xbd -b 64K $physical3 64K" \
+ $devpath3 > /dev/null
+
+$XFS_IO_PROG -d -c "pwrite -S 0xba -b 64K $physical1 128K" \
+ $devpath1 > /dev/null
+
+$XFS_IO_PROG -d -c "pwrite -S 0xbb -b 64K $((physical2 + 65536)) 128K" \
+ $devpath2 > /dev/null
+
+$XFS_IO_PROG -d -c "pwrite -S 0xbc -b 64K $((physical3 + (2 * 65536))) 128K" \
+ $devpath3 > /dev/null
+
+_scratch_mount
+
+# step 3, 128k dio read (this read can repair bad copy)
+echo "step 3......repair the bad copy"
+
+_btrfs_buffered_read_on_mirror 0 3 "$SCRATCH_MNT/foobar" 0 256K
+_btrfs_buffered_read_on_mirror 1 3 "$SCRATCH_MNT/foobar" 0 256K
+_btrfs_buffered_read_on_mirror 2 3 "$SCRATCH_MNT/foobar" 0 256K
+
+_scratch_unmount
+
+echo "step 4......check if the repair worked"
+$XFS_IO_PROG -d -c "pread -v -b 512 $physical1 512" $devpath1 |\
+ _filter_xfs_io_offset
+$XFS_IO_PROG -d -c "pread -v -b 512 $physical2 512" $devpath2 |\
+ _filter_xfs_io_offset
+$XFS_IO_PROG -d -c "pread -v -b 512 $physical3 512" $devpath3 |\
+ _filter_xfs_io_offset
+
+_scratch_dev_pool_put
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 266
+step 1......mkfs.btrfs
+wrote 262144/262144 bytes
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+step 2......corrupt file extent
+step 3......repair the bad copy
+step 4......check if the repair worked
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+read 512/512 bytes
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+read 512/512 bytes
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+read 512/512 bytes
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2017 Liu Bo. All Rights Reserved.
+# Copyright (c) 2022 Christoph Hellwig.
+#
+# FS QA Test 267
+#
+# Test that btrfs buffered read repair on a raid1c3 profile can repair
+# interleaving errors on all mirrors.
+#
+
+. ./common/preamble
+_begin_fstest auto quick read_repair
+
+# Import common functions.
+. ./common/filter
+
+# real QA test starts here
+
+_supported_fs btrfs
+_require_scratch_dev_pool 3
+
+# No data checksums for NOCOW case, so can't detect corruption and repair data.
+_require_btrfs_no_nodatacow
+_require_odirect
+# Overwriting data is forbidden on a zoned block device
+_require_non_zoned_device "${SCRATCH_DEV}"
+
+_scratch_dev_pool_get 3
+# step 1, create a raid1 btrfs which contains one 128k file.
+echo "step 1......mkfs.btrfs"
+
+mkfs_opts="-d raid1c3 -b 1G"
+_scratch_pool_mkfs $mkfs_opts >>$seqres.full 2>&1
+
+_scratch_mount
+
+$XFS_IO_PROG -f -d -c "pwrite -S 0xaa -b 256K 0 256K" \
+ "$SCRATCH_MNT/foobar" | \
+ _filter_xfs_io_offset
+
+# step 2, corrupt 64k in each copy
+echo "step 2......corrupt file extent"
+
+# ensure btrfs-map-logical sees the tree updates
+sync
+
+logical=$(_btrfs_get_first_logical $SCRATCH_MNT/foobar)
+
+physical1=$(_btrfs_get_physical ${logical} 1)
+devpath1=$(_btrfs_get_device_path ${logical} 1)
+
+physical2=$(_btrfs_get_physical ${logical} 2)
+devpath2=$(_btrfs_get_device_path ${logical} 2)
+
+physical3=$(_btrfs_get_physical ${logical} 3)
+devpath3=$(_btrfs_get_device_path ${logical} 3)
+
+_scratch_unmount
+
+$XFS_IO_PROG -d -c "pwrite -S 0xbd -b 64K $physical3 64K" \
+ $devpath3 > /dev/null
+
+$XFS_IO_PROG -d -c "pwrite -S 0xba -b 64K $physical1 128K" \
+ $devpath1 > /dev/null
+
+$XFS_IO_PROG -d -c "pwrite -S 0xbb -b 64K $((physical2 + 65536)) 128K" \
+ $devpath2 > /dev/null
+
+$XFS_IO_PROG -d -c "pwrite -S 0xbc -b 64K $((physical3 + (2 * 65536))) 128K" \
+ $devpath3 > /dev/null
+
+_scratch_mount
+
+# step 3, 128k dio read (this read can repair bad copy)
+echo "step 3......repair the bad copy"
+
+_btrfs_direct_read_on_mirror 0 3 "$SCRATCH_MNT/foobar" 0 256K
+_btrfs_direct_read_on_mirror 1 3 "$SCRATCH_MNT/foobar" 0 256K
+_btrfs_direct_read_on_mirror 2 3 "$SCRATCH_MNT/foobar" 0 256K
+
+_scratch_unmount
+
+echo "step 4......check if the repair worked"
+$XFS_IO_PROG -d -c "pread -v -b 512 $physical1 512" $devpath1 |\
+ _filter_xfs_io_offset
+$XFS_IO_PROG -d -c "pread -v -b 512 $physical2 512" $devpath2 |\
+ _filter_xfs_io_offset
+$XFS_IO_PROG -d -c "pread -v -b 512 $physical3 512" $devpath3 |\
+ _filter_xfs_io_offset
+
+_scratch_dev_pool_put
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 267
+step 1......mkfs.btrfs
+wrote 262144/262144 bytes
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+step 2......corrupt file extent
+step 3......repair the bad copy
+step 4......check if the repair worked
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+read 512/512 bytes
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+read 512/512 bytes
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+read 512/512 bytes
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Christoph Hellwig.
+#
+# FS QA Test 268
+#
+# Test that btrfs read repair on a raid1 profile won't loop forever if data
+# is corrupted on both mirrors and can't be recovered.
+#
+
+. ./common/preamble
+_begin_fstest auto quick read_repair
+
+. ./common/filter
+
+_supported_fs btrfs
+_require_scratch
+_require_odirect
+# No data checksums for NOCOW case, so can't detect corruption and repair data.
+_require_btrfs_no_nodatacow
+_require_non_zoned_device "${SCRATCH_DEV}" # no overwrites on zoned devices
+_require_scratch_dev_pool 2
+_scratch_dev_pool_get 2
+
+echo "step 1......mkfs.btrfs"
+
+_scratch_pool_mkfs "-d raid1 -b 1G" >>$seqres.full 2>&1
+_scratch_mount
+
+$XFS_IO_PROG -f -d -c "pwrite -S 0xaa -b 256K 0 256K" \
+ "$SCRATCH_MNT/foobar" | \
+ _filter_xfs_io_offset
+
+# ensure btrfs-map-logical sees the tree updates
+sync
+
+logical=$(_btrfs_get_first_logical $SCRATCH_MNT/foobar)
+
+physical1=$(_btrfs_get_physical ${logical} 1)
+devpath1=$(_btrfs_get_device_path ${logical} 1)
+
+physical2=$(_btrfs_get_physical ${logical} 2)
+devpath2=$(_btrfs_get_device_path ${logical} 2)
+
+physical3=$(_btrfs_get_physical ${logical} 3)
+devpath3=$(_btrfs_get_device_path ${logical} 3)
+
+_scratch_unmount
+
+echo "step 2......corrupt file extent"
+$XFS_IO_PROG -d -c "pwrite -S 0xba -b 4K $physical1 4K" \
+ $devpath1 > /dev/null
+$XFS_IO_PROG -d -c "pwrite -S 0xba -b 4K $physical2 4K" \
+ $devpath2 > /dev/null
+
+_scratch_mount
+
+echo "step 3......try to repair"
+$XFS_IO_PROG -d -c "pread -b 4K 0 4K" $SCRATCH_MNT/foobar
+
+_scratch_unmount
+_scratch_dev_pool_put
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 268
+step 1......mkfs.btrfs
+wrote 262144/262144 bytes
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+step 2......corrupt file extent
+step 3......try to repair
+pread: Input/output error
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Christoph Hellwig.
+#
+# FS QA Test 269
+#
+# Test btrfs read repair over tricky stripe boundaries on the raid10 profile:
+#
+# | stripe 0 | stripe 2
+# --------------------------------
+# mirror 1 | I/O FAIL | GOOD
+# mirror 2 | GOOD | CSUM FAIL
+#
+
+. ./common/preamble
+_begin_fstest auto quick read_repair
+
+. ./common/filter
+
+_supported_fs btrfs
+_require_scratch
+_require_odirect
+# No data checksums for NOCOW case, so can't detect corruption and repair data.
+_require_btrfs_no_nodatacow
+_require_non_zoned_device "${SCRATCH_DEV}" # no overwrites on zoned devices
+_require_scratch_dev_pool 4
+_scratch_dev_pool_get 4
+
+echo "step 1......mkfs.btrfs"
+
+_scratch_pool_mkfs "-d raid10 -b 1G" >>$seqres.full 2>&1
+_scratch_mount
+
+$XFS_IO_PROG -f -d -c "pwrite -S 0xaa -b 128K 0 128K" \
+ "$SCRATCH_MNT/foobar" | \
+ _filter_xfs_io_offset
+
+# ensure btrfs-map-logical sees the tree updates
+sync
+
+logical=$(_btrfs_get_first_logical $SCRATCH_MNT/foobar)
+
+physical1=$(_btrfs_get_physical ${logical} 1)
+devpath1=$(_btrfs_get_device_path ${logical} 1)
+
+physical4=$(_btrfs_get_physical ${logical} 3)
+devpath4=$(_btrfs_get_device_path ${logical} 3)
+
+_scratch_unmount
+
+echo "step 2......corrupt file extent"
+
+$XFS_IO_PROG -d -c "pwrite -S 0xbd -b 64K $physical1 64K" \
+ $devpath1 > /dev/null
+$XFS_IO_PROG -d -c "pwrite -S 0xbb -b 64K $((physical4 + 65536)) 64K" \
+ $devpath4 > /dev/null
+
+_scratch_mount
+
+echo "step 3......repair the bad copy"
+
+_btrfs_direct_read_on_mirror 0 2 "$SCRATCH_MNT/foobar" 0 128K
+_btrfs_direct_read_on_mirror 1 2 "$SCRATCH_MNT/foobar" 0 128K
+
+_scratch_unmount
+
+echo "step 4......check if the repair worked"
+$XFS_IO_PROG -d -c "pread -v -b 512 $physical1 512" $devpath1 |\
+ _filter_xfs_io_offset
+$XFS_IO_PROG -d -c "pread -v -b 512 $((physical4 + 65536)) 512" $devpath4 |\
+ _filter_xfs_io_offset
+
+_scratch_dev_pool_put
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 269
+step 1......mkfs.btrfs
+wrote 131072/131072 bytes
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+step 2......corrupt file extent
+step 3......repair the bad copy
+step 4......check if the repair worked
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+read 512/512 bytes
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2017 Liu Bo. All Rights Reserved.
+#
+# FS QA Test 270
+#
+# Regression test for btrfs buffered read repair of compressed data.
+#
+. ./common/preamble
+_begin_fstest auto quick read_repair compress
+
+. ./common/filter
+
+_supported_fs btrfs
+_require_scratch
+_require_btrfs_command inspect-internal dump-tree
+_require_non_zoned_device "${SCRATCH_DEV}" # no overwrites on zoned devices
+_require_scratch_dev_pool 2
+_scratch_dev_pool_get 2
+
+get_physical()
+{
+ local logical=$1
+ local stripe=$2
+ $BTRFS_UTIL_PROG inspect-internal dump-tree -t 3 $SCRATCH_DEV | \
+ grep $logical -A 6 | \
+ $AWK_PROG "(\$1 ~ /stripe/ && \$3 ~ /devid/ && \$2 ~ /$stripe/) { print \$6 }"
+}
+
+get_devid()
+{
+ local logical=$1
+ local stripe=$2
+ $BTRFS_UTIL_PROG inspect-internal dump-tree -t 3 $SCRATCH_DEV | \
+ grep $logical -A 6 | \
+ $AWK_PROG "(\$1 ~ /stripe/ && \$3 ~ /devid/ && \$2 ~ /$stripe/) { print \$4 }"
+}
+
+get_device_path()
+{
+ local devid=$1
+ echo "$SCRATCH_DEV_POOL" | $AWK_PROG "{print \$$devid}"
+}
+
+
+echo "step 1......mkfs.btrfs"
+_check_minimal_fs_size $(( 1024 * 1024 * 1024 ))
+_scratch_pool_mkfs "-d raid1 -b 1G" >>$seqres.full 2>&1
+_scratch_mount -ocompress
+
+# Create a file with all data being compressed
+$XFS_IO_PROG -f -c "pwrite -S 0xaa -W -b 128K 0 128K" \
+ "$SCRATCH_MNT/foobar" | _filter_xfs_io_offset
+
+logical_in_btrfs=$(_btrfs_get_first_logical $SCRATCH_MNT/foobar)
+physical=$(get_physical ${logical_in_btrfs} 1)
+devid=$(get_devid ${logical_in_btrfs} 1)
+devpath=$(get_device_path ${devid})
+
+_scratch_unmount
+echo "step 2......corrupt file extent"
+echo " corrupt stripe #1, devid $devid devpath $devpath physical $physical" \
+ >> $seqres.full
+dd if=$devpath of=$TEST_DIR/$seq.dump.good skip=$physical bs=1 count=4096 \
+ 2>/dev/null
+$XFS_IO_PROG -c "pwrite -S 0xbb -b 4K $physical 4K" $devpath > /dev/null
+
+_scratch_mount
+
+echo "step 3......repair the bad copy"
+_btrfs_buffered_read_on_mirror 1 2 "$SCRATCH_MNT/foobar" 0 128K
+
+_scratch_unmount
+
+echo "step 4......check if the repair worked"
+dd if=$devpath of=$TEST_DIR/$seq.dump skip=$physical bs=1 count=4096 \
+ 2>/dev/null
+cmp -bl $TEST_DIR/$seq.dump.good $TEST_DIR/$seq.dump
+
+_scratch_dev_pool_put
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 270
+step 1......mkfs.btrfs
+wrote 131072/131072 bytes
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+step 2......corrupt file extent
+step 3......repair the bad copy
+step 4......check if the repair worked
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Christoph Hellwig.
+#
+# FS QA Test btrfs/271
+#
+# Test btrfs write error propagation and reporting on the raid1 profile.
+#
+. ./common/preamble
+_begin_fstest auto quick raid
+
+. ./common/filter
+. ./common/fail_make_request
+
+_supported_fs btrfs
+_require_scratch
+_require_fail_make_request
+_require_scratch_dev_pool 2
+_scratch_dev_pool_get 2
+
+_check_minimal_fs_size $(( 1024 * 1024 * 1024 ))
+_scratch_pool_mkfs "-d raid1 -b 1G" >> $seqres.full 2>&1
+
+_scratch_mount
+
+dev2=`echo $SCRATCH_DEV_POOL | $AWK_PROG '{print $2}'`
+
+_allow_fail_make_request
+
+echo "Step 1: writing with one failing mirror:"
+_bdev_fail_make_request $SCRATCH_DEV 1
+$XFS_IO_PROG -f -c "pwrite -W -S 0xaa 0 8K" $SCRATCH_MNT/foobar | _filter_xfs_io
+_bdev_fail_make_request $SCRATCH_DEV 0
+
+# btrfs counts errors per IO, assuming the data is merged that'll be 1 IO, then
+# the log tree block and then the log root tree block and then the super block.
+# We should see at least 4 failed IO's, but with subpage blocksize we could see
+# more if the log blocks end up on the same page, or if the data IO gets split
+# at all.
+errs=$($BTRFS_UTIL_PROG device stats $SCRATCH_DEV | \
+ $AWK_PROG '/write_io_errs/ { print $2 }')
+if [ $errs -lt 4 ]; then
+ _fail "Errors: $errs expected: 4"
+fi
+
+echo "Step 2: verify that the data reads back fine:"
+$XFS_IO_PROG -c "pread -v 0 8K" $SCRATCH_MNT/foobar | _filter_xfs_io_offset
+
+echo "Step 3: writing with two failing mirrors (should fail):"
+_bdev_fail_make_request $SCRATCH_DEV 1
+_bdev_fail_make_request $dev2 1
+$XFS_IO_PROG -f -c "pwrite -W -S 0xbb 0 8K" $SCRATCH_MNT/foobar | _filter_xfs_io
+_bdev_fail_make_request $dev2 0
+_bdev_fail_make_request $SCRATCH_DEV 0
+
+_disallow_fail_make_request
+
+_scratch_dev_pool_put
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 271
+Allow global fail_make_request feature
+Step 1: writing with one failing mirror:
+wrote 8192/8192 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Step 2: verify that the data reads back fine:
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
+read 8192/8192 bytes
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Step 3: writing with two failing mirrors (should fail):
+fsync: Input/output error
+Disallow global fail_make_request feature
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 BingJing Chang.
+#
+# FS QA Test No. btrfs/272
+#
+# Regression test for btrfs incremental send issue where a link instruction
+# is sent against an existing path, causing btrfs receive to fail.
+#
+# This issue is fixed by the following linux kernel btrfs patch:
+#
+# commit 3aa5bd367fa5a3 ("btrfs: send: fix sending link commands for
+# existing file paths")
+#
+. ./common/preamble
+_begin_fstest auto quick send
+
+# real QA test starts here
+_supported_fs btrfs
+_fixed_by_kernel_commit 3aa5bd367fa5a3 \
+ "btrfs: send: fix sending link commands for existing file paths"
+_require_test
+_require_scratch
+_require_fssum
+
+send_files_dir=$TEST_DIR/btrfs-test-$seq
+
+rm -fr $send_files_dir
+mkdir $send_files_dir
+
+_scratch_mkfs >>$seqres.full 2>&1
+_scratch_mount
+
+# Create a file and 2000 hard links to the same inode
+_run_btrfs_util_prog subvolume create $SCRATCH_MNT/vol
+touch $SCRATCH_MNT/vol/foo
+for i in {1..2000}; do
+ link $SCRATCH_MNT/vol/foo $SCRATCH_MNT/vol/$i
+done
+
+# Create a snapshot for a full send operation
+_run_btrfs_util_prog subvolume snapshot -r $SCRATCH_MNT/vol $SCRATCH_MNT/snap1
+_run_btrfs_util_prog send -f $send_files_dir/1.snap $SCRATCH_MNT/snap1
+
+# Remove 2000 hard links and re-create the last 1000 links
+for i in {1..2000}; do
+ rm $SCRATCH_MNT/vol/$i
+done
+for i in {1001..2000}; do
+ link $SCRATCH_MNT/vol/foo $SCRATCH_MNT/vol/$i
+done
+
+# Create another snapshot for an incremental send operation
+_run_btrfs_util_prog subvolume snapshot -r $SCRATCH_MNT/vol $SCRATCH_MNT/snap2
+_run_btrfs_util_prog send -p $SCRATCH_MNT/snap1 -f $send_files_dir/2.snap \
+ $SCRATCH_MNT/snap2
+
+$FSSUM_PROG -A -f -w $send_files_dir/1.fssum $SCRATCH_MNT/snap1
+$FSSUM_PROG -A -f -w $send_files_dir/2.fssum \
+ -x $SCRATCH_MNT/snap2/snap1 $SCRATCH_MNT/snap2
+
+# Recreate the filesystem by receiving both send streams and verify we get
+# the same content that the original filesystem had.
+_scratch_unmount
+_scratch_mkfs >>$seqres.full 2>&1
+_scratch_mount
+
+# Add the first snapshot to the new filesystem by applying the first send
+# stream.
+_run_btrfs_util_prog receive -f $send_files_dir/1.snap $SCRATCH_MNT
+
+# The incremental receive operation below used to fail with the following
+# error:
+#
+# ERROR: link 1238 -> foo failed: File exists
+#
+# This is because the path "1238" was stored as an extended ref item in the
+# original snapshot but as a normal ref item in the next snapshot. The send
+# operation cannot handle the duplicated paths, which are stored in
+# different ways, well, so it decides to issue a link operation for the
+# existing path. This results in the receiver to fail with the above error.
+_run_btrfs_util_prog receive -f $send_files_dir/2.snap $SCRATCH_MNT
+
+$FSSUM_PROG -r $send_files_dir/1.fssum $SCRATCH_MNT/snap1
+$FSSUM_PROG -r $send_files_dir/2.fssum $SCRATCH_MNT/snap2
+
+status=0
+exit
--- /dev/null
+QA output created by 272
+OK
+OK
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Western Digital Corporation. All Rights Reserved.
+#
+# FS QA Test No. 273
+#
+# Test that an active zone is properly reclaimed to allow the further
+# allocations, even if the active zones are mostly filled.
+#
+. ./common/preamble
+_begin_fstest auto quick snapshot zone
+
+# Override the default cleanup function.
+_cleanup()
+{
+ cd /
+
+ # kill commands in stress_data_bgs_2
+ [ -n "$pid1" ] && kill $pid1
+ [ -n "$pid2" ] && kill $pid2
+ wait
+}
+
+# Import common functions.
+. ./common/zoned
+
+# real QA test starts here
+
+_supported_fs btrfs
+_fixed_by_kernel_commit 2ce543f47843 \
+ "btrfs: zoned: wait until zone is finished when allocation didn't progress"
+# which is further fixed by
+_fixed_by_kernel_commit d5b81ced74af \
+ "btrfs: zoned: fix API misuse of zone finish waiting"
+_require_zoned_device "$SCRATCH_DEV"
+_require_limited_active_zones "$SCRATCH_DEV"
+
+_require_command "$BLKZONE_PROG" blkzone
+_require_btrfs_command inspect-internal dump-tree
+
+# This test requires specific data space usage, skip if we have compression
+# enabled.
+_require_no_compress
+
+max_active=$(cat $(_sysfs_dev ${SCRATCH_DEV})/queue/max_active_zones)
+
+# Fill the zones leaving the last 1MB
+fill_active_zones() {
+ # Asuumes we have the same capacity between zones.
+ local capacity=$(_zone_capacity 0)
+ local fill_size=$((capacity - 1024 * 1024))
+
+ for x in $(seq ${max_active}); do
+ dd if=/dev/zero of=${SCRATCH_MNT}/fill$(printf "%02d" $x) \
+ bs=${fill_size} count=1 oflag=direct 2>/dev/null
+ $BTRFS_UTIL_PROG filesystem sync ${SCRATCH_MNT}
+
+ local nactive=$($BLKZONE_PROG report ${SCRATCH_DEV} | grep oi | wc -l)
+ if [[ ${nactive} == ${max_active} ]]; then
+ break
+ fi
+ done
+
+ echo "max active zones: ${max_active}" >> $seqres.full
+ $BLKZONE_PROG report ${SCRATCH_DEV} | grep oi | cat -n >> $seqres.full
+}
+
+workout() {
+ local func="$1"
+
+ _scratch_mkfs >/dev/null 2>&1
+ _scratch_mount
+
+ fill_active_zones
+ eval "$func" || _fail "${func} failed"
+
+ _scratch_unmount
+ _check_btrfs_filesystem ${SCRATCH_DEV}
+}
+
+stress_data_bgs() {
+ # This dd fails with ENOSPC, which should not :(
+ dd if=/dev/zero of=${SCRATCH_MNT}/large bs=64M count=1 oflag=sync \
+ >>$seqres.full 2>&1
+}
+
+stress_data_bgs_2() {
+ # This dd fails with ENOSPC, which should not :(
+ dd if=/dev/zero of=${SCRATCH_MNT}/large bs=64M count=10 conv=fsync \
+ >>$seqres.full 2>&1 &
+ pid1=$!
+
+ dd if=/dev/zero of=${SCRATCH_MNT}/large2 bs=64M count=10 conv=fsync \
+ >>$seqres.full 2>&1 &
+ pid2=$!
+
+ wait $pid1; local ret1=$?; unset pid1
+ wait $pid2; local ret2=$?; unset pid2
+
+ if [ $ret1 -ne 0 -o $ret2 -ne 0 ]; then
+ return 1
+ fi
+ return 0
+}
+
+get_meta_bgs() {
+ $BTRFS_UTIL_PROG inspect-internal dump-tree -t EXTENT ${SCRATCH_DEV} |
+ grep BLOCK_GROUP -A 1 |grep -B1 'METADATA|' |
+ grep -oP '\(\d+ BLOCK_GROUP_ITEM \d+\)'
+}
+
+# This test case does not return the result because
+# _run_btrfs_util_prog will call _fail() in the error case anyway.
+stress_metadata_bgs() {
+ local metabgs=$(get_meta_bgs)
+ local count=0
+
+ while : ; do
+ _run_btrfs_util_prog subvolume snapshot ${SCRATCH_MNT} ${SCRATCH_MNT}/snap$i
+ _run_btrfs_util_prog filesystem sync ${SCRATCH_MNT}
+ cur_metabgs=$(get_meta_bgs)
+ if [[ "${cur_metabgs}" != "${metabgs}" ]]; then
+ break
+ fi
+ i=$((i + 1))
+ done
+}
+
+WORKS=(
+ stress_data_bgs
+ stress_data_bgs_2
+ stress_metadata_bgs
+)
+
+for work in "${WORKS[@]}"; do
+ workout "${work}"
+done
+
+echo "Silence is golden"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 273
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2022 SUSE Linux Products GmbH. All Rights Reserved.
+#
+# FS QA Test 274
+#
+# Test that we can not delete a subvolume that has an active swap file.
+#
+. ./common/preamble
+_begin_fstest auto quick swap subvol
+
+_cleanup()
+{
+ cd /
+ rm -f $tmp.*
+ test -n "$swap_file" && swapoff $swap_file &> /dev/null
+}
+
+. ./common/filter
+
+_supported_fs btrfs
+_fixed_by_kernel_commit 60021bd754c6ca \
+ "btrfs: prevent subvol with swapfile from being deleted"
+_require_scratch_swapfile
+
+_scratch_mkfs >> $seqres.full 2>&1
+_scratch_mount
+
+swap_file="$SCRATCH_MNT/subvol/swap"
+$BTRFS_UTIL_PROG subvolume create $SCRATCH_MNT/subvol | _filter_scratch
+
+echo "Creating and activating swap file..."
+_format_swapfile $swap_file $(($(_get_page_size) * 32)) >> $seqres.full
+_swapon_file $swap_file
+
+echo "Attempting to delete subvolume with swap file enabled..."
+# Output differs with different btrfs-progs versions and some display multiple
+# lines on failure like this for example:
+#
+# ERROR: Could not destroy subvolume/snapshot: Operation not permitted
+# WARNING: deletion failed with EPERM, send may be in progress
+# Delete subvolume (no-commit): '/home/fdmanana/btrfs-tests/scratch_1/subvol'
+#
+# So just redirect all output to the .full file and check the command's exit
+# status instead.
+$BTRFS_UTIL_PROG subvolume delete $SCRATCH_MNT/subvol >> $seqres.full 2>&1 && \
+ echo "subvolume deletion successful, expected failure!"
+
+echo "Disabling swap file..."
+swapoff $swap_file
+
+echo "Attempting to delete subvolume after disabling swap file..."
+$BTRFS_UTIL_PROG subvolume delete $SCRATCH_MNT/subvol >> $seqres.full 2>&1 || \
+ echo "subvolume deletion failure, expected success!"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 274
+Create subvolume 'SCRATCH_MNT/subvol'
+Creating and activating swap file...
+Attempting to delete subvolume with swap file enabled...
+Disabling swap file...
+Attempting to delete subvolume after disabling swap file...
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2022 SUSE Linux Products GmbH. All Rights Reserved.
+#
+# FS QA Test No. 275
+#
+# Test that no xattr can be changed once btrfs property is set to RO.
+#
+. ./common/preamble
+_begin_fstest auto quick attr
+
+# Import common functions.
+. ./common/filter
+. ./common/attr
+
+# real QA test starts here
+_supported_fs btrfs
+_fixed_by_kernel_commit b51111271b03 \
+ "btrfs: check if root is readonly while setting security xattr"
+_require_attrs
+_require_btrfs_command "property"
+_require_scratch
+
+_scratch_mkfs >> $seqres.full 2>&1
+_scratch_mount
+
+FILENAME=$SCRATCH_MNT/foo
+
+set_xattr()
+{
+ local value=$1
+ $SETFATTR_PROG -n "user.one" -v $value $FILENAME 2>&1 | _filter_scratch
+ $SETFATTR_PROG -n "security.one" -v $value $FILENAME 2>&1 | _filter_scratch
+ $SETFATTR_PROG -n "trusted.one" -v $value $FILENAME 2>&1 | _filter_scratch
+}
+
+get_xattr()
+{
+ _getfattr --absolute-names -n "user.one" $FILENAME 2>&1 | _filter_scratch
+ _getfattr --absolute-names -n "security.one" $FILENAME 2>&1 | _filter_scratch
+ _getfattr --absolute-names -n "trusted.one" $FILENAME 2>&1 | _filter_scratch
+}
+
+del_xattr()
+{
+ $SETFATTR_PROG -x "user.one" $FILENAME 2>&1 | _filter_scratch
+ $SETFATTR_PROG -x "security.one" $FILENAME 2>&1 | _filter_scratch
+ $SETFATTR_PROG -x "trusted.one" $FILENAME 2>&1 | _filter_scratch
+}
+
+# Create a test file.
+echo "hello world" > $FILENAME
+
+set_xattr 1
+
+$BTRFS_UTIL_PROG property set $SCRATCH_MNT ro true
+$BTRFS_UTIL_PROG property get $SCRATCH_MNT ro
+
+# Attempt to change values of RO (property) filesystem.
+set_xattr 2
+
+# Check the values of RO (property) filesystem are not changed.
+get_xattr
+
+# Attempt to remove xattr from RO (property) filesystem.
+del_xattr
+
+# Check if xattr still exist.
+get_xattr
+
+# Change filesystem property RO to false
+$BTRFS_UTIL_PROG property set $SCRATCH_MNT ro false
+$BTRFS_UTIL_PROG property get $SCRATCH_MNT ro
+
+# Change the xattrs after RO is false.
+set_xattr 2
+
+# Get the changed values.
+get_xattr
+
+# Remove xattr.
+del_xattr
+
+# check if the xattrs are really deleted.
+get_xattr
+
+status=0
+exit
--- /dev/null
+QA output created by 275
+ro=true
+setfattr: SCRATCH_MNT/foo: Read-only file system
+setfattr: SCRATCH_MNT/foo: Read-only file system
+setfattr: SCRATCH_MNT/foo: Read-only file system
+# file: SCRATCH_MNT/foo
+user.one="1"
+
+# file: SCRATCH_MNT/foo
+security.one="1"
+
+# file: SCRATCH_MNT/foo
+trusted.one="1"
+
+setfattr: SCRATCH_MNT/foo: Read-only file system
+setfattr: SCRATCH_MNT/foo: Read-only file system
+setfattr: SCRATCH_MNT/foo: Read-only file system
+# file: SCRATCH_MNT/foo
+user.one="1"
+
+# file: SCRATCH_MNT/foo
+security.one="1"
+
+# file: SCRATCH_MNT/foo
+trusted.one="1"
+
+ro=false
+# file: SCRATCH_MNT/foo
+user.one="2"
+
+# file: SCRATCH_MNT/foo
+security.one="2"
+
+# file: SCRATCH_MNT/foo
+trusted.one="2"
+
+SCRATCH_MNT/foo: user.one: No such attribute
+SCRATCH_MNT/foo: security.one: No such attribute
+SCRATCH_MNT/foo: trusted.one: No such attribute
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2022 SUSE Linux Products GmbH. All Rights Reserved.
+#
+# FS QA Test 276
+#
+# Verify that fiemap correctly reports the sharedness of extents for a file with
+# a very large number of extents, spanning many b+tree leaves in the fs tree,
+# and when the file's subvolume was snapshoted.
+#
+. ./common/preamble
+_begin_fstest auto snapshot fiemap remount
+
+. ./common/filter
+. ./common/filter.btrfs
+. ./common/attr
+
+_supported_fs btrfs
+_require_scratch
+_require_xfs_io_command "fiemap" "ranged"
+_require_attrs
+_require_odirect
+
+_scratch_mkfs >> $seqres.full 2>&1
+_scratch_mount
+
+fiemap_test_file()
+{
+ local offset=$1
+ local len=$2
+
+ # Skip the first two lines of xfs_io's fiemap output (file path and
+ # header describing the output columns) as well as holes.
+ $XFS_IO_PROG -c "fiemap -v $offset $len" $SCRATCH_MNT/foo | \
+ grep -v 'hole' | tail -n +3
+}
+
+# Count the number of shared extents for the whole test file or just for a given
+# range.
+count_shared_extents()
+{
+ local offset=$1
+ local len=$2
+
+ # Column 5 (from xfs_io's "fiemap -v" command) is the flags (hex field).
+ # 0x2000 is the value for the FIEMAP_EXTENT_SHARED flag.
+ fiemap_test_file $offset $len | \
+ $AWK_PROG --source 'BEGIN { cnt = 0 }' \
+ --source '{ if (and(strtonum($5), 0x2000)) cnt++ }' \
+ --source 'END { print cnt }'
+}
+
+# Count the number of non shared extents for the whole test file or just for a
+# given range.
+count_not_shared_extents()
+{
+ local offset=$1
+ local len=$2
+
+ # Column 5 (from xfs_io's "fiemap -v" command) is the flags (hex field).
+ # 0x2000 is the value for the FIEMAP_EXTENT_SHARED flag.
+ fiemap_test_file $offset $len | \
+ $AWK_PROG --source 'BEGIN { cnt = 0 }' \
+ --source '{ if (!and(strtonum($5), 0x2000)) cnt++ }' \
+ --source 'END { print cnt }'
+}
+
+# Create a file with 2000 extents, and a fs tree with a height of at least 3
+# (root node at level 2). We want to verify later that fiemap correctly reports
+# the sharedness of each extent, even when it needs to switch from one leaf to
+# the next one and from a node at level 1 to the next node at level 1.
+# To speedup creating a fs tree of height >= 3, add several large xattrs.
+ext_size=$(( 64 * 1024 ))
+file_size=$(( 2000 * 2 * $ext_size )) # about 250M
+nr_cpus=$("$here/src/feature" -o)
+workers=0
+for (( i = 0; i < $file_size; i += 2 * $ext_size )); do
+ $XFS_IO_PROG -f -d -c "pwrite -b $ext_size $i $ext_size" \
+ $SCRATCH_MNT/foo > /dev/null &
+ workers=$(( workers + 1 ))
+ if [ "$workers" -ge "$nr_cpus" ]; then
+ workers=0
+ wait
+ fi
+done
+wait
+
+workers=0
+xattr_value=$(printf '%0.sX' $(seq 1 3900))
+for (( i = 1; i <= 29000; i++ )); do
+ echo -n > $SCRATCH_MNT/filler_$i
+ $SETFATTR_PROG -n 'user.x1' -v $xattr_value $SCRATCH_MNT/filler_$i &
+ workers=$(( workers + 1 ))
+ if [ "$workers" -ge "$nr_cpus" ]; then
+ workers=0
+ wait
+ fi
+done
+wait
+
+# Make sure every ordered extent completed and therefore updated the fs tree.
+sync
+
+# All extents should be reported as non shared (2000 extents).
+echo "Number of non-shared extents in the whole file: $(count_not_shared_extents)"
+
+# Creating a snapshot.
+$BTRFS_UTIL_PROG subvolume snapshot $SCRATCH_MNT $SCRATCH_MNT/snap | _filter_scratch
+
+# We have a snapshot, so now all extents should be reported as shared.
+echo "Number of shared extents in the whole file: $(count_shared_extents)"
+
+# Now COW two file ranges, of 64K each, in the snapshot's file.
+# So 2 extents should become non-shared after this. Each file extent item is in
+# different leaf of the snapshot tree.
+#
+$XFS_IO_PROG -d -c "pwrite -b $ext_size 512K $ext_size" \
+ -d -c "pwrite -b $ext_size 249M $ext_size" \
+ $SCRATCH_MNT/snap/foo | _filter_xfs_io
+
+# Wait for ordered extents to complete and commit current transaction to make
+# sure fiemap will see all extents in the subvolume and extent trees.
+sync
+
+# Now we should have 2 non-shared extents and 1998 shared extents.
+echo "Number of non-shared extents in the whole file: $(count_not_shared_extents)"
+echo "Number of shared extents in the whole file: $(count_shared_extents)"
+
+# Check that the non-shared extents are indeed in the expected file ranges.
+echo "Number of non-shared extents in range [512K, 512K + 64K): $(count_not_shared_extents 512K 64K)"
+echo "Number of non-shared extents in range [249M, 249M + 64K): $(count_not_shared_extents 249M 64K)"
+
+# Now delete the snapshot.
+$BTRFS_UTIL_PROG subvolume delete -c $SCRATCH_MNT/snap | _filter_btrfs_subvol_delete
+
+# We deleted the snapshot and committed the transaction used to delete it (-c),
+# but all its extents (both metadata and data) are actually only deleted in the
+# background, by the cleaner kthread. So remount, which wakes up the cleaner
+# kthread, with a commit interval of 1 second and sleep for 1.1 seconds - after
+# this we are guaranteed all extents of the snapshot were deleted.
+_scratch_remount commit=1
+sleep 1.1
+
+# Now all extents should be reported as not shared (2000 extents).
+echo "Number of non-shared extents in the whole file: $(count_not_shared_extents)"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 276
+Number of non-shared extents in the whole file: 2000
+Create a snapshot of 'SCRATCH_MNT' in 'SCRATCH_MNT/snap'
+Number of shared extents in the whole file: 2000
+wrote 65536/65536 bytes at offset 524288
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 65536/65536 bytes at offset 261095424
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Number of non-shared extents in the whole file: 2
+Number of shared extents in the whole file: 1998
+Number of non-shared extents in range [512K, 512K + 64K): 1
+Number of non-shared extents in range [249M, 249M + 64K): 1
+Delete subvolume 'SCRATCH_MNT/snap'
+Number of non-shared extents in the whole file: 2000
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Meta, Inc. All Rights Reserved.
+#
+# FS QA Test 277
+#
+# Test sendstreams involving fs-verity enabled files.
+#
+. ./common/preamble
+_begin_fstest auto quick verity send
+
+# Override the default cleanup function.
+_cleanup()
+{
+ cd /
+ _restore_fsverity_signatures
+ rm -r -f $tmp.*
+}
+
+# Import common functions.
+. ./common/filter
+. ./common/verity
+
+# real QA test starts here
+
+# Modify as appropriate.
+_supported_fs btrfs
+_require_scratch_verity
+_require_fsverity_builtin_signatures
+_require_command "$SETCAP_PROG" setcap
+_require_command "$GETCAP_PROG" getcap
+_require_btrfs_send_version 3
+
+subv=$SCRATCH_MNT/subv
+fsv_file=$subv/file.fsv
+keyfile=$tmp.key.pem
+certfile=$tmp.cert.pem
+certfileder=$tmp.cert.der
+sigfile=$tmp.sig
+stream=$tmp.fsv.ss
+
+_test_send_verity() {
+ local sig=$1
+ local salt=$2
+ local extra_args=""
+
+ _scratch_mkfs >> $seqres.full
+ _scratch_mount
+ echo -e "\nverity send/recv test: sig: $sig salt: $salt"
+ _disable_fsverity_signatures
+
+ echo "create subvolume"
+ $BTRFS_UTIL_PROG subvolume create $subv >> $seqres.full
+ echo "create file"
+ $XFS_IO_PROG -fc "pwrite -q -S 0x58 0 12288" $fsv_file
+ if $salt; then
+ extra_args+=" --salt=deadbeef"
+ fi
+ if $sig; then
+ echo "generate keys and cert"
+ _fsv_generate_cert $keyfile $certfile $certfileder
+ echo "clear keyring"
+ _fsv_clear_keyring
+ echo "load cert into keyring"
+ _fsv_load_cert $certfileder
+ echo "require signatures"
+ _enable_fsverity_signatures
+ echo "sign file digest"
+ _fsv_sign $fsv_file $sigfile --key=$keyfile --cert=$certfile \
+ $extra_args | _filter_scratch >> $seqres.full
+ extra_args+=" --signature=$sigfile"
+ fi
+ echo "enable verity"
+ _fsv_enable $fsv_file $extra_args
+ cat $fsv_file > $tmp.file-before
+ _fsv_measure $fsv_file > $tmp.digest-before
+
+ # ensure send plays nice with other properties that are set when
+ # finishing the file during send, like chmod and capabilities.
+ echo "modify other properties"
+ chmod a+x $fsv_file
+ $SETCAP_PROG "cap_sys_ptrace+ep cap_sys_nice+ep" $fsv_file
+ $GETCAP_PROG $fsv_file > $tmp.cap-before
+
+ echo "set subvolume read only"
+ $BTRFS_UTIL_PROG property set $subv ro true
+ echo "send subvolume"
+ $BTRFS_UTIL_PROG send $subv -f $stream -q --proto=3 >> $seqres.full
+
+ echo "blow away fs"
+ _scratch_unmount
+ _scratch_mkfs >> $seqres.full
+ _scratch_mount
+
+ echo "receive sendstream"
+ $BTRFS_UTIL_PROG receive $SCRATCH_MNT -f $stream -q >> $seqres.full
+
+ echo "check received subvolume..."
+ _scratch_cycle_mount
+ _fsv_measure $fsv_file > $tmp.digest-after
+ $GETCAP_PROG $fsv_file > $tmp.cap-after
+ diff $tmp.file-before $fsv_file
+ diff $tmp.digest-before $tmp.digest-after
+ diff $tmp.cap-before $tmp.cap-after
+ _scratch_unmount
+ echo OK
+}
+
+_test_send_verity false false # no sig; no salt
+_test_send_verity false true # no sig; salt
+_test_send_verity true false # sig; no salt
+_test_send_verity true true # sig; salt
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 277
+
+verity send/recv test: sig: false salt: false
+create subvolume
+create file
+enable verity
+modify other properties
+set subvolume read only
+send subvolume
+blow away fs
+receive sendstream
+check received subvolume...
+OK
+
+verity send/recv test: sig: false salt: true
+create subvolume
+create file
+enable verity
+modify other properties
+set subvolume read only
+send subvolume
+blow away fs
+receive sendstream
+check received subvolume...
+OK
+
+verity send/recv test: sig: true salt: false
+create subvolume
+create file
+generate keys and cert
+clear keyring
+load cert into keyring
+require signatures
+sign file digest
+enable verity
+modify other properties
+set subvolume read only
+send subvolume
+blow away fs
+receive sendstream
+check received subvolume...
+OK
+
+verity send/recv test: sig: true salt: true
+create subvolume
+create file
+generate keys and cert
+clear keyring
+load cert into keyring
+require signatures
+sign file digest
+enable verity
+modify other properties
+set subvolume read only
+send subvolume
+blow away fs
+receive sendstream
+check received subvolume...
+OK
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 BingJing Chang.
+#
+# FS QA Test No. btrfs/278
+#
+# Regression test for btrfs incremental send issue when processing inodes
+# with no links
+#
+# This issue is fixed by the following linux kernel btrfs patch:
+#
+# commit 9ed0a72e5b355d ("btrfs: send: fix failures when processing
+# inodes with no links")
+#
+. ./common/preamble
+_begin_fstest auto quick send
+
+# real QA test starts here
+_supported_fs btrfs
+_fixed_by_kernel_commit 9ed0a72e5b355d \
+ "btrfs: send: fix failures when processing inodes with no links"
+_require_test
+_require_scratch
+_require_btrfs_command "property"
+_require_fssum
+
+send_files_dir=$TEST_DIR/btrfs-test-$seq
+
+rm -fr $send_files_dir
+mkdir $send_files_dir
+
+_scratch_mkfs >>$seqres.full 2>&1
+_scratch_mount
+_run_btrfs_util_prog subvolume create $SCRATCH_MNT/vol
+
+# Creating the first snapshot looks like:
+#
+# . (ino 256)
+# |--- deleted.file (ino 257)
+# |--- deleted.dir/ (ino 258)
+# |--- changed_subcase1.file (ino 259)
+# |--- changed_subcase2.file (ino 260)
+# |--- changed_subcase1.dir/ (ino 261)
+# | |---- foo (ino 262)
+# |--- changed_subcase2.dir/ (ino 263)
+# | |---- foo (ino 264)
+#
+touch $SCRATCH_MNT/vol/deleted.file
+mkdir $SCRATCH_MNT/vol/deleted.dir
+touch $SCRATCH_MNT/vol/changed_subcase1.file
+touch $SCRATCH_MNT/vol/changed_subcase2.file
+mkdir $SCRATCH_MNT/vol/changed_subcase1.dir
+touch $SCRATCH_MNT/vol/changed_subcase1.dir/foo
+mkdir $SCRATCH_MNT/vol/changed_subcase2.dir
+touch $SCRATCH_MNT/vol/changed_subcase2.dir/foo
+_run_btrfs_util_prog subvolume snapshot -r $SCRATCH_MNT/vol $SCRATCH_MNT/snap1
+
+# Delete the deleted.*, create a new file and a new directory, and then
+# take the second snapshot looks like:
+#
+# . (ino 256)
+# |--- changed_subcase1.file (ino 259)
+# |--- changed_subcase2.file (ino 260)
+# |--- changed_subcase1.dir/ (ino 261)
+# | |---- foo (ino 262)
+# |--- changed_subcase2.dir/ (ino 263)
+# | |---- foo (ino 264)
+# |--- new.file (ino 265)
+# |--- new.dir/ (ino 266)
+#
+unlink $SCRATCH_MNT/vol/deleted.file
+rmdir $SCRATCH_MNT/vol/deleted.dir
+touch $SCRATCH_MNT/vol/new.file
+mkdir $SCRATCH_MNT/vol/new.dir
+_run_btrfs_util_prog subvolume snapshot -r $SCRATCH_MNT/vol $SCRATCH_MNT/snap2
+
+# Set the snapshot "snap1" to read-write mode and turn several inodes to
+# orphans, so that the snapshot will look like this:
+#
+# . (ino 256)
+# |--- (orphan) deleted.file (ino 257)
+# |--- (orphan) deleted.dir/ (ino 258)
+# |--- (orphan) changed_subcase1.file (ino 259)
+# |--- changed_subcase2.file (ino 260)
+# |--- (orphan) changed_subcase1.dir/ (ino 261)
+# |--- changed_subcase2.dir/ (ino 263)
+# | |---- foo (ino 264)
+#
+# Note: To make an easy illustration, I just put a tag "(orphan)" in front of
+# their original names to indicate that they're deleted, but their inodes can
+# not be removed because of open file descriptors on them. Mention that orphan
+# inodes don't have names(paths).
+#
+$BTRFS_UTIL_PROG property set $SCRATCH_MNT/snap1 ro false
+exec 71<$SCRATCH_MNT/snap1/deleted.file
+exec 72<$SCRATCH_MNT/snap1/deleted.dir
+exec 73<$SCRATCH_MNT/snap1/changed_subcase1.file
+exec 74<$SCRATCH_MNT/snap1/changed_subcase1.dir
+unlink $SCRATCH_MNT/snap1/deleted.file
+rmdir $SCRATCH_MNT/snap1/deleted.dir
+unlink $SCRATCH_MNT/snap1/changed_subcase1.file
+unlink $SCRATCH_MNT/snap1/changed_subcase1.dir/foo
+rmdir $SCRATCH_MNT/snap1/changed_subcase1.dir
+
+# Turn the snapshot "snap1" back to read-only mode.
+$BTRFS_UTIL_PROG property set $SCRATCH_MNT/snap1 ro true
+
+# Set the snapshot "snap2" to read-write mode and turn several inodes to
+# orphans, so that the snapshot will look like this:
+#
+# . (ino 256)
+# |--- (orphan) changed_subcase1.file (ino 259)
+# |--- (orphan) changed_subcase2.file (ino 260)
+# |--- (orphan) changed_subcase1.dir/ (ino 261)
+# |--- (orphan) changed_subcase2.dir/ (ino 263)
+# |--- (orphan) new.file (ino 265)
+# |--- (orphan) new.dir/ (ino 266)
+#
+# Note: Same notice as above. Mention that orphan inodes don't have
+# names(paths).
+#
+$BTRFS_UTIL_PROG property set $SCRATCH_MNT/snap2 ro false
+exec 81<$SCRATCH_MNT/snap2/changed_subcase1.file
+exec 82<$SCRATCH_MNT/snap2/changed_subcase1.dir
+exec 83<$SCRATCH_MNT/snap2/changed_subcase2.file
+exec 84<$SCRATCH_MNT/snap2/changed_subcase2.dir
+exec 85<$SCRATCH_MNT/snap2/new.file
+exec 86<$SCRATCH_MNT/snap2/new.dir
+unlink $SCRATCH_MNT/snap2/changed_subcase1.file
+unlink $SCRATCH_MNT/snap2/changed_subcase1.dir/foo
+rmdir $SCRATCH_MNT/snap2/changed_subcase1.dir
+unlink $SCRATCH_MNT/snap2/changed_subcase2.file
+unlink $SCRATCH_MNT/snap2/changed_subcase2.dir/foo
+rmdir $SCRATCH_MNT/snap2/changed_subcase2.dir
+unlink $SCRATCH_MNT/snap2/new.file
+rmdir $SCRATCH_MNT/snap2/new.dir
+
+# Turn the snapshot "snap2" back to read-only mode.
+$BTRFS_UTIL_PROG property set $SCRATCH_MNT/snap2 ro true
+
+# Test that a full send operation can handle orphans with no paths
+_run_btrfs_util_prog send -f $send_files_dir/1.snap $SCRATCH_MNT/snap1
+
+# Test that an incremental send operation can handle orphans.
+#
+# Here're descriptions for the details:
+#
+# Case 1: new.file and new.dir (BTRFS_COMPARE_TREE_NEW)
+# | send snapshot | action
+# --------------------------------
+# nlink | 0 | ignore
+#
+# They are new inodes in the send snapshot ("snap2"), but they don't have
+# paths because they have no links. Test that the send operation can ignore
+# them in order not to generate the creation commands for them. Or it will
+# fail, with -ENOENT, when trying to generate paths for them.
+#
+#
+# Case 2: deleted.file and deleted.dir (BTRFS_COMPARE_TREE_DELETED)
+# | parent snapshot | action
+# ----------------------------------
+# nlink | 0 | as usual
+#
+# They're deleted in the parent snapshot ("snap1") but become orphans which
+# have no paths. Test that no deletion commands will be generated as usual.
+# This case didn't fail before.
+#
+#
+# Case 3: changed_*.file and changed_*.dir (BTRFS_COMPARE_TREE_CHANGED)
+# | | parent snapshot | send snapshot | action
+# -----------------------------------------------------------------------
+# subcase 1 | nlink | 0 | 0 | ignore
+# subcase 2 | nlink | >0 | 0 | new_gen(deletion)
+#
+# In subcase 1, test that the send operation can ignore them without trying
+# to generate any commands.
+#
+# In subcase 2, test that the send operation can generate an unlink command
+# for that file and test that it can generate a rename command for the
+# non-empty directory first and a rmdir command to remove it finally. Or
+# the receive operation will fail with a wrong unlink on a non-empty
+# directory.
+#
+_run_btrfs_util_prog send -p $SCRATCH_MNT/snap1 -f $send_files_dir/2.snap \
+ $SCRATCH_MNT/snap2
+
+$FSSUM_PROG -A -f -w $send_files_dir/1.fssum $SCRATCH_MNT/snap1
+$FSSUM_PROG -A -f -w $send_files_dir/2.fssum \
+ -x $SCRATCH_MNT/snap2/snap1 $SCRATCH_MNT/snap2
+
+# Recreate the filesystem by receiving both send streams and verify we get
+# the same content that the original filesystem had.
+exec 71>&-
+exec 72>&-
+exec 73>&-
+exec 74>&-
+exec 81>&-
+exec 82>&-
+exec 83>&-
+exec 84>&-
+exec 85>&-
+exec 86>&-
+_scratch_unmount
+_scratch_mkfs >>$seqres.full 2>&1
+_scratch_mount
+
+# Add the first snapshot to the new filesystem by applying the first send
+# stream.
+_run_btrfs_util_prog receive -f $send_files_dir/1.snap $SCRATCH_MNT
+
+# Test the incremental send stream
+_run_btrfs_util_prog receive -f $send_files_dir/2.snap $SCRATCH_MNT
+
+$FSSUM_PROG -r $send_files_dir/1.fssum $SCRATCH_MNT/snap1
+$FSSUM_PROG -r $send_files_dir/2.fssum $SCRATCH_MNT/snap2
+
+status=0
+exit
--- /dev/null
+QA output created by 278
+OK
+OK
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2022 SUSE Linux Products GmbH. All Rights Reserved.
+#
+# FS QA Test 279
+#
+# Test that if we have two files with shared extents, after removing one of the
+# files, if we do a fiemap against the other file, it does not report extents as
+# shared anymore.
+#
+# This exercises the processing of delayed references for data extents in the
+# backref walking code, used by fiemap to determine if an extent is shared.
+#
+. ./common/preamble
+_begin_fstest auto quick subvol fiemap clone
+
+. ./common/filter
+. ./common/reflink
+. ./common/punch # for _filter_fiemap_flags
+
+_supported_fs btrfs
+_require_scratch_reflink
+_require_cp_reflink
+_require_xfs_io_command "fiemap"
+
+_fixed_by_kernel_commit 4fc7b5722824 \
+ "btrfs: fix processing of delayed data refs during backref walking"
+
+run_test()
+{
+ local file_path_1=$1
+ local file_path_2=$2
+ local do_sync=$3
+
+ $XFS_IO_PROG -f -c "pwrite 0 64K" $file_path_1 | _filter_xfs_io
+ _cp_reflink $file_path_1 $file_path_2
+
+ if [ $do_sync -eq 1 ]; then
+ sync
+ fi
+
+ echo "Fiemap of $file_path_1 before deleting $file_path_2:" | \
+ _filter_scratch
+ $XFS_IO_PROG -c "fiemap -v" $file_path_1 | _filter_fiemap_flags
+
+ rm -f $file_path_2
+
+ echo "Fiemap of $file_path_1 after deleting $file_path_2:" | \
+ _filter_scratch
+ $XFS_IO_PROG -c "fiemap -v" $file_path_1 | _filter_fiemap_flags
+}
+
+_scratch_mkfs >> $seqres.full 2>&1
+_scratch_mount
+
+# Create two test subvolumes, we'll reflink files between them.
+$BTRFS_UTIL_PROG subvolume create $SCRATCH_MNT/subv1 | _filter_scratch
+$BTRFS_UTIL_PROG subvolume create $SCRATCH_MNT/subv2 | _filter_scratch
+
+echo
+echo "Testing with same subvolume and without transaction commit"
+echo
+run_test "$SCRATCH_MNT/subv1/f1" "$SCRATCH_MNT/subv1/f2" 0
+
+echo
+echo "Testing with same subvolume and with transaction commit"
+echo
+run_test "$SCRATCH_MNT/subv1/f3" "$SCRATCH_MNT/subv1/f4" 1
+
+echo
+echo "Testing with different subvolumes and without transaction commit"
+echo
+run_test "$SCRATCH_MNT/subv1/f5" "$SCRATCH_MNT/subv2/f6" 0
+
+echo
+echo "Testing with different subvolumes and with transaction commit"
+echo
+run_test "$SCRATCH_MNT/subv1/f7" "$SCRATCH_MNT/subv2/f8" 1
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 279
+Create subvolume 'SCRATCH_MNT/subv1'
+Create subvolume 'SCRATCH_MNT/subv2'
+
+Testing with same subvolume and without transaction commit
+
+wrote 65536/65536 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Fiemap of SCRATCH_MNT/subv1/f1 before deleting SCRATCH_MNT/subv1/f2:
+0: [0..127]: shared|last
+Fiemap of SCRATCH_MNT/subv1/f1 after deleting SCRATCH_MNT/subv1/f2:
+0: [0..127]: last
+
+Testing with same subvolume and with transaction commit
+
+wrote 65536/65536 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Fiemap of SCRATCH_MNT/subv1/f3 before deleting SCRATCH_MNT/subv1/f4:
+0: [0..127]: shared|last
+Fiemap of SCRATCH_MNT/subv1/f3 after deleting SCRATCH_MNT/subv1/f4:
+0: [0..127]: last
+
+Testing with different subvolumes and without transaction commit
+
+wrote 65536/65536 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Fiemap of SCRATCH_MNT/subv1/f5 before deleting SCRATCH_MNT/subv2/f6:
+0: [0..127]: shared|last
+Fiemap of SCRATCH_MNT/subv1/f5 after deleting SCRATCH_MNT/subv2/f6:
+0: [0..127]: last
+
+Testing with different subvolumes and with transaction commit
+
+wrote 65536/65536 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Fiemap of SCRATCH_MNT/subv1/f7 before deleting SCRATCH_MNT/subv2/f8:
+0: [0..127]: shared|last
+Fiemap of SCRATCH_MNT/subv1/f7 after deleting SCRATCH_MNT/subv2/f8:
+0: [0..127]: last
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2022 SUSE Linux Products GmbH. All Rights Reserved.
+#
+# FS QA Test 280
+#
+# Test that if we have a large file, with file extent items spanning several
+# leaves in the fs tree, and that is shared due to a snapshot, if we COW one of
+# the extents, doing a fiemap will report the respective file range as not
+# shared.
+#
+# This exercises the processing of delayed references for metadata extents in
+# the backref walking code, used by fiemap to determine if an extent is shared.
+#
+. ./common/preamble
+_begin_fstest auto quick compress snapshot fiemap
+
+. ./common/filter
+. ./common/punch # for _filter_fiemap_flags
+
+_supported_fs btrfs
+_require_scratch
+_require_xfs_io_command "fiemap"
+
+_fixed_by_kernel_commit 943553ef9b51 \
+ "btrfs: fix processing of delayed tree block refs during backref walking"
+
+_scratch_mkfs >> $seqres.full 2>&1
+# We use compression because it's a very quick way to create a file with a very
+# large number of extents (compression limits the maximum extent size to 128K)
+# and while using very little disk space.
+_scratch_mount -o compress
+
+# A 128M file full of compressed extents results in a fs tree with a heigth
+# of 2 (root at level 1), so we'll end up with tree block references in the
+# extent tree (if the root was a leaf, we would have only data references).
+$XFS_IO_PROG -f -c "pwrite -b 1M 0 128M" $SCRATCH_MNT/foo | _filter_xfs_io
+
+# Create a RW snapshot of the default subvolume.
+$BTRFS_UTIL_PROG subvolume snapshot $SCRATCH_MNT $SCRATCH_MNT/snap | _filter_scratch
+
+echo
+echo "File foo fiemap before COWing extent:"
+echo
+$XFS_IO_PROG -c "fiemap -v" $SCRATCH_MNT/foo | _filter_fiemap_flags 1
+
+echo
+echo "Overwriting file range [120M, 120M + 128K) in the snapshot"
+echo
+$XFS_IO_PROG -c "pwrite -b 128K 120M 128K" $SCRATCH_MNT/snap/foo | _filter_xfs_io
+# Now fsync the file to force COWing the extent and the path from the root of
+# the snapshot tree down to the leaf where the extent is at.
+$XFS_IO_PROG -c "fsync" $SCRATCH_MNT/snap/foo
+
+echo
+echo "File foo fiemap after COWing extent in the snapshot:"
+echo
+# Now we should have all extents marked as shared except the 128K extent in the
+# file range [120M, 120M + 128K).
+$XFS_IO_PROG -c "fiemap -v" $SCRATCH_MNT/foo | _filter_fiemap_flags 1
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 280
+wrote 134217728/134217728 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Create a snapshot of 'SCRATCH_MNT' in 'SCRATCH_MNT/snap'
+
+File foo fiemap before COWing extent:
+
+0: [0..261887]: shared|encoded
+1: [261888..262143]: shared|encoded|last
+
+Overwriting file range [120M, 120M + 128K) in the snapshot
+
+wrote 131072/131072 bytes at offset 125829120
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+File foo fiemap after COWing extent in the snapshot:
+
+0: [0..245759]: shared|encoded
+1: [245760..246015]: encoded
+2: [246016..261887]: shared|encoded
+3: [261888..262143]: shared|encoded|last
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2022 SUSE Linux Products GmbH. All Rights Reserved.
+#
+# FS QA Test 281
+#
+# Test that if we have a snapshot with a compressed extent that is partially
+# shared between two files, one of them has a size that is not sector size
+# aligned, we create a v2 send stream for the snapshot with compressed data,
+# and then apply that stream to another filesystem, the operation succeeds and
+# no data is missing. Also check that the file that had a reference to the whole
+# extent gets two compressed extents in the new filesystem, with only one of
+# them being shared (reflinked).
+#
+. ./common/preamble
+_begin_fstest auto quick send compress clone fiemap
+
+. ./common/filter
+. ./common/reflink
+. ./common/punch # for _filter_fiemap_flags
+
+_supported_fs btrfs
+_require_test
+_require_scratch_reflink
+_require_btrfs_send_version 2
+_require_xfs_io_command "fiemap"
+_require_fssum
+_require_btrfs_no_nodatacow
+
+_fixed_by_kernel_commit a11452a3709e \
+ "btrfs: send: avoid unaligned encoded writes when attempting to clone range"
+
+send_files_dir=$TEST_DIR/btrfs-test-$seq
+send_stream=$send_files_dir/snap.stream
+snap_fssum=$send_files_dir/snap.fssum
+
+rm -fr $send_files_dir
+mkdir $send_files_dir
+
+_scratch_mkfs >> $seqres.full 2>&1
+_scratch_mount -o compress
+
+# File foo has a size of 65K, which is not sector size aligned for any
+# supported sector size on btrfs.
+$XFS_IO_PROG -f -c "pwrite -S 0xab 0 65K" $SCRATCH_MNT/foo | _filter_xfs_io
+
+# File bar has a compressed extent (and its size is sector size aligned).
+$XFS_IO_PROG -f -c "pwrite -S 0xcd 0 128K" $SCRATCH_MNT/bar | _filter_xfs_io
+
+# Now clone only half of bar's extent into foo.
+$XFS_IO_PROG -c "reflink $SCRATCH_MNT/bar 0 0 64K" $SCRATCH_MNT/foo \
+ | _filter_xfs_io
+
+echo "Creating snapshot and a send stream for it..."
+$BTRFS_UTIL_PROG subvolume snapshot -r $SCRATCH_MNT $SCRATCH_MNT/snap \
+ | _filter_scratch
+$BTRFS_UTIL_PROG send --compressed-data -f $send_stream $SCRATCH_MNT/snap 2>&1 \
+ | _filter_scratch
+
+$FSSUM_PROG -A -f -w $snap_fssum $SCRATCH_MNT/snap
+
+echo "Creating a new filesystem to receive the send stream..."
+_scratch_unmount
+_scratch_mkfs >> $seqres.full 2>&1
+# Mount without compression, we created the stream with data compression enabled
+# so we want to verify that applying the stream preserves the compression.
+_scratch_mount
+
+$BTRFS_UTIL_PROG receive -f $send_stream $SCRATCH_MNT
+
+echo "Verifying data matches the original filesystem..."
+$FSSUM_PROG -r $snap_fssum $SCRATCH_MNT/snap
+
+# Now check that fiemap reports two extents for file bar:
+#
+# 1) The first extent should be encoded, because compression was enabled in the
+# original filesystem, and should also be flagged as shared, since that file
+# range was reflinked with file foo in the original filesystem;
+#
+# 2) The second extent should also be encoded (compression was enabled in the
+# original filesystem), but not shared since that file range was not
+# reflinked in the original filesystem. It should also have the "last" flag
+# set, as it's the last extent in the file.
+#
+echo "File bar fiemap output in the new filesystem:"
+$XFS_IO_PROG -r -c "fiemap -v" $SCRATCH_MNT/snap/bar | _filter_fiemap_flags 1
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 281
+wrote 66560/66560 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 131072/131072 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+linked 65536/65536 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Creating snapshot and a send stream for it...
+Create a readonly snapshot of 'SCRATCH_MNT' in 'SCRATCH_MNT/snap'
+At subvol SCRATCH_MNT/snap
+Creating a new filesystem to receive the send stream...
+At subvol snap
+Verifying data matches the original filesystem...
+OK
+File bar fiemap output in the new filesystem:
+0: [0..127]: shared|encoded
+1: [128..255]: encoded|last
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2023 SUSE Linux Products GmbH. All Rights Reserved.
+#
+# FS QA Test 282
+#
+# Make sure scrub speed limitation works as expected.
+#
+. ./common/preamble
+_begin_fstest auto scrub
+
+. ./common/filter
+
+# real QA test starts here
+_supported_fs btrfs
+_wants_kernel_commit eb3b50536642 \
+ "btrfs: scrub: per-device bandwidth control"
+
+# We want at least 5G for the scratch device.
+_require_scratch_size $(( 5 * 1024 * 1024))
+
+# Make sure we can create scrub progress data file
+if [ -e /var/lib/btrfs ]; then
+ test -w /var/lib/btrfs || _notrun '/var/lib/btrfs is not writable'
+else
+ test -w /var/lib || _notrun '/var/lib/btrfs cannot be created'
+fi
+
+_scratch_mkfs >> $seqres.full 2>&1
+_scratch_mount
+
+uuid=$(findmnt -n -o UUID $SCRATCH_MNT)
+
+devinfo_dir="/sys/fs/btrfs/${uuid}/devinfo/1"
+
+# Check if we have the sysfs interface first.
+if [ ! -f "${devinfo_dir}/scrub_speed_max" ]; then
+ _notrun "No sysfs interface for scrub speed throttle"
+fi
+
+# Create a 2G file for later scrub workload.
+# The 2G size is chosen to fit even DUP on a 5G disk.
+$XFS_IO_PROG -f -c "pwrite -i /dev/urandom 0 2G" $SCRATCH_MNT/file | _filter_xfs_io
+
+# Writeback above data, as scrub only verify the committed data.
+sync
+
+# The first scrub, mostly to grab the speed of the scrub.
+$BTRFS_UTIL_PROG scrub start -B $SCRATCH_MNT >> $seqres.full
+
+# We grab the rate from "scrub status" which supports raw bytes reporting
+#
+# The output looks like this:
+# UUID: 62eaabc5-93e8-445f-b8a7-6f027934aea7
+# Scrub started: Thu Jan 5 14:59:12 2023
+# Status: finished
+# Duration: 0:00:02
+# Total to scrub: 1076166656
+# Rate: 538083328/s
+# Error summary: no errors found
+#
+# What we care is that Rate line.
+init_speed=$($BTRFS_UTIL_PROG scrub status --raw $SCRATCH_MNT | grep "Rate:" |\
+ $AWK_PROG '{print $2}' | cut -f1 -d\/)
+
+# This can happen for older progs
+if [ -z "$init_speed" ]; then
+ _notrun "btrfs-progs doesn't support scrub rate reporting"
+fi
+
+# Cycle mount to drop any possible cache.
+_scratch_cycle_mount
+
+target_speed=$(( $init_speed / 2 ))
+echo "$target_speed" > "${devinfo_dir}/scrub_speed_max"
+
+# The second scrub, to check the throttled speed.
+$BTRFS_UTIL_PROG scrub start -B $SCRATCH_MNT >> $seqres.full
+speed=$($BTRFS_UTIL_PROG scrub status --raw $SCRATCH_MNT | grep "Rate:" |\
+ $AWK_PROG '{print $2}' | cut -f1 -d\/)
+
+# We gave a +- 10% tolerance for the throttle
+if [ "$speed" -gt "$(( $target_speed * 11 / 10 ))" -o \
+ "$speed" -lt "$(( $target_speed * 9 / 10))" ]; then
+ echo "scrub speed $speed Bytes/s is not properly throttled, target is $target_speed Bytes/s"
+fi
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 282
+wrote 2147483648/2147483648 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2023 SUSE Linux Products GmbH. All Rights Reserved.
+#
+# FS QA Test 283
+#
+# Test that send operations do the best cloning decisions when we have extents
+# that are shared but some files refer to the full extent while others refer to
+# only a section of the extent.
+#
+. ./common/preamble
+_begin_fstest auto quick send clone fiemap
+
+. ./common/filter
+. ./common/reflink
+. ./common/punch # for _filter_fiemap_flags
+
+_supported_fs btrfs
+_require_test
+_require_scratch_reflink
+_require_cp_reflink
+_require_xfs_io_command "fiemap"
+_require_fssum
+
+_wants_kernel_commit c7499a64dcf6 \
+ "btrfs: send: optimize clone detection to increase extent sharing"
+
+extent_size=$(( 128 * 1024 ))
+if _scratch_btrfs_is_zoned; then
+ zone_append_max=$(cat "/sys/block/$(_short_dev $SCRATCH_DEV)/queue/zone_append_max_bytes")
+ if [[ $zone_append_max -gt 0 && $zone_append_max -lt $extent_size ]]; then
+ _notrun "zone append max $zone_append_max is smaller than wanted extent size $extent_size"
+ fi
+fi
+
+send_files_dir=$TEST_DIR/btrfs-test-$seq
+send_stream=$send_files_dir/snap.stream
+snap_fssum=$send_files_dir/snap.fssum
+
+rm -fr $send_files_dir
+mkdir $send_files_dir
+
+_scratch_mkfs >> $seqres.full 2>&1
+_scratch_mount
+
+# When using compression, btrfs limits the extent size to 128K, so do not do
+# larger writes and then expect larger extents, as that would break the test
+# if we are run with compression enabled through $MOUNT_OPTIONS (resulting in
+# mismatch with the golden output).
+$XFS_IO_PROG -f -c "pwrite -S 0xab -b 128K 0 128K" $SCRATCH_MNT/foo | _filter_xfs_io
+
+# Now clone file foo twice, which will make the 128K extent shared 3 times.
+_cp_reflink $SCRATCH_MNT/foo $SCRATCH_MNT/bar
+_cp_reflink $SCRATCH_MNT/foo $SCRATCH_MNT/baz
+
+# Overwrite the second half of file foo.
+$XFS_IO_PROG -c "pwrite -S 0xcd -b 64K 64K 64K" $SCRATCH_MNT/foo | _filter_xfs_io
+
+echo "Creating snapshot and a send stream for it..."
+$BTRFS_UTIL_PROG subvolume snapshot -r $SCRATCH_MNT $SCRATCH_MNT/snap \
+ | _filter_scratch
+
+$BTRFS_UTIL_PROG send -f $send_stream $SCRATCH_MNT/snap 2>&1 | _filter_scratch
+
+$FSSUM_PROG -A -f -w $snap_fssum $SCRATCH_MNT/snap
+
+echo "Creating a new filesystem to receive the send stream..."
+_scratch_unmount
+_scratch_mkfs >> $seqres.full 2>&1
+_scratch_mount
+
+$BTRFS_UTIL_PROG receive -f $send_stream $SCRATCH_MNT
+
+echo "Verifying data matches the original filesystem..."
+$FSSUM_PROG -r $snap_fssum $SCRATCH_MNT/snap
+
+# Now verify that all extents, for all files, are shared.
+
+# File 'foo' should have a single 128K extent, which is shared because its first
+# half is referred by files 'bar' and 'baz'.
+echo -e "\nfiemap of file foo:\n"
+$XFS_IO_PROG -r -c "fiemap -v" $SCRATCH_MNT/snap/foo | _filter_fiemap_flags
+
+# File 'bar' should have two 64K shared extents. The first one is shared with
+# files 'foo' and 'baz', while the second one is only shared with file 'baz'.
+echo -e "\nfiemap of file bar:\n"
+$XFS_IO_PROG -r -c "fiemap -v" $SCRATCH_MNT/snap/bar | _filter_fiemap_flags
+
+# File 'baz' should have two 64K shared extents. The first one is shared with
+# files 'foo' and 'bar', while the second one is only shared with file 'bar'.
+echo -e "\nfiemap of file baz:\n"
+$XFS_IO_PROG -r -c "fiemap -v" $SCRATCH_MNT/snap/baz | _filter_fiemap_flags
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 283
+wrote 131072/131072 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 65536/65536 bytes at offset 65536
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Creating snapshot and a send stream for it...
+Create a readonly snapshot of 'SCRATCH_MNT' in 'SCRATCH_MNT/snap'
+At subvol SCRATCH_MNT/snap
+Creating a new filesystem to receive the send stream...
+At subvol snap
+Verifying data matches the original filesystem...
+OK
+
+fiemap of file foo:
+
+0: [0..255]: shared|last
+
+fiemap of file bar:
+
+0: [0..127]: shared
+1: [128..255]: shared|last
+
+fiemap of file baz:
+
+0: [0..127]: shared
+1: [128..255]: shared|last
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2023 SUSE Linux Products GmbH. All Rights Reserved.
+#
+# FS QA Test 284
+#
+# Test btrfs send stream v2, sending and receiving compressed data without
+# decompression at the sending side.
+#
+. ./common/preamble
+_begin_fstest auto quick send compress snapshot
+
+# Modify as appropriate.
+_supported_fs btrfs
+_require_btrfs_send_version 2
+_require_test
+# The size needed is variable as it depends on the specific randomized
+# operations from fsstress and on the value of $LOAD_FACTOR. But require at
+# least $LOAD_FACTOR * 1G, just to be on the safe side.
+_require_scratch_size $(($LOAD_FACTOR * 1 * 1024 * 1024))
+_require_fssum
+
+_fixed_by_git_commit btrfs-progs e3209f8792f4 \
+ "btrfs-progs: receive: fix a corruption when decompressing zstd extents"
+_fixed_by_git_commit btrfs-progs 6f4a51886b37 \
+ "btrfs-progs: receive: fix silent data loss after fall back from encoded write"
+
+send_files_dir=$TEST_DIR/btrfs-test-$seq
+
+rm -fr $send_files_dir
+mkdir $send_files_dir
+
+# Redirect stdout to the .full file and make it not part of the golden output.
+# This is because the number of available compression algorithms may vary across
+# kernel versions, so the number of times we are running this function is
+# variable.
+run_send_test()
+{
+ local algo=$1
+ local snapshot_cmd
+ local first_stream="$send_files_dir/snap1.stream"
+ local second_stream="$send_files_dir/snap2.stream"
+ local first_fssum="$send_files_dir/snap1.fssum"
+ local second_fssum="$send_files_dir/snap2.fssum"
+
+ _scratch_mkfs >> $seqres.full 2>&1
+ _scratch_mount -o compress=$algo
+
+ snapshot_cmd="$BTRFS_UTIL_PROG subvolume snapshot -r $SCRATCH_MNT"
+ snapshot_cmd="$snapshot_cmd $SCRATCH_MNT/snap1"
+
+ # Use a single process so that in case of failure it's easier to
+ # reproduce by using the same seed (logged in $seqres.full).
+ run_check $FSSTRESS_PROG -d $SCRATCH_MNT -p 1 -n $((LOAD_FACTOR * 200)) \
+ -w $FSSTRESS_AVOID -x "$snapshot_cmd" >> $seqres.full
+
+ $BTRFS_UTIL_PROG subvolume snapshot -r $SCRATCH_MNT $SCRATCH_MNT/snap2 \
+ >> $seqres.full
+
+ echo "Creating full and incremental send streams..." >> $seqres.full
+
+ $BTRFS_UTIL_PROG send --compressed-data -q -f $first_stream \
+ $SCRATCH_MNT/snap1 2>&1 | tee -a $seqres.full
+ $BTRFS_UTIL_PROG send --compressed-data -q -f $second_stream \
+ -p $SCRATCH_MNT/snap1 $SCRATCH_MNT/snap2 2>&1 | \
+ tee -a $seqres.full
+
+ echo "Computing the checksums for each snapshot..." >> $seqres.full
+
+ $FSSUM_PROG -A -f -w $first_fssum $SCRATCH_MNT/snap1 2>&1 | \
+ tee -a $seqres.full
+ $FSSUM_PROG -A -f -w $second_fssum -x $SCRATCH_MNT/snap2/snap1 \
+ $SCRATCH_MNT/snap2 2>&1 | tee -a $seqres.full
+
+ echo "Creating a new fs to receive the streams..." >> $seqres.full
+
+ _scratch_unmount
+ _scratch_mkfs >> $seqres.full 2>&1
+ _scratch_mount
+
+ echo "Receiving the streams..." >> $seqres.full
+
+ $BTRFS_UTIL_PROG receive -q -f $first_stream $SCRATCH_MNT 2>&1 | \
+ tee -a $seqres.full
+ $BTRFS_UTIL_PROG receive -q -f $second_stream $SCRATCH_MNT 2>&1 | \
+ tee -a $seqres.full
+
+ echo "Verifying the checksums for each snapshot..." >> $seqres.full
+
+ # On success, fssum outputs only a single line with "OK" to stdout, and
+ # on error it outputs several lines to stdout telling about each file
+ # with data or metadata mismatches. Since the number of times we run
+ # fssum depends on the available compression algorithms for the running
+ # kernel, filter out the success case, so we don't have a mismatch with
+ # the golden output. We only want the mismatch with the golden output in
+ # case there's a checksum failure.
+ $FSSUM_PROG -r $first_fssum $SCRATCH_MNT/snap1 | grep -Ev '^OK$' | \
+ tee -a $seqres.full
+ $FSSUM_PROG -r $second_fssum $SCRATCH_MNT/snap2 | grep -Ev '^OK$' | \
+ tee -a $seqres.full
+
+ # Now receive again the streams in a new filesystem, but this time use
+ # the option --force-decompress of the receiver to verify that it works
+ # as expected.
+ echo "Creating a new fs to receive the streams with decompression..." >> $seqres.full
+
+ _scratch_unmount
+ _scratch_mkfs >> $seqres.full 2>&1
+ _scratch_mount
+
+ echo "Receiving the streams with decompression..." >> $seqres.full
+
+ $BTRFS_UTIL_PROG receive -q --force-decompress -f $first_stream $SCRATCH_MNT 2>&1 \
+ | tee -a $seqres.full
+ $BTRFS_UTIL_PROG receive -q --force-decompress -f $second_stream $SCRATCH_MNT 2>&1 \
+ | tee -a $seqres.full
+
+ echo "Verifying the checksums for each snapshot..." >> $seqres.full
+
+ $FSSUM_PROG -r $first_fssum $SCRATCH_MNT/snap1 | grep -Ev '^OK$' | \
+ tee -a $seqres.full
+ $FSSUM_PROG -r $second_fssum $SCRATCH_MNT/snap2 | grep -Ev '^OK$' | \
+ tee -a $seqres.full
+
+ _scratch_unmount
+ rm -f $send_files_dir/*
+}
+
+algo_list=($(_btrfs_compression_algos))
+for algo in ${algo_list[@]}; do
+ echo -e "\nTesting with $algo...\n" >> $seqres.full
+ run_send_test $algo
+done
+
+# success, all done
+echo "Silence is golden"
+status=0
+exit
--- /dev/null
+QA output created by 284
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023 Meta Platforms, Inc. All Rights Reserved.
+#
+# FS QA Test No. 285
+#
+# Test that mounting a btrfs filesystem properly loads block group size classes.
+#
+. ./common/preamble
+_begin_fstest auto quick
+
+sysfs_size_classes() {
+ local uuid="$(findmnt -n -o UUID "$SCRATCH_MNT")"
+ cat "/sys/fs/btrfs/$uuid/allocation/data/size_classes"
+}
+
+_supported_fs btrfs
+_require_scratch
+_require_btrfs_fs_sysfs
+_require_fs_sysfs allocation/data/size_classes
+
+f="$SCRATCH_MNT/f"
+small=$((16 * 1024))
+medium=$((1024 * 1024))
+large=$((16 * 1024 * 1024))
+
+_scratch_mkfs >/dev/null
+_scratch_mount
+# Write files with extents in each size class
+$XFS_IO_PROG -fc "pwrite -q 0 $small" $f.small
+$XFS_IO_PROG -fc "pwrite -q 0 $medium" $f.medium
+$XFS_IO_PROG -fc "pwrite -q 0 $large" $f.large
+# Sync to force the extent allocation
+sync
+pre=$(sysfs_size_classes)
+
+# cycle mount to drop the block group cache
+_scratch_cycle_mount
+
+# Another write causes us to actually load the block groups
+$XFS_IO_PROG -fc "pwrite -q 0 $large" $f.large.2
+sync
+
+post=$(sysfs_size_classes)
+diff <(echo $pre) <(echo $post)
+
+echo "Silence is golden"
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 285
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2023 SUSE Linux Products GmbH. All Rights Reserved.
+#
+# FS QA Test 286
+#
+# Make sure btrfs dev-replace on missing device won't cause data corruption
+# for NODATASUM data.
+#
+. ./common/preamble
+_begin_fstest auto replace
+
+# Import common functions.
+. ./common/filter
+
+# real QA test starts here
+
+# Modify as appropriate.
+_supported_fs btrfs
+_require_command "$WIPEFS_PROG" wipefs
+_btrfs_get_profile_configs replace-missing
+_require_fssum
+_require_scratch_dev_pool 5
+_scratch_dev_pool_get 4
+_spare_dev_get
+
+workload()
+{
+ local profile=$1
+ local victim="$(echo $SCRATCH_DEV_POOL | $AWK_PROG '{print $2}')"
+
+ echo "=== Profile: $profile ===" >> $seqres.full
+ rm -f $tmp.fssum
+ _scratch_pool_mkfs "$profile" >> $seqres.full 2>&1
+
+ # Use nodatasum mount option, so all data won't have checksum.
+ _scratch_mount -o nodatasum
+
+ $FSSTRESS_PROG -p 10 -n 200 -d $SCRATCH_MNT >> $seqres.full
+ sync
+
+ # Generate fssum for later verification, here we only care
+ # about the file contents, thus we don't bother metadata at all.
+ $FSSUM_PROG -n -d -f -w $tmp.fssum $SCRATCH_MNT
+ _scratch_unmount
+
+ # Wipe devid 2
+ $WIPEFS_PROG -a $victim >> $seqres.full 2>&1
+
+ # Mount the fs with the victim device missing
+ _scratch_mount -o degraded,nodatasum
+
+ # Verify no data corruption first.
+ echo "=== Verify the contents before replace ===" >> $seqres.full
+ $FSSUM_PROG -r $tmp.fssum $SCRATCH_MNT >> $seqres.full 2>&1
+
+ # Replace the missing device
+ $BTRFS_UTIL_PROG replace start -Bf 2 $SPARE_DEV $SCRATCH_MNT >> $seqres.full
+
+ # Drop all cache to make sure later read are all from the disks
+ echo 3 > /proc/sys/vm/drop_caches
+
+ # Re-check the file contents
+ echo "=== Verify the contents after replace ===" >> $seqres.full
+ $FSSUM_PROG -r $tmp.fssum $SCRATCH_MNT >> $seqres.full 2>&1
+
+ _scratch_unmount
+}
+
+for t in "${_btrfs_profile_configs[@]}"; do
+ workload "$t"
+done
+
+_spare_dev_put
+_scratch_dev_pool_put
+
+echo "Silence is golden"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 286
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2023 SUSE Linux Products GmbH. All Rights Reserved.
+#
+# FS QA Test 287
+#
+# Test btrfs' logical to inode ioctls (v1 and v2).
+#
+. ./common/preamble
+_begin_fstest auto quick snapshot clone punch logical_resolve
+
+. ./common/filter.btrfs
+. ./common/reflink
+
+_supported_fs btrfs
+_require_btrfs_scratch_logical_resolve_v2
+_require_scratch_reflink
+_require_xfs_io_command "fpunch"
+
+# This is a test case to test the logical to ino ioctl in general but it also
+# serves as a regression a test for an issue fixed by the following commit.
+_fixed_by_kernel_commit 0cad8f14d70c \
+ "btrfs: fix backref walking not returning all inode refs"
+
+query_logical_ino()
+{
+ $BTRFS_UTIL_PROG inspect-internal logical-resolve -P $* $SCRATCH_MNT
+}
+
+# The IDs of the snapshots (roots) we create may vary if we are using the free
+# space tree or not for example (mkfs options -R free-space-tree and
+# -R ^free-space-tree). So replace their IDs with names so that we don't get
+# golden output mismatches if we are using features that create other roots.
+filter_snapshot_ids()
+{
+ sed -e "s/root $snap1_id\b/snap1/" -e "s/root $snap2_id\b/snap2/"
+}
+
+_scratch_mkfs >> $seqres.full || _fail "mkfs failed"
+_scratch_mount
+
+# Create a file with two extents:
+#
+# 1) One 4M extent covering the file range [0, 4M)
+# 2) Another 4M extent covering the file range [4M, 8M)
+$XFS_IO_PROG -f -c "pwrite -S 0xab -b 4M 0 4M" \
+ -c "fsync" \
+ -c "pwrite -S 0xcd -b 4M 4M 8M" \
+ -c "fsync" $SCRATCH_MNT/foo | _filter_xfs_io
+
+echo "resolve first extent:"
+first_extent_bytenr=$(_btrfs_get_file_extent_item_bytenr "$SCRATCH_MNT/foo" 0)
+query_logical_ino $first_extent_bytenr
+
+echo "resolve second extent:"
+sz_4m=$((4 * 1024 * 1024))
+second_extent_bytenr=$(_btrfs_get_file_extent_item_bytenr "$SCRATCH_MNT/foo" $sz_4m)
+query_logical_ino $second_extent_bytenr
+
+# Now clone both extents twice to the end of the file.
+sz_8m=$((8 * 1024 * 1024))
+$XFS_IO_PROG -c "reflink $SCRATCH_MNT/foo 0 $sz_8m $sz_8m" $SCRATCH_MNT/foo \
+ | _filter_xfs_io
+sz_16m=$((16 * 1024 * 1024))
+$XFS_IO_PROG -c "reflink $SCRATCH_MNT/foo 0 $sz_16m $sz_8m" $SCRATCH_MNT/foo \
+ | _filter_xfs_io
+
+# Now lets resolve the extents again. They should now be listed 3 times each, at
+# the right file offsets.
+echo "resolve first extent:"
+query_logical_ino $first_extent_bytenr
+
+echo "resolve second extent:"
+query_logical_ino $second_extent_bytenr
+
+# Now lets punch a 2M hole at file offset 0. This changes the first file extent
+# item to point to the first extent with an offset of 2M and a length of 2M, so
+# doing a logical resolve with the bytenr of the first extent should not return
+# file offset 0.
+$XFS_IO_PROG -c "fpunch 0 2M" $SCRATCH_MNT/foo
+echo "resolve first extent after punching hole at file range [0, 2M):"
+query_logical_ino $first_extent_bytenr
+
+# Doing a logical resolve call with the BTRFS_LOGICAL_INO_ARGS_IGNORE_OFFSET
+# flag (passing -o to logical-resolve command) should ignore file extent offsets
+# and return file offsets for all file extent items that point to any section of
+# the extent (3 of them, file offsets 2M, 8M and 16M).
+echo "resolve first extent with ignore offset option:"
+query_logical_ino -o $first_extent_bytenr
+
+# Now query for file extent items containing the first extent at offset +1M.
+# Should only return the file offsets 9M and 17M.
+bytenr=$(( $first_extent_bytenr + 1024 * 1024))
+echo "resolve first extent +1M offset:"
+query_logical_ino $bytenr
+
+# Now do the same query again but with the ignore offset ioctl argument. This
+# should returns 3 results, for file offsets 2M, 8M and 16M.
+echo "resolve first extent +1M offset with ignore offset option:"
+query_logical_ino -o $bytenr
+
+# Now query for file extent items containing the first extent at offset +3M.
+# Should return the file offsets 3M and 11M and 19M.
+bytenr=$(( $first_extent_bytenr + 3 * 1024 * 1024))
+echo "resolve first extent +3M offset:"
+query_logical_ino $bytenr
+
+# Now do the same query again but with the ignore offset ioctl argument. This
+# should returns 3 results, for file offsets 2M, 8M and 16M.
+echo "resolve first extent +3M offset with ignore offset option:"
+query_logical_ino -o $bytenr
+
+# Now create two snapshots and then do some queries.
+$BTRFS_UTIL_PROG subvolume snapshot -r $SCRATCH_MNT $SCRATCH_MNT/snap1 \
+ | _filter_scratch
+$BTRFS_UTIL_PROG subvolume snapshot -r $SCRATCH_MNT $SCRATCH_MNT/snap2 \
+ | _filter_scratch
+
+snap1_id=$(_btrfs_get_subvolid $SCRATCH_MNT snap1)
+snap2_id=$(_btrfs_get_subvolid $SCRATCH_MNT snap2)
+
+# Query for the first extent (at offset 0). Should give two entries for each
+# root - default subvolume and the 2 snapshots, for file offsets 8M and 16M.
+echo "resolve first extent:"
+query_logical_ino $first_extent_bytenr | filter_snapshot_ids
+
+# Query for the first extent (at offset 0) with the ignore offset option.
+# Should give 3 entries for each root - default subvolume and the 2 snapshots,
+# for file offsets 2M, 8M and 16M.
+echo "resolve first extent with ignore offset option:"
+query_logical_ino -o $first_extent_bytenr | filter_snapshot_ids
+
+# Now lets punch a 1M hole at file offset 4M. This changes the second file
+# extent item to point to the second extent with an offset of 1M and a length
+# of 3M, so doing a logical resolve with the bytenr of the second extent should
+# not return file offset 4M for root 5 (default subvolume), bit it should return
+# file offset 4M for the files in the snapshots. For all the roots, it should
+# return file offsets 12M and 20M.
+$XFS_IO_PROG -c "fpunch 4M 1M" $SCRATCH_MNT/foo
+echo "resolve second extent after punching hole at file range [4M, 5M):"
+query_logical_ino $second_extent_bytenr | filter_snapshot_ids
+
+# Repeat the query but with the ignore offset option. We should get 3 entries
+# for each root. For the snapshot roots, we should get entries for file offsets
+# 4M, 12M and 20M, while for the default subvolume (root 5) we should get for
+# file offsets 5M, 12M and 20M.
+echo "resolve second extent with ignore offset option:"
+query_logical_ino -o $second_extent_bytenr | filter_snapshot_ids
+
+# Now delete the first snapshot and repeat the last 2 queries.
+$BTRFS_UTIL_PROG subvolume delete -C $SCRATCH_MNT/snap1 | _filter_btrfs_subvol_delete
+
+# Query the second extent with an offset of 0, should return file offsets 12M
+# and 20M for the default subvolume (root 5) and file offsets 4M, 12M and 20M
+# for the second snapshot root.
+echo "resolve second extent:"
+query_logical_ino $second_extent_bytenr | filter_snapshot_ids
+
+# Query the second extent with the ignore offset option, should return file
+# offsets 5M, 12M and 20M for the default subvolume (root 5) and file offsets
+# 4M, 12M and 20M for the second snapshot root.
+echo "resolve second extent with ignore offset option:"
+query_logical_ino -o $second_extent_bytenr | filter_snapshot_ids
+
+status=0
+exit
--- /dev/null
+QA output created by 287
+wrote 4194304/4194304 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 8388608/8388608 bytes at offset 4194304
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+resolve first extent:
+inode 257 offset 0 root 5
+resolve second extent:
+inode 257 offset 4194304 root 5
+linked 8388608/8388608 bytes at offset 8388608
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+linked 8388608/8388608 bytes at offset 16777216
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+resolve first extent:
+inode 257 offset 16777216 root 5
+inode 257 offset 8388608 root 5
+inode 257 offset 0 root 5
+resolve second extent:
+inode 257 offset 20971520 root 5
+inode 257 offset 12582912 root 5
+inode 257 offset 4194304 root 5
+resolve first extent after punching hole at file range [0, 2M):
+inode 257 offset 16777216 root 5
+inode 257 offset 8388608 root 5
+resolve first extent with ignore offset option:
+inode 257 offset 16777216 root 5
+inode 257 offset 8388608 root 5
+inode 257 offset 2097152 root 5
+resolve first extent +1M offset:
+inode 257 offset 17825792 root 5
+inode 257 offset 9437184 root 5
+resolve first extent +1M offset with ignore offset option:
+inode 257 offset 16777216 root 5
+inode 257 offset 8388608 root 5
+inode 257 offset 2097152 root 5
+resolve first extent +3M offset:
+inode 257 offset 19922944 root 5
+inode 257 offset 11534336 root 5
+inode 257 offset 3145728 root 5
+resolve first extent +3M offset with ignore offset option:
+inode 257 offset 16777216 root 5
+inode 257 offset 8388608 root 5
+inode 257 offset 2097152 root 5
+Create a readonly snapshot of 'SCRATCH_MNT' in 'SCRATCH_MNT/snap1'
+Create a readonly snapshot of 'SCRATCH_MNT' in 'SCRATCH_MNT/snap2'
+resolve first extent:
+inode 257 offset 16777216 snap2
+inode 257 offset 8388608 snap2
+inode 257 offset 16777216 snap1
+inode 257 offset 8388608 snap1
+inode 257 offset 16777216 root 5
+inode 257 offset 8388608 root 5
+resolve first extent with ignore offset option:
+inode 257 offset 16777216 snap2
+inode 257 offset 8388608 snap2
+inode 257 offset 2097152 snap2
+inode 257 offset 16777216 snap1
+inode 257 offset 8388608 snap1
+inode 257 offset 2097152 snap1
+inode 257 offset 16777216 root 5
+inode 257 offset 8388608 root 5
+inode 257 offset 2097152 root 5
+resolve second extent after punching hole at file range [4M, 5M):
+inode 257 offset 20971520 snap2
+inode 257 offset 12582912 snap2
+inode 257 offset 4194304 snap2
+inode 257 offset 20971520 snap1
+inode 257 offset 12582912 snap1
+inode 257 offset 4194304 snap1
+inode 257 offset 20971520 root 5
+inode 257 offset 12582912 root 5
+resolve second extent with ignore offset option:
+inode 257 offset 20971520 snap2
+inode 257 offset 12582912 snap2
+inode 257 offset 4194304 snap2
+inode 257 offset 20971520 snap1
+inode 257 offset 12582912 snap1
+inode 257 offset 4194304 snap1
+inode 257 offset 20971520 root 5
+inode 257 offset 12582912 root 5
+inode 257 offset 5242880 root 5
+Delete subvolume 'SCRATCH_MNT/snap1'
+resolve second extent:
+inode 257 offset 20971520 snap2
+inode 257 offset 12582912 snap2
+inode 257 offset 4194304 snap2
+inode 257 offset 20971520 root 5
+inode 257 offset 12582912 root 5
+resolve second extent with ignore offset option:
+inode 257 offset 20971520 snap2
+inode 257 offset 12582912 snap2
+inode 257 offset 4194304 snap2
+inode 257 offset 20971520 root 5
+inode 257 offset 12582912 root 5
+inode 257 offset 5242880 root 5
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2023 SUSE Linux Products GmbH. All Rights Reserved.
+#
+# FS QA Test 288
+#
+# Make sure btrfs-scrub respects the read-only flag.
+#
+. ./common/preamble
+_begin_fstest auto repair quick volume scrub
+
+. ./common/filter
+
+# real QA test starts here
+
+# Modify as appropriate.
+_supported_fs btrfs
+_require_scratch_dev_pool 2
+
+
+_require_odirect
+# Overwriting data is forbidden on a zoned block device
+_require_non_zoned_device "${SCRATCH_DEV}"
+
+_fixed_by_kernel_commit 1f2030ff6e49 \
+ "btrfs: scrub: respect the read-only flag during repair"
+
+_scratch_dev_pool_get 2
+
+# Step 1, create a raid btrfs with one 128K file
+echo "step 1......mkfs.btrfs"
+_scratch_pool_mkfs -d raid1 -b 1G >> $seqres.full 2>&1
+_scratch_mount
+
+$XFS_IO_PROG -f -d -c "pwrite -S 0xaa -b 128K 0 128K" "$SCRATCH_MNT/foobar" |\
+ _filter_xfs_io
+
+# Step 2, corrupt one mirror so we can still repair the fs.
+echo "step 2......corrupt one mirror"
+# ensure btrfs-map-logical sees the tree updates
+sync
+
+logical=$(_btrfs_get_first_logical $SCRATCH_MNT/foobar)
+
+physical1=$(_btrfs_get_physical ${logical} 1)
+devpath1=$(_btrfs_get_device_path ${logical} 1)
+
+_scratch_unmount
+
+echo " corrupt stripe #1, devpath $devpath1 physical $physical1" \
+ >> $seqres.full
+$XFS_IO_PROG -d -c "pwrite -S 0xf1 -b 64K $physical1 64K" $devpath1 \
+ > /dev/null
+
+# Step 3, do a read-only scrub, which should not fix the corruption.
+echo "step 3......do a read-only scrub"
+_scratch_mount -o ro
+$BTRFS_UTIL_PROG scrub start -BRrd $SCRATCH_MNT >> $seqres.full 2>&1
+_scratch_unmount
+
+# Step 4, make sure the corruption is still there
+echo "step 4......verify the corruption is not repaired"
+echo " the first 16 bytes of the extent at mirror 1:"
+$XFS_IO_PROG -c "pread -qv $physical1 16" $devpath1 |\
+ _filter_xfs_io_offset
+
+_scratch_dev_pool_put
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 288
+step 1......mkfs.btrfs
+wrote 131072/131072 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+step 2......corrupt one mirror
+step 3......do a read-only scrub
+step 4......verify the corruption is not repaired
+ the first 16 bytes of the extent at mirror 1:
+XXXXXXXX: f1 f1 f1 f1 f1 f1 f1 f1 f1 f1 f1 f1 f1 f1 f1 f1 ................
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2023 SUSE Linux Products GmbH. All Rights Reserved.
+#
+# FS QA Test 289
+#
+# Make sure btrfs-scrub reports errors correctly for repaired sectors.
+#
+. ./common/preamble
+_begin_fstest auto quick scrub repair
+
+. ./common/filter
+
+# real QA test starts here
+
+# Modify as appropriate.
+_supported_fs btrfs
+_require_scratch
+# No data checksums for NOCOW case, so can't detect corruption and repair data.
+_require_btrfs_no_nodatacow
+
+_require_odirect
+# Overwriting data is forbidden on a zoned block device
+_require_non_zoned_device "${SCRATCH_DEV}"
+
+# The errors reported would be in the unit of sector, thus the number
+# is dependent on the sectorsize.
+_require_btrfs_support_sectorsize 4096
+
+_fixed_by_kernel_commit 79b8ee702c91 \
+ "btrfs: scrub: also report errors hit during the initial read"
+
+# Create a single btrfs with DUP data profile, and create one 128K file.
+_scratch_mkfs -s 4k -d dup -b 1G >> $seqres.full 2>&1
+_scratch_mount
+$XFS_IO_PROG -f -d -c "pwrite -S 0xaa -b 128K 0 128K" "$SCRATCH_MNT/foobar" \
+ > /dev/null
+sync
+
+logical=$(_btrfs_get_first_logical "$SCRATCH_MNT/foobar")
+
+physical1=$(_btrfs_get_physical ${logical} 1)
+devpath1=$(_btrfs_get_device_path ${logical} 1)
+_scratch_unmount
+
+echo " corrupt stripe #1, devpath $devpath1 physical $physical1" \
+ >> $seqres.full
+$XFS_IO_PROG -d -c "pwrite -S 0xf1 -b 64K $physical1 128K" $devpath1 \
+ >> $seqres.full
+
+# Mount and do a scrub and compare the output
+_scratch_mount
+$BTRFS_UTIL_PROG scrub start -BR $SCRATCH_MNT >> $tmp.scrub_report 2>&1
+cat $tmp.scrub_report >> $seqres.full
+
+# Csum errors should be 128K/4K = 32
+csum_errors=$(grep "csum_errors" $tmp.scrub_report | $AWK_PROG '{print $2}')
+if [ $csum_errors -ne 32 ]; then
+ echo "csum_errors incorrect, expect 32 has $csum_errors"
+fi
+
+# And all errors should be repaired, thus corrected errors should also be 32.
+corrected_errors=$(grep "corrected_errors" $tmp.scrub_report | $AWK_PROG '{print $2}')
+if [ $corrected_errors -ne 32 ]; then
+ echo "corrected_errors incorrect, expect 32 has $corrected_errors"
+fi
+
+echo "Silence is golden"
+
+status=0
+exit
--- /dev/null
+QA output created by 289
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2021 Facebook, Inc. All Rights Reserved.
+#
+# FS QA Test 290
+#
+# Test btrfs support for fsverity.
+# This test extends the generic fsverity testing by corrupting inline extents,
+# preallocated extents, holes, and the Merkle descriptor in a btrfs-aware way.
+#
+. ./common/preamble
+_begin_fstest auto quick verity prealloc
+
+# Import common functions.
+. ./common/filter
+. ./common/verity
+
+# Override the default cleanup function.
+_cleanup()
+{
+ cd /
+ _restore_fsverity_signatures
+ rm -f $tmp.*
+}
+
+# real QA test starts here
+_supported_fs btrfs
+_require_scratch_verity
+_require_scratch_nocheck
+_require_odirect
+_require_xfs_io_command "falloc"
+_require_xfs_io_command "pread"
+_require_xfs_io_command "pwrite"
+_require_btrfs_corrupt_block
+_disable_fsverity_signatures
+
+get_ino() {
+ local file=$1
+ stat -c "%i" $file
+}
+
+validate() {
+ local f=$1
+ local sz=$(_get_filesize $f)
+ # buffered io
+ echo $(basename $f)
+ $XFS_IO_PROG -rc "pread -q 0 $sz" $f 2>&1 | _filter_scratch
+ # direct io
+ $XFS_IO_PROG -rdc "pread -q 0 $sz" $f 2>&1 | _filter_scratch
+}
+
+# corrupt the data portion of an inline extent
+corrupt_inline() {
+ local f=$SCRATCH_MNT/inl
+ $XFS_IO_PROG -fc "pwrite -q -S 0x58 0 42" $f
+ local ino=$(get_ino $f)
+ _fsv_enable $f
+ _scratch_unmount
+ # inline data starts at disk_bytenr
+ # overwrite the first u64 with random bogus junk
+ $BTRFS_CORRUPT_BLOCK_PROG -i $ino -x 0 -f disk_bytenr $SCRATCH_DEV > /dev/null 2>&1
+ _scratch_mount
+ validate $f
+}
+
+# preallocate a file, then corrupt it by changing it to a regular file
+corrupt_prealloc_to_reg() {
+ local f=$SCRATCH_MNT/prealloc
+ $XFS_IO_PROG -fc "falloc 0 12k" $f
+ local ino=$(get_ino $f)
+ _fsv_enable $f
+ _scratch_unmount
+ # ensure non-zero at the pre-allocated region on disk
+ # set extent type from prealloc (2) to reg (1)
+ $BTRFS_CORRUPT_BLOCK_PROG -i $ino -x 0 -f type -v 1 $SCRATCH_DEV >/dev/null 2>&1
+ _scratch_mount
+ # now that it's a regular file, reading actually looks at the previously
+ # preallocated region, so ensure that has non-zero contents.
+ head -c 5 /dev/zero | tr '\0' X | _fsv_scratch_corrupt_bytes $f 0
+ validate $f
+}
+
+# corrupt a regular file by changing the type to preallocated
+corrupt_reg_to_prealloc() {
+ local f=$SCRATCH_MNT/reg
+ $XFS_IO_PROG -fc "pwrite -q -S 0x58 0 12288" $f
+ local ino=$(get_ino $f)
+ _fsv_enable $f
+ _scratch_unmount
+ # set type from reg (1) to prealloc (2)
+ $BTRFS_CORRUPT_BLOCK_PROG -i $ino -x 0 -f type -v 2 $SCRATCH_DEV >/dev/null 2>&1
+ _scratch_mount
+ validate $f
+}
+
+# corrupt a file by punching a hole
+corrupt_punch_hole() {
+ local f=$SCRATCH_MNT/punch
+ $XFS_IO_PROG -fc "pwrite -q -S 0x58 0 12288" $f
+ local ino=$(get_ino $f)
+ # make a new extent in the middle, sync so the writes don't coalesce
+ $XFS_IO_PROG -c sync $SCRATCH_MNT
+ $XFS_IO_PROG -fc "pwrite -q -S 0x59 4096 4096" $f
+ _fsv_enable $f
+ _scratch_unmount
+ # change disk_bytenr to 0, representing a hole
+ $BTRFS_CORRUPT_BLOCK_PROG -i $ino -x 4096 -f disk_bytenr -v 0 $SCRATCH_DEV > /dev/null 2>&1
+ _scratch_mount
+ validate $f
+}
+
+# plug hole
+corrupt_plug_hole() {
+ local f=$SCRATCH_MNT/plug
+ $XFS_IO_PROG -fc "pwrite -q -S 0x58 0 12288" $f
+ local ino=$(get_ino $f)
+ $XFS_IO_PROG -fc "falloc 4k 4k" $f
+ _fsv_enable $f
+ _scratch_unmount
+ # change disk_bytenr to some value, plugging the hole
+ $BTRFS_CORRUPT_BLOCK_PROG -i $ino -x 4096 -f disk_bytenr -v 13639680 $SCRATCH_DEV > /dev/null 2>&1
+ _scratch_mount
+ validate $f
+}
+
+# corrupt the fsverity descriptor item indiscriminately (causes EINVAL)
+corrupt_verity_descriptor() {
+ local f=$SCRATCH_MNT/desc
+ $XFS_IO_PROG -fc "pwrite -q -S 0x58 0 12288" $f
+ local ino=$(get_ino $f)
+ _fsv_enable $f
+ _scratch_unmount
+ # key for the descriptor item is <inode, BTRFS_VERITY_DESC_ITEM_KEY, 1>,
+ # 88 is X. So we write 5 Xs to the start of the descriptor
+ $BTRFS_CORRUPT_BLOCK_PROG -r 5 -I $ino,36,1 -v 88 -o 0 -b 5 $SCRATCH_DEV > /dev/null 2>&1
+ _scratch_mount
+ validate $f
+}
+
+# specifically target the root hash in the descriptor (causes EIO)
+corrupt_root_hash() {
+ local f=$SCRATCH_MNT/roothash
+ $XFS_IO_PROG -fc "pwrite -q -S 0x58 0 12288" $f
+ local ino=$(get_ino $f)
+ _fsv_enable $f
+ _scratch_unmount
+ $BTRFS_CORRUPT_BLOCK_PROG -r 5 -I $ino,36,1 -v 88 -o 16 -b 1 $SCRATCH_DEV > /dev/null 2>&1
+ _scratch_mount
+ validate $f
+}
+
+# corrupt the Merkle tree data itself
+corrupt_merkle_tree() {
+ local f=$SCRATCH_MNT/merkle
+ $XFS_IO_PROG -fc "pwrite -q -S 0x58 0 12288" $f
+ local ino=$(get_ino $f)
+ _fsv_enable $f
+ _scratch_unmount
+ # key for the descriptor item is <inode, BTRFS_VERITY_MERKLE_ITEM_KEY, 0>,
+ # 88 is X. So we write 5 Xs to somewhere in the middle of the first
+ # merkle item
+ $BTRFS_CORRUPT_BLOCK_PROG -r 5 -I $ino,37,0 -v 88 -o 100 -b 5 $SCRATCH_DEV > /dev/null 2>&1
+ _scratch_mount
+ validate $f
+}
+
+# real QA test starts here
+_scratch_mkfs >/dev/null
+_scratch_mount
+
+corrupt_inline
+corrupt_prealloc_to_reg
+corrupt_reg_to_prealloc
+corrupt_punch_hole
+corrupt_plug_hole
+corrupt_verity_descriptor
+corrupt_root_hash
+corrupt_merkle_tree
+
+status=0
+exit
--- /dev/null
+QA output created by 290
+inl
+pread: Input/output error
+pread: Input/output error
+prealloc
+pread: Input/output error
+pread: Input/output error
+reg
+pread: Input/output error
+pread: Input/output error
+punch
+pread: Input/output error
+pread: Input/output error
+plug
+pread: Input/output error
+pread: Input/output error
+desc
+SCRATCH_MNT/desc: Invalid argument
+SCRATCH_MNT/desc: Invalid argument
+roothash
+pread: Input/output error
+pread: Input/output error
+merkle
+pread: Input/output error
+pread: Input/output error
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2021 Facebook, Inc. All Rights Reserved.
+#
+# FS QA Test 291
+#
+# Test btrfs consistency after each FUA while enabling verity on a file
+# This test works by following the pattern in log-writes/replay-individual.sh:
+# 1. run a workload (verity + sync) while logging to the log device
+# 2. replay an entry to the replay device
+# 3. snapshot the replay device to the snapshot device
+# 4. run destructive tests on the snapshot device (e.g. mount with orphans)
+# 5. goto 2
+#
+. ./common/preamble
+_begin_fstest auto verity recoveryloop
+
+# Override the default cleanup function.
+_cleanup()
+{
+ cd /
+ _log_writes_cleanup &> /dev/null
+ $LVM_PROG vgremove -f -y $vgname >>$seqres.full 2>&1
+ losetup -d $loop_dev >>$seqres.full 2>&1
+ rm -f $img
+ _restore_fsverity_signatures
+}
+
+# Import common functions.
+. ./common/filter
+. ./common/attr
+. ./common/dmlogwrites
+. ./common/verity
+
+# real QA test starts here
+_supported_fs btrfs
+
+_require_scratch
+_require_test
+_require_loop
+_require_log_writes
+_require_dm_target snapshot
+_require_command $LVM_PROG lvm
+_require_scratch_verity
+_require_btrfs_command inspect-internal dump-tree
+_require_test_program "log-writes/replay-log"
+_disable_fsverity_signatures
+
+sync_loop() {
+ i=$1
+ [ -z "$i" ] && _fail "sync loop needs a number of iterations"
+ while [ $i -gt 0 ]
+ do
+ $XFS_IO_PROG -c sync $SCRATCH_MNT
+ let i-=1
+ done
+}
+
+dump_tree() {
+ local dev=$1
+ $BTRFS_UTIL_PROG inspect-internal dump-tree $dev
+}
+
+count_item() {
+ local dev=$1
+ local item=$2
+ dump_tree $dev | grep -c "$item"
+}
+
+count_merkle_items() {
+ local dev=$1
+ count_item $dev 'VERITY_\(DESC\|MERKLE\)_ITEM'
+}
+
+_log_writes_init $SCRATCH_DEV
+_log_writes_mkfs
+_log_writes_mount
+
+f=$SCRATCH_MNT/fsv
+MB=$((1024 * 1024))
+img=$TEST_DIR/$$.img
+$XFS_IO_PROG -fc "pwrite -q 0 $((10 * $MB))" $f
+$XFS_IO_PROG -c sync $SCRATCH_MNT
+sync_loop 10 &
+sync_proc=$!
+_fsv_enable $f
+$XFS_IO_PROG -c sync $SCRATCH_MNT
+wait $sync_proc
+
+_log_writes_unmount
+_log_writes_remove
+
+# the snapshot and the replay will each be the size of the log writes dev
+# so we create a loop device of size 2 * logwrites and then split it into
+# replay and snapshot with lvm.
+log_writes_blocks=$(blockdev --getsz $LOGWRITES_DEV)
+replay_bytes=$((512 * $log_writes_blocks))
+img_bytes=$((2 * $replay_bytes))
+
+$XFS_IO_PROG -fc "pwrite -q -S 0 $img_bytes $MB" $img >>$seqres.full 2>&1 || \
+ _fail "failed to create image for loop device"
+loop_dev=$(losetup -f --show $img)
+vgname=vg_replay
+lvname=lv_replay
+replay_dev=/dev/mapper/vg_replay-lv_replay
+snapname=lv_snap
+snap_dev=/dev/mapper/vg_replay-$snapname
+
+$LVM_PROG vgcreate -f $vgname $loop_dev >>$seqres.full 2>&1 || _fail "failed to vgcreate $vgname"
+$LVM_PROG lvcreate -L "$replay_bytes"B -n $lvname $vgname -y >>$seqres.full 2>&1 || \
+ _fail "failed to lvcreate $lvname"
+$UDEV_SETTLE_PROG >>$seqres.full 2>&1
+
+replay_log_prog=$here/src/log-writes/replay-log
+num_entries=$($replay_log_prog --log $LOGWRITES_DEV --num-entries)
+entry=$($replay_log_prog --log $LOGWRITES_DEV --replay $replay_dev --find --end-mark mkfs | cut -d@ -f1)
+prev=$(_log_writes_mark_to_entry_number mkfs)
+[ -z "$prev" ] && _fail "failed to locate entry mark 'mkfs'"
+cur=$(_log_writes_find_next_fua $prev)
+
+# state = 0: verity hasn't started
+# state = 1: verity underway
+# state = 2: verity done
+state=0
+while [ ! -z "$cur" ];
+do
+ _log_writes_replay_log_range $cur $replay_dev >> $seqres.full
+
+ $LVM_PROG lvcreate -s -L 4M -n $snapname $vgname/$lvname >>$seqres.full 2>&1 || \
+ _fail "Failed to create snapshot"
+ $UDEV_SETTLE_PROG >>$seqres.full 2>&1
+
+ orphan=$(count_item $snap_dev ORPHAN)
+ [ $state -eq 0 ] && [ $orphan -gt 0 ] && state=1
+
+ pre_mount=$(count_merkle_items $snap_dev)
+ _mount $snap_dev $SCRATCH_MNT || _fail "mount failed at entry $cur"
+ fsverity measure $SCRATCH_MNT/fsv >>$seqres.full 2>&1
+ measured=$?
+ umount $SCRATCH_MNT
+ [ $state -eq 1 ] && [ $measured -eq 0 ] && state=2
+ [ $state -eq 2 ] && ([ $measured -eq 0 ] || _fail "verity done, but measurement failed at entry $cur")
+ post_mount=$(count_merkle_items $snap_dev)
+
+ echo "entry: $cur, state: $state, orphan: $orphan, pre_mount: $pre_mount, post_mount: $post_mount" >> $seqres.full
+
+ if [ $state -eq 1 ]; then
+ [ $post_mount -eq 0 ] || \
+ _fail "mount failed to clear under-construction merkle items pre: $pre_mount, post: $post_mount at entry $cur";
+ fi
+ if [ $state -eq 2 ]; then
+ [ $pre_mount -gt 0 ] || \
+ _fail "expected to have verity items before mount at entry $cur"
+ [ $pre_mount -eq $post_mount ] || \
+ _fail "mount cleared merkle items after verity was enabled $pre_mount vs $post_mount at entry $cur";
+ fi
+
+ $LVM_PROG lvremove $vgname/$snapname -y >>$seqres.full
+
+ prev=$cur
+ cur=$(_log_writes_find_next_fua $(($cur + 1)))
+done
+
+[ $state -eq 2 ] || _fail "expected to reach verity done state"
+
+echo "Silence is golden"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 291
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2023 SUSE Linux Products GmbH. All Rights Reserved.
+#
+# FS QA Test 292
+#
+# Test btrfs behavior with large chunks (size beyond 4G) for basic read-write
+# and discard.
+# This test focus on RAID0.
+#
+. ./common/preamble
+_begin_fstest auto raid volume trim
+
+. ./common/filter
+
+# real QA test starts here
+
+# Modify as appropriate.
+_supported_fs btrfs
+_require_scratch_dev_pool 6
+_require_fstrim
+_fixed_by_kernel_commit a7299a18a179 \
+ "btrfs: fix u32 overflows when left shifting @stripe_nr"
+
+_scratch_dev_pool_get 6
+
+
+datasize=$((5 * 1024 * 1024 * 1024))
+filesize=$((8 * 1024 * 1024))
+nr_files=$(($datasize / $filesize))
+
+# Make sure each device has at least 2G.
+# Btrfs has a limits on per-device stripe length of 1G.
+# Double that so that we can ensure a RAID0 data chunk with 6G size.
+for i in $SCRATCH_DEV_POOL; do
+ devsize=$(blockdev --getsize64 "$i")
+ if [ $devsize -lt $((2 * 1024 * 1024 * 1024)) ]; then
+ _scratch_dev_pool_put
+ _notrun "device $i is too small, need at least 2G"
+ fi
+done
+
+_scratch_pool_mkfs -m raid1 -d raid0 >> $seqres.full 2>&1
+
+# We disable async/sync auto discard, so that btrfs won't try to
+# cache the discard result which can cause unexpected skip for some trim range.
+_scratch_mount -o nodiscard
+_require_batched_discard $SCRATCH_MNT
+
+# Fill the data chunk with 5G data.
+for (( i = 0; i < $nr_files; i++ )); do
+ $XFS_IO_PROG -f -c "pwrite -i /dev/urandom 0 $filesize" \
+ $SCRATCH_MNT/file_$i > /dev/null
+done
+sync
+echo "=== With initial 5G data written ===" >> $seqres.full
+$BTRFS_UTIL_PROG filesystem df $SCRATCH_MNT >> $seqres.full
+
+_scratch_unmount
+
+# Make sure we haven't corrupted anything.
+$BTRFS_UTIL_PROG check --check-data-csum $SCRATCH_DEV >> $seqres.full 2>&1
+if [ $? -ne 0 ]; then
+ _scratch_dev_pool_put
+ _fail "data corruption detected after initial data filling"
+fi
+
+_scratch_mount -o nodiscard
+# Delete half of the data, and do discard
+rm -rf - "$SCRATCH_MNT/*[02468]"
+sync
+$FSTRIM_PROG $SCRATCH_MNT
+
+echo "=== With 2.5G data trimmed ===" >> $seqres.full
+$BTRFS_UTIL_PROG filesystem df $SCRATCH_MNT >> $seqres.full
+_scratch_unmount
+
+# Make sure fstrim doesn't corrupt anything.
+$BTRFS_UTIL_PROG check --check-data-csum $SCRATCH_DEV >> $seqres.full 2>&1
+if [ $? -ne 0 ]; then
+ _scratch_dev_pool_put
+ _fail "data corruption detected after running fstrim"
+fi
+
+_scratch_dev_pool_put
+
+echo "Silence is golden"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 292
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2023 SUSE Linux Products GmbH. All Rights Reserved.
+#
+# FS QA Test No. 293
+#
+# Test that if we have a subvolume with a non-active swap file, we can not
+# activate it if there are any snapshots. Also test that after all the snapshots
+# are removed, we will be able to activate the swapfile.
+#
+. ./common/preamble
+_begin_fstest auto quick swap snapshot remount
+
+_cleanup()
+{
+ cd /
+ rm -f $tmp.*
+ test -n "$swap_file" && swapoff $swap_file &> /dev/null
+}
+
+. ./common/filter.btrfs
+
+_supported_fs btrfs
+_fixed_by_kernel_commit deccae40e4b3 \
+ "btrfs: can_nocow_file_extent should pass down args->strict from callers"
+_require_scratch_swapfile
+
+_scratch_mkfs >> $seqres.full 2>&1
+_scratch_mount
+
+swap_file="$SCRATCH_MNT/swapfile"
+_format_swapfile $swap_file $(($(_get_page_size) * 64)) >> $seqres.full
+
+echo "Creating first snapshot..."
+$BTRFS_UTIL_PROG subvolume snapshot -r $SCRATCH_MNT $SCRATCH_MNT/snap1 | _filter_scratch
+echo "Creating second snapshot..."
+$BTRFS_UTIL_PROG subvolume snapshot $SCRATCH_MNT $SCRATCH_MNT/snap2 | _filter_scratch
+
+echo "Activating swap file... (should fail due to snapshots)"
+_swapon_file $swap_file 2>&1 | _filter_scratch
+
+echo "Deleting first snapshot..."
+$BTRFS_UTIL_PROG subvolume delete -c $SCRATCH_MNT/snap1 | _filter_btrfs_subvol_delete
+
+# We deleted the snapshot and committed the transaction used to delete it (-c),
+# but all its extents are actually only deleted in the background, by the cleaner
+# kthread. So remount, which wakes up the cleaner kthread, with a commit interval
+# of 1 second and sleep for 1.1 seconds - after this we are guaranteed all
+# extents of the snapshot were deleted.
+echo "Remounting and waiting for cleaner thread to remove the first snapshot..."
+_scratch_remount commit=1
+sleep 1.2
+
+echo "Activating swap file... (should fail due to snapshot)"
+_swapon_file $swap_file 2>&1 | _filter_scratch
+
+echo "Deleting second snapshot..."
+$BTRFS_UTIL_PROG subvolume delete -c $SCRATCH_MNT/snap2 | _filter_btrfs_subvol_delete
+
+echo "Remounting and waiting for cleaner thread to remove the second snapshot..."
+_scratch_remount commit=1
+sleep 1.2
+
+# No more snapshots, we should be able to activate the swap file.
+echo "Activating swap file..."
+_swapon_file $swap_file
+echo "Disabling swap file..."
+swapoff $swap_file
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 293
+Creating first snapshot...
+Create a readonly snapshot of 'SCRATCH_MNT' in 'SCRATCH_MNT/snap1'
+Creating second snapshot...
+Create a snapshot of 'SCRATCH_MNT' in 'SCRATCH_MNT/snap2'
+Activating swap file... (should fail due to snapshots)
+swapon: SCRATCH_MNT/swapfile: swapon failed: Invalid argument
+Deleting first snapshot...
+Delete subvolume 'SCRATCH_MNT/snap1'
+Remounting and waiting for cleaner thread to remove the first snapshot...
+Activating swap file... (should fail due to snapshot)
+swapon: SCRATCH_MNT/swapfile: swapon failed: Invalid argument
+Deleting second snapshot...
+Delete subvolume 'SCRATCH_MNT/snap2'
+Remounting and waiting for cleaner thread to remove the second snapshot...
+Activating swap file...
+Disabling swap file...
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2023 SUSE Linux Products GmbH. All Rights Reserved.
+#
+# FS QA Test No. 294
+#
+# Test btrfs write behavior with large RAID56 chunks (size beyond 4G).
+#
+. ./common/preamble
+_begin_fstest auto raid volume
+
+# Import common functions.
+. ./common/filter
+
+# real QA test starts here
+
+# Modify as appropriate.
+_supported_fs btrfs
+
+# No zoned support for RAID56 yet.
+_require_non_zoned_device "${SCRATCH_DEV}"
+
+_require_scratch_dev_pool 8
+_fixed_by_kernel_commit a7299a18a179 \
+ "btrfs: fix u32 overflows when left shifting @stripe_nr"
+_fixed_by_kernel_commit cb091225a538 \
+ "btrfs: fix remaining u32 overflows when left shifting stripe_nr"
+
+_scratch_dev_pool_get 8
+
+datasize=$((5 * 1024 * 1024 * 1024))
+
+
+workload()
+{
+ local data_profile=$1
+
+ _scratch_pool_mkfs -m raid1 -d $data_profile >> $seqres.full 2>&1
+ _scratch_mount
+ $XFS_IO_PROG -f -c "pwrite -b 1m 0 $datasize" $SCRATCH_MNT/foobar > /dev/null
+
+ # Unpatched kernel would trigger an ASSERT() or crash at writeback.
+ sync
+
+ echo "=== With initial 5G data written ($data_profile) ===" >> $seqres.full
+ $BTRFS_UTIL_PROG filesystem df $SCRATCH_MNT >> $seqres.full
+ _scratch_unmount
+
+ # Make sure we haven't corrupted anything.
+ $BTRFS_UTIL_PROG check --check-data-csum $SCRATCH_DEV >> $seqres.full 2>&1
+ if [ $? -ne 0 ]; then
+ _scratch_dev_pool_put
+ _fail "data corruption detected after initial data filling"
+ fi
+}
+
+# Make sure each device has at least 2G.
+# Btrfs has a limits on per-device stripe length of 1G.
+# Double that so that we can ensure a RAID6 data chunk with 6G size.
+for i in $SCRATCH_DEV_POOL; do
+ devsize=$(blockdev --getsize64 "$i")
+ if [ $devsize -lt $((2 * 1024 * 1024 * 1024)) ]; then
+ _scratch_dev_pool_put
+ _notrun "device $i is too small, need at least 2G"
+ fi
+done
+
+workload raid5
+workload raid6
+
+_scratch_dev_pool_put
+echo "Silence is golden"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 294
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2023 SUSE Linux Products GmbH. All Rights Reserved.
+#
+# FS QA Test No. 295
+#
+# Make sure btrfs handles critical errors gracefully during mount.
+#
+. ./common/preamble
+_begin_fstest auto quick dangerous
+
+. ./common/filter
+_supported_fs btrfs
+_require_scratch
+# Directly writing to the device, which may not work with a zoned device
+_require_non_zoned_device "$SCRATCH_DEV"
+
+# Use single metadata profile so we only need to corrupt one copy of tree block
+_scratch_mkfs -m single > $seqres.full
+
+logical_root=$($BTRFS_UTIL_PROG inspect dump-tree -t root "$SCRATCH_DEV" | \
+ grep leaf | head -n1 | cut -f2 -d\ )
+physical_root=$(_btrfs_get_physical $logical_root 1)
+
+echo "tree root logical=$logical_root" >> $seqres.full
+echo "tree root physical=$physical_root" >> $seqres.full
+
+_pwrite_byte 0x00 "$physical_root" 4 $SCRATCH_DEV >> $seqres.full
+
+# mount may lead to crash
+_try_scratch_mount >> $seqres.full 2>&1
+
+echo "Silence is golden"
+
+# Re-create the fs to avoid false alert from the corrupted fs.
+_scratch_mkfs -m single >> $seqres.full
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 295
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2023 SUSE Linux Products GmbH. All Rights Reserved.
+#
+# FS QA Test No. 296
+#
+# Make sure that per-fs features sysfs interface get properly updated
+# when a new feature is added.
+#
+. ./common/preamble
+_begin_fstest auto quick balance
+
+# real QA test starts here
+_supported_fs btrfs
+_require_scratch_dev_pool 3
+_fixed_by_kernel_commit b7625f461da6 \
+ "btrfs: sysfs: update fs features directory asynchronously"
+
+# We need the global features support
+_require_btrfs_fs_sysfs
+
+global_features="/sys/fs/btrfs/features"
+# Make sure we have support RAID1C34 first
+if [ ! -f "${global_features}/raid1c34" ]; then
+ _notrun "no RAID1C34 support"
+fi
+
+_scratch_dev_pool_get 3
+
+# Go the very basic profile first, so that even older progs can support it.
+_scratch_pool_mkfs -m dup -d single >> $seqres.full 2>&1
+
+_scratch_mount
+uuid="$(findmnt -n -o UUID "$SCRATCH_MNT")"
+per_fs_features="/sys/fs/btrfs/${uuid}/features"
+
+# First we need per-fs features directory
+if [ ! -d "${per_fs_features}" ]; then
+ _notrun "no per-fs features sysfs directory"
+fi
+
+# Make sure the per-fs features doesn't include raid1c34
+if [ -f "${per_fs_features}/raid1c34" ]; then
+ _fail "raid1c34 feature found unexpectedly"
+fi
+
+# Balance to RAID1C3
+$BTRFS_UTIL_PROG balance start -mconvert=raid1c3 "$SCRATCH_MNT" >> $seqres.full
+
+# Sync before checking for sysfs update during cleaner_kthread().
+sync
+
+# Check if the per-fs features directory contains raid1c34 now
+# Make sure the per-fs features doesn't include raid1c34
+if [ ! -f "${per_fs_features}/raid1c34" ]; then
+ _fail "raid1c34 feature not found"
+fi
+
+echo "Silence is golden"
+
+_scratch_unmount
+_scratch_dev_pool_put
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 296
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2023 SUSE Linux Products GmbH. All Rights Reserved.
+#
+# FS QA Test 297
+#
+# Make sure btrfs scrub can fix parity stripe corruption
+#
+. ./common/preamble
+_begin_fstest auto quick raid scrub
+
+. ./common/filter
+
+_supported_fs btrfs
+_require_odirect
+_require_non_zoned_device "${SCRATCH_DEV}"
+_require_scratch_dev_pool 3
+_fixed_by_kernel_commit 486c737f7fdc \
+ "btrfs: raid56: always verify the P/Q contents for scrub"
+
+workload()
+{
+ local profile=$1
+ local nr_devs=$2
+
+ echo "=== Testing $nr_devs devices $profile ===" >> $seqres.full
+ _scratch_dev_pool_get $nr_devs
+
+ _scratch_pool_mkfs -d $profile -m single >> $seqres.full 2>&1
+ # Use v2 space cache to prevent v1 space cache affecting
+ # the result.
+ _scratch_mount -o space_cache=v2
+
+ # Create one 64K extent which would cover one data stripe.
+ $XFS_IO_PROG -f -d -c "pwrite -S 0xaa -b 64K 0 64K" \
+ "$SCRATCH_MNT/foobar" > /dev/null
+ sync
+
+ # Corrupt the P/Q stripe
+ local logical=$(_btrfs_get_first_logical $SCRATCH_MNT/foobar)
+
+ # The 2nd copy is pointed to P stripe directly.
+ physical_p=$(_btrfs_get_physical ${logical} 2)
+ devpath_p=$(_btrfs_get_device_path ${logical} 2)
+
+ _scratch_unmount
+
+ echo "Corrupt stripe P at devpath $devpath_p physical $physical_p" \
+ >> $seqres.full
+ $XFS_IO_PROG -d -c "pwrite -S 0xff -b 64K $physical_p 64K" $devpath_p \
+ > /dev/null
+
+ # Do a scrub to try repair the P stripe.
+ _scratch_mount -o space_cache=v2
+ $BTRFS_UTIL_PROG scrub start -BdR $SCRATCH_MNT >> $seqres.full 2>&1
+ _scratch_unmount
+
+ # Verify the repaired content directly
+ local output=$($XFS_IO_PROG -c "pread -qv $physical_p 16" $devpath_p | _filter_xfs_io_offset)
+ local expect="XXXXXXXX: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................"
+
+ echo "The first 16 bytes of parity stripe after scrub:" >> $seqres.full
+ echo $output >> $seqres.full
+ if [ "$output" != "$expect" ]; then
+ echo "Unexpected parity content"
+ echo "has:"
+ echo "$output"
+ echo "expect"
+ echo "$expect"
+ fi
+
+ # Last safenet, let btrfs check --check-data-csum to do an offline scrub.
+ $BTRFS_UTIL_PROG check --check-data-csum $SCRATCH_DEV >> $seqres.full 2>&1
+ if [ $? -ne 0 ]; then
+ echo "Error detected after the scrub"
+ fi
+ _scratch_dev_pool_put
+}
+
+workload raid5 2
+workload raid6 3
+
+echo "Silence is golden"
+status=0
+exit
--- /dev/null
+QA output created by 297
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023 Oracle. All Rights Reserved.
+#
+# FS QA Test 298
+#
+# Check if the device scan registers for a single-device seed and drops
+# it from the kernel if it is eventually marked as non-seed.
+#
+. ./common/preamble
+_begin_fstest auto quick seed
+
+_supported_fs btrfs
+_require_command "$BTRFS_TUNE_PROG" btrfstune
+_require_command "$WIPEFS_PROG" wipefs
+_require_scratch_dev_pool 2
+_scratch_dev_pool_get 1
+_spare_dev_get
+
+$WIPEFS_PROG -a $SCRATCH_DEV
+$WIPEFS_PROG -a $SPARE_DEV
+
+echo "#setup seed sprout device" >> $seqres.full
+_scratch_mkfs "-b 300M" >> $seqres.full 2>&1 || \
+ _fail "Fail to make SCRATCH_DEV with -b 300M"
+$BTRFS_TUNE_PROG -S 1 $SCRATCH_DEV
+_scratch_mount >> $seqres.full 2>&1
+$BTRFS_UTIL_PROG device add $SPARE_DEV $SCRATCH_MNT >> $seqres.full
+_scratch_unmount
+$BTRFS_UTIL_PROG device scan --forget
+
+echo "#Scan seed device and check using mount" >> $seqres.full
+$BTRFS_UTIL_PROG device scan $SCRATCH_DEV >> $seqres.full
+_mount $SPARE_DEV $SCRATCH_MNT
+umount $SCRATCH_MNT
+
+echo "#check again, ensures seed device still in kernel" >> $seqres.full
+_mount $SPARE_DEV $SCRATCH_MNT
+umount $SCRATCH_MNT
+
+echo "#Now scan of non-seed device makes kernel forget" >> $seqres.full
+$BTRFS_TUNE_PROG -f -S 0 $SCRATCH_DEV >> $seqres.full 2>&1
+$BTRFS_UTIL_PROG device scan $SCRATCH_DEV >> $seqres.full
+
+echo "#Sprout mount must fail for missing seed device" >> $seqres.full
+_mount $SPARE_DEV $SCRATCH_MNT > /dev/null 2>&1
+[[ $? == 32 ]] || _fail "mount failed to fail"
+
+_spare_dev_put
+_scratch_dev_pool_put
+
+# success, all done
+echo Silence is golden
+status=0
+exit
--- /dev/null
+QA output created by 298
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Meta Platforms, Inc. All Rights Reserved.
+#
+# FS QA Test 299
+#
+# Given a file with extents:
+# [0 : 4096) (inline)
+# [4096 : N] (prealloc)
+# if a user uses the ioctl BTRFS_IOC_LOGICAL_INO[_V2] asking for the file of the
+# non-inline extent, it results in reading the offset field of the inline
+# extent, which is meaningless (it is full of user data..). If we are
+# particularly lucky, it can be past the end of the extent buffer, resulting in
+# a crash. This test creates that circumstance and asserts that logical inode
+# resolution is still successful.
+#
+. ./common/preamble
+_begin_fstest auto quick preallocrw logical_resolve
+
+# real QA test starts here
+# Modify as appropriate.
+_supported_fs btrfs
+_require_scratch
+_require_xfs_io_command "falloc" "-k"
+_require_btrfs_command inspect-internal logical-resolve
+# Can't run with nodatacow because we need to be able to create an inline extent
+# in a range with a prealloc extent, which can only happen with COW enabled.
+_require_btrfs_no_nodatacow
+_fixed_by_kernel_commit 560840afc3e6 \
+ "btrfs: fix resolving backrefs for inline extent followed by prealloc"
+
+# This test needs to create conditions s.t. the special inode's inline extent
+# is the first item in a leaf. Therefore, fix a leaf size and add
+# items that are otherwise not necessary to reproduce the inline-prealloc
+# condition to get to such a state.
+#
+# Roughly, the idea for getting the right item fill is to:
+# 1. create extra inline items to cause leaf splitting.
+# 2. put the target item in the middle so it is likely to catch the split
+# 3. add an extra variable inline item to tweak any final adjustments
+#
+# It took a bit of trial and error to hit working counts of inline items, since
+# it also had to account for dir and index items all going to the front.
+
+# use a 64k nodesize so that an fs with 64k pages and no subpage sector size
+# support will correctly reproduce the problem.
+_scratch_mkfs "--nodesize 64k" >> $seqres.full || _fail "mkfs failed"
+_scratch_mount
+
+f=$SCRATCH_MNT/f
+# write extra files before the evil file to use up the leaf and
+# help trick leaf balancing
+for i in {1..41}; do
+ $XFS_IO_PROG -fc "pwrite -q 0 1024" $f.inl.$i
+done
+
+# write a variable inline file to help pad the preceeding leaf
+$XFS_IO_PROG -fc "pwrite -q 0 1" $f.inl-var.$i
+
+# falloc the evil file whose inline extent will start a leaf
+$XFS_IO_PROG -fc "falloc -k 0 1m" $f.evil
+$XFS_IO_PROG -fc fsync $f.evil
+
+# write extra files after the evil file to use up the leaf and
+# help trick leaf balancing
+for i in {1..42}; do
+ $XFS_IO_PROG -fc "pwrite -q 0 1024" $f.inl.2.$i
+done
+
+# grab the prealloc offset from dump tree while it's still the only
+# extent data item for the inode
+logical=$(_btrfs_get_file_extent_item_bytenr $f.evil 0)
+
+# do the "small write; fsync; small write" pattern which reproduces the desired
+# item pattern of an inline extent followed by a preallocated extent. The 23
+# size is somewhat arbitrary, but ensures that the offset field is past the eb
+# when we are item 0 (borrowed from the actual crash this reproduces).
+$XFS_IO_PROG -fc "pwrite -q 0 23" $f.evil
+$XFS_IO_PROG -fc fsync $f.evil
+$XFS_IO_PROG -fc "pwrite -q 0 23" $f.evil
+
+# ensure we have all the extent_data items for when we do logical to inode
+# resolution
+sync
+
+# trigger the backref walk which accesses the bad inline extent
+btrfs inspect-internal logical-resolve $logical $SCRATCH_MNT
+
+echo "Silence is golden"
+status=0
+exit
--- /dev/null
+QA output created by 299
+Silence is golden
--- /dev/null
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023 Meta Platforms, Inc. All Rights Reserved.
+#
+# FS QA Test 300
+#
+# Validate that snapshots taken while in a remapped namespace preserve
+# the permissions of the user.
+#
+. ./common/preamble
+
+_begin_fstest auto quick subvol snapshot
+_register_cleanup "cleanup"
+
+_supported_fs btrfs
+_fixed_by_kernel_commit 94628ad94408 \
+ "btrfs: copy dir permission and time when creating a stub subvolume"
+
+_require_test
+_require_user
+_require_group
+_require_unix_perm_checking
+_require_unshare --keep-caps --map-auto --map-root-user
+
+test_dir="${TEST_DIR}/${seq}"
+cleanup() {
+ rm -rf $test_dir
+ cd /
+ rm -rf $tmp.*
+}
+
+rm -rf $test_dir
+mkdir $test_dir
+chown $qa_user:$qa_group $test_dir
+
+# _user_do executes each command as $qa_user in its own subshell. unshare
+# sets the namespace for the running shell. The test must run in one user
+# subshell to preserve the namespace over multiple commands.
+_user_do "
+cd ${test_dir};
+unshare --user --keep-caps --map-auto --map-root-user;
+$BTRFS_UTIL_PROG subvolume create subvol;
+touch subvol/{1,2,3};
+$BTRFS_UTIL_PROG subvolume create subvol/subsubvol;
+touch subvol/subsubvol/{4,5,6};
+$BTRFS_UTIL_PROG subvolume snapshot subvol snapshot;
+"
+
+find $test_dir/. -printf "%M %u %g ./%P\n"
+
+status=0
+exit
--- /dev/null
+QA output created by 300
+Create subvolume './subvol'
+Create subvolume 'subvol/subsubvol'
+Create a snapshot of 'subvol' in './snapshot'
+drwxr-xr-x fsgqa fsgqa ./
+drwxr-xr-x fsgqa fsgqa ./subvol
+-rw-r--r-- fsgqa fsgqa ./subvol/1
+-rw-r--r-- fsgqa fsgqa ./subvol/2
+-rw-r--r-- fsgqa fsgqa ./subvol/3
+drwxr-xr-x fsgqa fsgqa ./subvol/subsubvol
+-rw-r--r-- fsgqa fsgqa ./subvol/subsubvol/4
+-rw-r--r-- fsgqa fsgqa ./subvol/subsubvol/5
+-rw-r--r-- fsgqa fsgqa ./subvol/subsubvol/6
+drwxr-xr-x fsgqa fsgqa ./snapshot
+-rw-r--r-- fsgqa fsgqa ./snapshot/1
+-rw-r--r-- fsgqa fsgqa ./snapshot/2
+-rw-r--r-- fsgqa fsgqa ./snapshot/3
+drwxr-xr-x fsgqa fsgqa ./snapshot/subsubvol
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023 Meta Platforms, Inc. All Rights Reserved.
+#
+# FS QA Test 301
+#
+# Test common btrfs simple quotas scenarios involving sharing extents and
+# removing them in various orders.
+#
+. ./common/preamble
+_begin_fstest auto quick qgroup clone subvol prealloc snapshot remount
+
+# Import common functions.
+. ./common/reflink
+
+# Real QA test starts here.
+
+# Modify as appropriate.
+_supported_fs btrfs
+_require_scratch_reflink
+_require_cp_reflink
+_require_btrfs_command inspect-internal dump-tree
+_require_xfs_io_command "falloc"
+_require_scratch_enable_simple_quota
+_require_no_compress
+
+subv=$SCRATCH_MNT/subv
+nested=$SCRATCH_MNT/subv/nested
+snap=$SCRATCH_MNT/snap
+nr_fill=512
+fill_sz=$((8 * 1024))
+total_fill=$(($nr_fill * $fill_sz))
+nodesize=$($BTRFS_UTIL_PROG inspect-internal dump-super $SCRATCH_DEV | \
+ grep nodesize | $AWK_PROG '{print $2}')
+ext_sz=$((128 * 1024 * 1024))
+limit_nr=8
+limit=$(($ext_sz * $limit_nr))
+
+prep_fio_config=$tmp.fio
+fio_out=$tmp.fio.out
+cat >$prep_fio_config <<EOF
+[randwrite]
+name=filler
+directory=${subv}
+rw=randwrite
+nrfiles=${nr_fill}
+filesize=${fill_sz}
+EOF
+_require_fio $fio_config
+
+get_qgroup_usage()
+{
+ local qgroupid=$1
+
+ $BTRFS_UTIL_PROG qgroup show --sync --raw $SCRATCH_MNT | \
+ grep "$qgroupid" | $AWK_PROG '{print $3}'
+}
+
+get_subvol_usage()
+{
+ local subvolid=$1
+
+ get_qgroup_usage "0/$subvolid"
+}
+
+count_subvol_owned_metadata()
+{
+ local subvolid=$1
+
+ # We need to sync so that the metadata extents are on disk and visible
+ # to dump-tree.
+ sync
+ # Find nodes and leaves owned by the subvol, then get unique offsets
+ # to account for snapshots sharing metadata.
+ count=$($BTRFS_UTIL_PROG inspect-internal dump-tree $SCRATCH_DEV | \
+ grep "owner $subvolid" | $AWK_PROG '{print $2}' | sort | \
+ uniq | wc -l)
+ # Output bytes rather than number of metadata blocks.
+ echo $(($count * $nodesize))
+}
+
+check_qgroup_usage()
+{
+ local qgroupid=$1
+ local expected=$2
+ local actual=$(get_qgroup_usage $qgroupid)
+
+ [ $expected -eq $actual ] || \
+ echo "qgroup $qgroupid mismatched usage $actual vs $expected"
+}
+
+check_subvol_usage()
+{
+ local subvolid=$1
+ local expected_data=$2
+ local expected_meta=$(count_subvol_owned_metadata $subvolid)
+ local actual=$(get_subvol_usage $subvolid)
+ local expected=$(($expected_data + $expected_meta))
+
+ [ $expected -eq $actual ] || \
+ echo "subvol $subvolid mismatched usage $actual vs $expected (expected data $expected_data expected meta $expected_meta diff $(($actual - $expected)))"
+}
+
+set_subvol_limit()
+{
+ local subvolid=$1
+ local limit=$2
+
+ $BTRFS_UTIL_PROG qgroup limit $2 0/$1 $SCRATCH_MNT
+}
+
+# We need the cleaner thread to run to actually delete extents for test
+# cases that care about that. The remount wakes up the cleaner thread and sets
+# the commit interval to 1s, so the 1.5s sleep is enough to wait for the cleaner
+# thread to run.
+trigger_cleaner()
+{
+ _scratch_remount commit=1
+ sleep 1.5
+}
+
+cycle_mount_check_subvol_usage()
+{
+ _scratch_cycle_mount
+ check_subvol_usage $@
+}
+
+do_write()
+{
+ local file=$1
+ local sz=$2
+
+ $XFS_IO_PROG -fc "pwrite -q 0 $sz" $file
+}
+
+do_enospc_write()
+{
+ local file=$1
+ local sz=$2
+
+ do_write $file $sz
+}
+
+do_falloc()
+{
+ local file=$1
+ local sz=$2
+
+ $XFS_IO_PROG -fc "falloc 0 $sz" $file
+}
+
+do_enospc_falloc()
+{
+ local file=$1
+ local sz=$2
+
+ do_falloc $file $sz
+}
+
+enable_quota()
+{
+ local mode=$1
+
+ [ $mode == "n" ] && return
+ arg=$([ $mode == "s" ] && echo "--simple")
+
+ $BTRFS_UTIL_PROG quota enable $arg $SCRATCH_MNT
+}
+
+get_subvid()
+{
+ _btrfs_get_subvolid $SCRATCH_MNT subv
+}
+
+get_snapid()
+{
+ _btrfs_get_subvolid $SCRATCH_MNT snap
+}
+
+get_nestedid()
+{
+ _btrfs_get_subvolid $SCRATCH_MNT subv/nested
+}
+
+prepare()
+{
+ _scratch_mkfs >> $seqres.full
+ _scratch_mount
+ enable_quota "s"
+ $BTRFS_UTIL_PROG subvolume create $subv >> $seqres.full
+ local subvid=$(get_subvid)
+ set_subvol_limit $subvid $limit
+ check_subvol_usage $subvid 0
+
+ # Create a bunch of little filler files to generate several levels in
+ # the btree, to make snapshotting sharing scenarios complex enough.
+ $FIO_PROG $prep_fio_config --output=$fio_out
+ check_subvol_usage $subvid $total_fill
+
+ # Create a single file whose extents we will explicitly share/unshare.
+ do_write $subv/f $ext_sz
+ check_subvol_usage $subvid $(($total_fill + $ext_sz))
+}
+
+prepare_snapshotted()
+{
+ prepare
+ $BTRFS_UTIL_PROG subvolume snapshot $subv $snap >> $seqres.full
+ check_subvol_usage $(get_subvid) $(($total_fill + $ext_sz))
+ check_subvol_usage $(get_snapid) 0
+}
+
+prepare_nested()
+{
+ prepare
+ local subvid=$(get_subvid)
+ $BTRFS_UTIL_PROG qgroup create 1/100 $SCRATCH_MNT
+ $BTRFS_UTIL_PROG qgroup limit $limit 1/100 $SCRATCH_MNT
+ $BTRFS_UTIL_PROG qgroup assign 0/$subvid 1/100 $SCRATCH_MNT >> $seqres.full
+ $BTRFS_UTIL_PROG subvolume create $nested >> $seqres.full
+ local nestedid=$(get_nestedid)
+ do_write $nested/f $ext_sz
+ check_subvol_usage $nestedid $ext_sz
+ check_subvol_usage $subvid $(($total_fill + $ext_sz))
+ local subv_usage=$(get_subvol_usage $subvid)
+ local nested_usage=$(get_subvol_usage $nestedid)
+ check_qgroup_usage 1/100 $(($subv_usage + $nested_usage))
+}
+
+# Write in a single subvolume, including going over the limit.
+basic_accounting()
+{
+ echo "basic accounting"
+ prepare
+ local subvid=$(get_subvid)
+ rm $subv/f
+ check_subvol_usage $subvid $total_fill
+ cycle_mount_check_subvol_usage $subvid $total_fill
+ do_write $subv/tmp 512M
+ rm $subv/tmp
+ do_write $subv/tmp 512M
+ rm $subv/tmp
+ do_enospc_falloc $subv/large_falloc 2G
+ do_enospc_write $subv/large 2G
+ _scratch_unmount
+}
+
+# Write to the same range of a file a bunch of times in a row
+# to test extent aware reservations.
+reservation_accounting()
+{
+ echo "reservation accounting"
+ prepare
+ for i in $(seq 10); do
+ do_write $subv/tmp 512M
+ rm $subv/tmp
+ done
+ do_enospc_write $subv/large 2G
+ _scratch_unmount
+}
+
+# Write in a snapshot.
+snapshot_accounting()
+{
+ echo "snapshot accounting"
+ prepare_snapshotted
+ local subvid=$(get_subvid)
+ local snapid=$(get_snapid)
+ touch $snap/f
+ check_subvol_usage $subvid $(($total_fill + $ext_sz))
+ check_subvol_usage $snapid 0
+ do_write $snap/f $ext_sz
+ check_subvol_usage $subvid $(($total_fill + $ext_sz))
+ check_subvol_usage $snapid $ext_sz
+ rm $snap/f
+ check_subvol_usage $subvid $(($total_fill + $ext_sz))
+ check_subvol_usage $snapid 0
+ rm $subv/f
+ check_subvol_usage $subvid $total_fill
+ check_subvol_usage $snapid 0
+ cycle_mount_check_subvol_usage $subvid $total_fill
+ check_subvol_usage $snapid 0
+ _scratch_unmount
+}
+
+# Delete the original ref first after a snapshot.
+delete_snapshot_src_ref()
+{
+ echo "delete src ref first"
+ prepare_snapshotted
+ local subvid=$(get_subvid)
+ local snapid=$(get_snapid)
+ rm $subv/f
+ check_subvol_usage $subvid $(($total_fill + $ext_sz))
+ check_subvol_usage $snapid 0
+ rm $snap/f
+ trigger_cleaner
+ check_subvol_usage $subvid $total_fill
+ check_subvol_usage $snapid 0
+ cycle_mount_check_subvol_usage $subvid $total_fill
+ check_subvol_usage $snapid 0
+ _scratch_unmount
+}
+
+# Delete the snapshot ref first after a snapshot.
+delete_snapshot_ref()
+{
+ echo "delete snapshot ref first"
+ prepare_snapshotted
+ local subvid=$(get_subvid)
+ local snapid=$(get_snapid)
+ rm $snap/f
+ check_subvol_usage $subvid $(($total_fill + $ext_sz))
+ check_subvol_usage $snapid 0
+ rm $subv/f
+ check_subvol_usage $subvid $total_fill
+ check_subvol_usage $snapid 0
+ cycle_mount_check_subvol_usage $subvid $total_fill
+ check_subvol_usage $snapid 0
+ _scratch_unmount
+}
+
+# Delete the snapshotted subvolume after a snapshot.
+delete_snapshot_src()
+{
+ echo "delete snapshot src first"
+ prepare_snapshotted
+ local subvid=$(get_subvid)
+ local snapid=$(get_snapid)
+ $BTRFS_UTIL_PROG subvolume delete $subv >> $seqres.full
+ check_subvol_usage $subvid $(($total_fill + $ext_sz))
+ check_subvol_usage $snapid 0
+ rm $snap/f
+ trigger_cleaner
+ check_subvol_usage $subvid $total_fill
+ check_subvol_usage $snapid 0
+ $BTRFS_UTIL_PROG subvolume delete $snap >> $seqres.full
+ trigger_cleaner
+ check_subvol_usage $subvid 0
+ check_subvol_usage $snapid 0
+ cycle_mount_check_subvol_usage $subvid 0
+ check_subvol_usage $snapid 0
+ _scratch_unmount
+}
+
+# Delete the snapshot subvolume after a snapshot.
+delete_snapshot()
+{
+ echo "delete snapshot first"
+ prepare_snapshotted
+ local subvid=$(get_subvid)
+ local snapid=$(get_snapid)
+ $BTRFS_UTIL_PROG subvolume delete $snap >> $seqres.full
+ check_subvol_usage $subvid $(($total_fill + $ext_sz))
+ check_subvol_usage $snapid 0
+ $BTRFS_UTIL_PROG subvolume delete $subv >> $seqres.full
+ trigger_cleaner
+ check_subvol_usage $subvid 0
+ check_subvol_usage $snapid 0
+ _scratch_unmount
+}
+
+# Write to a subvolume nested in another subvolume.
+# Exercises the auto-inheritance feature of simple quotas.
+nested_accounting()
+{
+ echo "nested accounting"
+ prepare_nested
+ local subvid=$(get_subvid)
+ local nestedid=$(get_nestedid)
+ rm $subv/f
+ check_subvol_usage $subvid $total_fill
+ check_subvol_usage $nestedid $ext_sz
+ local subv_usage=$(get_subvol_usage $subvid)
+ local nested_usage=$(get_subvol_usage $nestedid)
+ check_qgroup_usage 1/100 $(($subv_usage + $nested_usage))
+ rm $nested/f
+ check_subvol_usage $subvid $total_fill
+ check_subvol_usage $nestedid 0
+ subv_usage=$(get_subvol_usage $subvid)
+ nested_usage=$(get_subvol_usage $nestedid)
+ check_qgroup_usage 1/100 $(($subv_usage + $nested_usage))
+ do_enospc_falloc $nested/large_falloc 2G
+ do_enospc_write $nested/large 2G
+ _scratch_unmount
+}
+
+# Enable simple quotas on a filesystem with existing extents.
+enable_mature()
+{
+ echo "enable mature"
+ _scratch_mkfs >> $seqres.full
+ _scratch_mount
+ $BTRFS_UTIL_PROG subvolume create $subv >> $seqres.full
+ local subvid=$(get_subvid)
+ do_write $subv/f $ext_sz
+ # Sync before enabling squotas to reliably *not* count the writes
+ # we did before enabling.
+ sync
+ enable_quota "s"
+ set_subvol_limit $subvid $limit
+ _scratch_cycle_mount
+ usage=$(get_subvol_usage $subvid)
+ [ $usage -lt $ext_sz ] || \
+ echo "captured usage from before enable $usage >= $ext_sz"
+ do_write $subv/g $ext_sz
+ usage=$(get_subvol_usage $subvid)
+ [ $usage -lt $ext_sz ] && \
+ echo "failed to capture usage after enable $usage < $ext_sz"
+ check_subvol_usage $subvid $ext_sz
+ rm $subv/f
+ check_subvol_usage $subvid $ext_sz
+ _scratch_cycle_mount
+ rm $subv/g
+ check_subvol_usage $subvid 0
+ _scratch_unmount
+}
+
+# Reflink a file within the subvolume.
+reflink_accounting()
+{
+ echo "reflink"
+ prepare
+ local subvid=$(get_subvid)
+ # Do enough reflinks to prove that they're free. If they counted, then
+ # this wouldn't fit in the limit.
+ for i in $(seq $(($limit_nr * 2))); do
+ _cp_reflink $subv/f $subv/f.i
+ done
+ # Confirm that there is no additional data usage from the reflinks.
+ check_subvol_usage $subvid $(($total_fill + $ext_sz))
+ _scratch_unmount
+}
+
+# Delete the src ref of a reflink first.
+delete_reflink_src_ref()
+{
+ echo "delete reflink src ref"
+ prepare
+ local subvid=$(get_subvid)
+ _cp_reflink $subv/f $subv/f.link
+ check_subvol_usage $subvid $(($total_fill + $ext_sz))
+ rm $subv/f
+ check_subvol_usage $subvid $(($total_fill + $ext_sz))
+ rm $subv/f.link
+ check_subvol_usage $subvid $(($total_fill))
+ _scratch_unmount
+}
+
+# Delete the link ref of a reflink first.
+delete_reflink_ref()
+{
+ echo "delete reflink ref"
+ prepare
+ local subvid=$(get_subvid)
+ _cp_reflink $subv/f $subv/f.link
+ check_subvol_usage $subvid $(($total_fill + $ext_sz))
+ rm $subv/f.link
+ check_subvol_usage $subvid $(($total_fill + $ext_sz))
+ rm $subv/f
+ check_subvol_usage $subvid $(($total_fill))
+ _scratch_unmount
+}
+
+basic_accounting
+reservation_accounting
+snapshot_accounting
+delete_snapshot_src_ref
+delete_snapshot_ref
+delete_snapshot_src
+delete_snapshot
+nested_accounting
+enable_mature
+reflink_accounting
+delete_reflink_src_ref
+delete_reflink_ref
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 301
+basic accounting
+fallocate: Disk quota exceeded
+pwrite: Disk quota exceeded
+reservation accounting
+pwrite: Disk quota exceeded
+snapshot accounting
+delete src ref first
+delete snapshot ref first
+delete snapshot src first
+delete snapshot first
+nested accounting
+fallocate: Disk quota exceeded
+pwrite: Disk quota exceeded
+enable mature
+reflink
+delete reflink src ref
+delete reflink ref
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2023 SUSE Linux Products GmbH. All Rights Reserved.
+#
+# FS QA Test 302
+#
+# Test that snapshotting a new subvolume (created in the current transaction)
+# that has a btree with a height > 1, works and does not result in a filesystem
+# corruption.
+#
+# This exercises a regression introduced in kernel 6.5 by the kernel commit:
+#
+# 1b53e51a4a8f ("btrfs: don't commit transaction for every subvol create")
+#
+. ./common/preamble
+_begin_fstest auto quick snapshot subvol
+
+. ./common/filter
+
+_supported_fs btrfs
+_require_scratch
+_require_fssum
+
+_fixed_by_kernel_commit eb96e221937a \
+ "btrfs: fix unwritten extent buffer after snapshotting a new subvolume"
+
+# Use a filesystem with a 64K node size so that we have the same node size on
+# every machine regardless of its page size (on x86_64 default node size is 16K
+# due to the 4K page size, while on PPC it's 64K by default). This way we can
+# make sure we are able to create a btree for the subvolume with a height of 2.
+_scratch_mkfs -n 64K >> $seqres.full 2>&1 || _fail "mkfs failed"
+_scratch_mount
+
+$BTRFS_UTIL_PROG subvolume create $SCRATCH_MNT/subvol | _filter_scratch
+
+# Create a few empty files on the subvolume, this bumps its btree height to 2
+# (root node at level 1 and 2 leaves).
+for ((i = 1; i <= 300; i++)); do
+ echo -n > $SCRATCH_MNT/subvol/file_$i
+done
+
+# Create a checksum of the subvolume's content.
+fssum_file="$SCRATCH_MNT/checksum.fssum"
+$FSSUM_PROG -A -f -w $fssum_file $SCRATCH_MNT/subvol
+
+# Now create a snapshot of the subvolume and make it accessible from within the
+# subvolume.
+$BTRFS_UTIL_PROG subvolume snapshot -r $SCRATCH_MNT/subvol \
+ $SCRATCH_MNT/subvol/snap | _filter_scratch
+
+# Now unmount and mount again the fs. We want to verify we are able to read all
+# metadata for the snapshot from disk (no IO failures, etc).
+_scratch_cycle_mount
+
+# The snapshot's content should match the subvolume's content before we created
+# the snapshot.
+$FSSUM_PROG -r $fssum_file $SCRATCH_MNT/subvol/snap
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 302
+Create subvolume 'SCRATCH_MNT/subvol'
+Create a readonly snapshot of 'SCRATCH_MNT/subvol' in 'SCRATCH_MNT/subvol/snap'
+OK
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2024 SUSE Linux Products GmbH. All Rights Reserved.
+#
+# FS QA Test 303
+#
+# Test that an incremental send does not issue unnecessary writes for a sparse
+# file that got one new extent between its previous extent and the file's size.
+#
+. ./common/preamble
+_begin_fstest auto quick snapshot send fiemap
+
+_cleanup()
+{
+ cd /
+ rm -r -f $tmp.*
+ rm -fr $send_files_dir
+}
+
+. ./common/filter
+. ./common/punch # for _filter_fiemap
+
+_supported_fs btrfs
+_require_test
+_require_scratch
+_require_xfs_io_command "fiemap"
+
+_fixed_by_kernel_commit 5897710b28ca \
+ "btrfs: send: don't issue unnecessary zero writes for trailing hole"
+
+send_files_dir=$TEST_DIR/btrfs-test-$seq
+
+rm -fr $send_files_dir
+mkdir $send_files_dir
+
+_scratch_mkfs >> $seqres.full 2>&1 || _fail "mkfs failed"
+_scratch_mount
+
+$XFS_IO_PROG -f -c "truncate 1G" $SCRATCH_MNT/foobar
+
+# Now create the base snapshot, which is going to be the parent snapshot for
+# a later incremental send.
+$BTRFS_UTIL_PROG subvolume snapshot -r $SCRATCH_MNT \
+ $SCRATCH_MNT/mysnap1 > /dev/null
+
+# Create send stream (full send) for the first snapshot.
+$BTRFS_UTIL_PROG send -f $send_files_dir/1.snap \
+ $SCRATCH_MNT/mysnap1 2>&1 1>/dev/null | _filter_scratch
+
+# Now write one extent at the beginning of the file and one somewhere in the
+# middle, leaving a gap between the end of this second extent and the file's
+# size.
+$XFS_IO_PROG -c "pwrite -S 0xab -b 64K 0 64K" \
+ -c "pwrite -S 0xcd -b 64K 512M 64K" \
+ $SCRATCH_MNT/foobar | _filter_xfs_io
+
+# Now create a second snapshot which is going to be used for an incremental
+# send operation.
+$BTRFS_UTIL_PROG subvolume snapshot -r $SCRATCH_MNT \
+ $SCRATCH_MNT/mysnap2 > /dev/null
+
+# Create send stream (incremental send) for the second snapshot.
+$BTRFS_UTIL_PROG send -p $SCRATCH_MNT/mysnap1 -f $send_files_dir/2.snap \
+ $SCRATCH_MNT/mysnap2 2>&1 1>/dev/null | _filter_scratch
+
+# Now recreate the filesystem by receiving both send streams and verify we get
+# the same content that the original filesystem had and file foobar has only two
+# extents with a size of 64K each.
+_scratch_unmount
+_scratch_mkfs >> $seqres.full 2>&1 || _fail "mkfs failed"
+_scratch_mount
+
+$BTRFS_UTIL_PROG receive -f $send_files_dir/1.snap $SCRATCH_MNT > /dev/null
+$BTRFS_UTIL_PROG receive -f $send_files_dir/2.snap $SCRATCH_MNT > /dev/null
+
+echo "File content in the new filesystem:"
+_hexdump $SCRATCH_MNT/mysnap2/foobar
+
+echo "File fiemap in the new filesystem:"
+# Should have:
+#
+# 64K extent at file range [0, 64K[
+# hole at file range [64K, 512M[
+# 64K extent at file range [512M, 512M + 64K[
+# hole at file range [512M + 64K, 1G[
+$XFS_IO_PROG -r -c "fiemap -v" $SCRATCH_MNT/mysnap2/foobar | _filter_fiemap
+
+# File should be using only 128K of data (two 64K extents).
+echo "Space used by the file: $(du -h $SCRATCH_MNT/mysnap2/foobar | cut -f 1)"
+
+status=0
+exit
--- /dev/null
+QA output created by 303
+At subvol SCRATCH_MNT/mysnap1
+wrote 65536/65536 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 65536/65536 bytes at offset 536870912
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+At subvol SCRATCH_MNT/mysnap2
+At subvol mysnap1
+File content in the new filesystem:
+000000 ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab >................<
+*
+010000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >................<
+*
+20000000 cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd >................<
+*
+20010000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >................<
+*
+40000000
+File fiemap in the new filesystem:
+0: [0..127]: data
+1: [128..1048575]: hole
+2: [1048576..1048703]: data
+3: [1048704..2097151]: hole
+Space used by the file: 128K
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023 Western Digital Cooperation. All Rights Reserved.
+#
+# FS QA Test 304
+#
+# Test on-disk layout of RAID Stripe Tree Metadata writing 4k to a new file on
+# a pristine file system.
+#
+. ./common/preamble
+_begin_fstest auto quick raid remount volume raid-stripe-tree
+
+. ./common/filter
+. ./common/filter.btrfs
+
+_supported_fs btrfs
+_require_btrfs_command inspect-internal dump-tree
+_require_btrfs_mkfs_feature "raid-stripe-tree"
+_require_scratch_dev_pool 4
+_require_btrfs_fs_feature "raid_stripe_tree"
+_require_btrfs_fs_feature "free_space_tree"
+_require_btrfs_free_space_tree
+_require_btrfs_no_compress
+
+test $(_get_page_size) -eq 4096 || _notrun "this tests requires 4k pagesize"
+
+test_4k_write()
+{
+ local profile=$1
+ local ndevs=$2
+
+ _scratch_dev_pool_get $ndevs
+
+ echo "==== Testing $profile ===="
+ _scratch_pool_mkfs -d $profile -m $profile -O raid-stripe-tree
+ _scratch_mount
+
+ $XFS_IO_PROG -fc "pwrite 0 4k" "$SCRATCH_MNT/foo" | _filter_xfs_io
+
+ _scratch_cycle_mount
+ md5sum "$SCRATCH_MNT/foo" | _filter_scratch
+
+ _scratch_unmount
+
+ $BTRFS_UTIL_PROG inspect-internal dump-tree -t raid_stripe $SCRATCH_DEV_POOL |\
+ _filter_stripe_tree
+
+ _scratch_dev_pool_put
+}
+
+echo "= Test basic 4k write ="
+test_4k_write raid0 2
+test_4k_write raid1 2
+test_4k_write raid10 4
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 304
+= Test basic 4k write =
+==== Testing raid0 ====
+wrote 4096/4096 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+5fed275e7617a806f94c173746a2a723 SCRATCH_MNT/foo
+
+raid stripe tree key (RAID_STRIPE_TREE ROOT_ITEM 0)
+leaf XXXXXXXXX items X free space XXXXX generation X owner RAID_STRIPE_TREE
+leaf XXXXXXXXX flags 0x1(WRITTEN) backref revision 1
+checksum stored <CHECKSUM>
+checksum calced <CHECKSUM>
+fs uuid <UUID>
+chunk uuid <UUID>
+ item 0 key (XXXXXX RAID_STRIPE 4096) itemoff XXXXX itemsize 24
+ encoding: RAID0
+ stripe 0 devid 1 physical XXXXXXXXX
+total bytes XXXXXXXX
+bytes used XXXXXX
+uuid <UUID>
+==== Testing raid1 ====
+wrote 4096/4096 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+5fed275e7617a806f94c173746a2a723 SCRATCH_MNT/foo
+
+raid stripe tree key (RAID_STRIPE_TREE ROOT_ITEM 0)
+leaf XXXXXXXXX items X free space XXXXX generation X owner RAID_STRIPE_TREE
+leaf XXXXXXXXX flags 0x1(WRITTEN) backref revision 1
+checksum stored <CHECKSUM>
+checksum calced <CHECKSUM>
+fs uuid <UUID>
+chunk uuid <UUID>
+ item 0 key (XXXXXX RAID_STRIPE 4096) itemoff XXXXX itemsize 40
+ encoding: RAID1
+ stripe 0 devid 1 physical XXXXXXXXX
+ stripe 1 devid 2 physical XXXXXXXXX
+total bytes XXXXXXXX
+bytes used XXXXXX
+uuid <UUID>
+==== Testing raid10 ====
+wrote 4096/4096 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+5fed275e7617a806f94c173746a2a723 SCRATCH_MNT/foo
+
+raid stripe tree key (RAID_STRIPE_TREE ROOT_ITEM 0)
+leaf XXXXXXXXX items X free space XXXXX generation X owner RAID_STRIPE_TREE
+leaf XXXXXXXXX flags 0x1(WRITTEN) backref revision 1
+checksum stored <CHECKSUM>
+checksum calced <CHECKSUM>
+fs uuid <UUID>
+chunk uuid <UUID>
+ item 0 key (XXXXXX RAID_STRIPE 4096) itemoff XXXXX itemsize 40
+ encoding: RAID10
+ stripe 0 devid 1 physical XXXXXXXXX
+ stripe 1 devid 2 physical XXXXXXXXX
+total bytes XXXXXXXX
+bytes used XXXXXX
+uuid <UUID>
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023 Western Digital Cooperation. All Rights Reserved.
+#
+# FS QA Test 305
+#
+# Test on-disk layout of RAID Stripe Tree Metadata by writing 8k to a new file
+# with a filesystem prepropulated, so that 4k of the write are written to the
+# 1st stripe and 4k start a new stripe.
+#
+. ./common/preamble
+_begin_fstest auto quick raid remount volume raid-stripe-tree
+
+. ./common/filter
+. ./common/filter.btrfs
+
+_supported_fs btrfs
+_require_btrfs_command inspect-internal dump-tree
+_require_btrfs_mkfs_feature "raid-stripe-tree"
+_require_scratch_dev_pool 4
+_require_btrfs_fs_feature "raid_stripe_tree"
+_require_btrfs_fs_feature "free_space_tree"
+_require_btrfs_free_space_tree
+_require_btrfs_no_compress
+
+test $(_get_page_size) -eq 4096 || _notrun "this tests requires 4k pagesize"
+
+test_8k_new_stripe()
+{
+ local profile=$1
+ local ndevs=$2
+
+ _scratch_dev_pool_get $ndevs
+
+ echo "==== Testing $profile ===="
+ _scratch_pool_mkfs -d $profile -m $profile -O raid-stripe-tree
+ _scratch_mount
+
+ # Fill the first stripe up to 64k - 4k
+ $XFS_IO_PROG -fc "pwrite 0 60k" -c fsync "$SCRATCH_MNT/bar" | _filter_xfs_io
+
+ # The actual 8k write
+ $XFS_IO_PROG -fc "pwrite 0 8k" "$SCRATCH_MNT/foo" | _filter_xfs_io
+
+ _scratch_cycle_mount
+ md5sum "$SCRATCH_MNT/foo" | _filter_scratch
+
+ _scratch_unmount
+
+ $BTRFS_UTIL_PROG inspect-internal dump-tree -t raid_stripe $SCRATCH_DEV_POOL |\
+ _filter_stripe_tree
+
+ _scratch_dev_pool_put
+}
+
+echo "= Test 8k write to a new file so that 4k start a new stripe ="
+test_8k_new_stripe raid0 2
+test_8k_new_stripe raid1 2
+test_8k_new_stripe raid10 4
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 305
+= Test 8k write to a new file so that 4k start a new stripe =
+==== Testing raid0 ====
+wrote 61440/61440 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 8192/8192 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+9d3940adb41dd525e008a847e01b15f4 SCRATCH_MNT/foo
+
+raid stripe tree key (RAID_STRIPE_TREE ROOT_ITEM 0)
+leaf XXXXXXXXX items X free space XXXXX generation X owner RAID_STRIPE_TREE
+leaf XXXXXXXXX flags 0x1(WRITTEN) backref revision 1
+checksum stored <CHECKSUM>
+checksum calced <CHECKSUM>
+fs uuid <UUID>
+chunk uuid <UUID>
+ item 0 key (XXXXXX RAID_STRIPE 61440) itemoff XXXXX itemsize 24
+ encoding: RAID0
+ stripe 0 devid 1 physical XXXXXXXXX
+ item 1 key (XXXXXX RAID_STRIPE 4096) itemoff XXXXX itemsize 24
+ encoding: RAID0
+ stripe 0 devid 1 physical XXXXXXXXX
+ item 2 key (XXXXXX RAID_STRIPE 4096) itemoff XXXXX itemsize 24
+ encoding: RAID0
+ stripe 0 devid 2 physical XXXXXXXXX
+total bytes XXXXXXXX
+bytes used XXXXXX
+uuid <UUID>
+==== Testing raid1 ====
+wrote 61440/61440 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 8192/8192 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+9d3940adb41dd525e008a847e01b15f4 SCRATCH_MNT/foo
+
+raid stripe tree key (RAID_STRIPE_TREE ROOT_ITEM 0)
+leaf XXXXXXXXX items X free space XXXXX generation X owner RAID_STRIPE_TREE
+leaf XXXXXXXXX flags 0x1(WRITTEN) backref revision 1
+checksum stored <CHECKSUM>
+checksum calced <CHECKSUM>
+fs uuid <UUID>
+chunk uuid <UUID>
+ item 0 key (XXXXXX RAID_STRIPE 61440) itemoff XXXXX itemsize 40
+ encoding: RAID1
+ stripe 0 devid 1 physical XXXXXXXXX
+ stripe 1 devid 2 physical XXXXXXXXX
+ item 1 key (XXXXXX RAID_STRIPE 8192) itemoff XXXXX itemsize 40
+ encoding: RAID1
+ stripe 0 devid 1 physical XXXXXXXXX
+ stripe 1 devid 2 physical XXXXXXXXX
+total bytes XXXXXXXX
+bytes used XXXXXX
+uuid <UUID>
+==== Testing raid10 ====
+wrote 61440/61440 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 8192/8192 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+9d3940adb41dd525e008a847e01b15f4 SCRATCH_MNT/foo
+
+raid stripe tree key (RAID_STRIPE_TREE ROOT_ITEM 0)
+leaf XXXXXXXXX items X free space XXXXX generation X owner RAID_STRIPE_TREE
+leaf XXXXXXXXX flags 0x1(WRITTEN) backref revision 1
+checksum stored <CHECKSUM>
+checksum calced <CHECKSUM>
+fs uuid <UUID>
+chunk uuid <UUID>
+ item 0 key (XXXXXX RAID_STRIPE 61440) itemoff XXXXX itemsize 40
+ encoding: RAID10
+ stripe 0 devid 1 physical XXXXXXXXX
+ stripe 1 devid 2 physical XXXXXXXXX
+ item 1 key (XXXXXX RAID_STRIPE 4096) itemoff XXXXX itemsize 40
+ encoding: RAID10
+ stripe 0 devid 1 physical XXXXXXXXX
+ stripe 1 devid 2 physical XXXXXXXXX
+ item 2 key (XXXXXX RAID_STRIPE 4096) itemoff XXXXX itemsize 40
+ encoding: RAID10
+ stripe 0 devid 3 physical XXXXXXXXX
+ stripe 1 devid 4 physical XXXXXXXXX
+total bytes XXXXXXXX
+bytes used XXXXXX
+uuid <UUID>
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023 Western Digital Cooperation. All Rights Reserved.
+#
+# FS QA Test 306
+#
+# Test on-disk layout of RAID Stripe Tree Metadata by writing 4k to an emppty
+# file at offset 64k with one stripe pre-filled on an otherwise pristine
+# filesystem.
+#
+. ./common/preamble
+_begin_fstest auto quick raid remount volume raid-stripe-tree
+
+. ./common/filter
+. ./common/filter.btrfs
+
+_supported_fs btrfs
+_require_btrfs_command inspect-internal dump-tree
+_require_btrfs_mkfs_feature "raid-stripe-tree"
+_require_scratch_dev_pool 4
+_require_btrfs_fs_feature "raid_stripe_tree"
+_require_btrfs_fs_feature "free_space_tree"
+_require_btrfs_free_space_tree
+_require_btrfs_no_compress
+
+test $(_get_page_size) -eq 4096 || _notrun "this tests requires 4k pagesize"
+
+test_4k_write_64koff()
+{
+ local profile=$1
+ local ndevs=$2
+
+ _scratch_dev_pool_get $ndevs
+
+ echo "==== Testing $profile ===="
+ _scratch_pool_mkfs -d $profile -m $profile -O raid-stripe-tree
+ _scratch_mount
+
+ # precondition one stripe
+ $XFS_IO_PROG -fc "pwrite 0 64k" "$SCRATCH_MNT/bar" | _filter_xfs_io
+
+ $XFS_IO_PROG -fc "pwrite 64k 4k" "$SCRATCH_MNT/foo" | _filter_xfs_io
+
+ _scratch_cycle_mount
+ md5sum "$SCRATCH_MNT/foo" | _filter_scratch
+
+ _scratch_unmount
+
+ $BTRFS_UTIL_PROG inspect-internal dump-tree -t raid_stripe $SCRATCH_DEV_POOL |\
+ _filter_stripe_tree
+
+ _scratch_dev_pool_put
+}
+echo "= Test 4k write to an empty file at offset 64k with one stripe prefilled ="
+test_4k_write_64koff raid0 2
+test_4k_write_64koff raid1 2
+test_4k_write_64koff raid10 4
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 306
+= Test 4k write to an empty file at offset 64k with one stripe prefilled =
+==== Testing raid0 ====
+wrote 65536/65536 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 4096/4096 bytes at offset 65536
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+381b0e7d72cb4f75286fe2b445e8d92a SCRATCH_MNT/foo
+
+raid stripe tree key (RAID_STRIPE_TREE ROOT_ITEM 0)
+leaf XXXXXXXXX items X free space XXXXX generation X owner RAID_STRIPE_TREE
+leaf XXXXXXXXX flags 0x1(WRITTEN) backref revision 1
+checksum stored <CHECKSUM>
+checksum calced <CHECKSUM>
+fs uuid <UUID>
+chunk uuid <UUID>
+ item 0 key (XXXXXX RAID_STRIPE 65536) itemoff XXXXX itemsize 24
+ encoding: RAID0
+ stripe 0 devid 1 physical XXXXXXXXX
+ item 1 key (XXXXXX RAID_STRIPE 4096) itemoff XXXXX itemsize 24
+ encoding: RAID0
+ stripe 0 devid 2 physical XXXXXXXXX
+total bytes XXXXXXXX
+bytes used XXXXXX
+uuid <UUID>
+==== Testing raid1 ====
+wrote 65536/65536 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 4096/4096 bytes at offset 65536
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+381b0e7d72cb4f75286fe2b445e8d92a SCRATCH_MNT/foo
+
+raid stripe tree key (RAID_STRIPE_TREE ROOT_ITEM 0)
+leaf XXXXXXXXX items X free space XXXXX generation X owner RAID_STRIPE_TREE
+leaf XXXXXXXXX flags 0x1(WRITTEN) backref revision 1
+checksum stored <CHECKSUM>
+checksum calced <CHECKSUM>
+fs uuid <UUID>
+chunk uuid <UUID>
+ item 0 key (XXXXXX RAID_STRIPE 65536) itemoff XXXXX itemsize 40
+ encoding: RAID1
+ stripe 0 devid 1 physical XXXXXXXXX
+ stripe 1 devid 2 physical XXXXXXXXX
+ item 1 key (XXXXXX RAID_STRIPE 4096) itemoff XXXXX itemsize 40
+ encoding: RAID1
+ stripe 0 devid 1 physical XXXXXXXXX
+ stripe 1 devid 2 physical XXXXXXXXX
+total bytes XXXXXXXX
+bytes used XXXXXX
+uuid <UUID>
+==== Testing raid10 ====
+wrote 65536/65536 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 4096/4096 bytes at offset 65536
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+381b0e7d72cb4f75286fe2b445e8d92a SCRATCH_MNT/foo
+
+raid stripe tree key (RAID_STRIPE_TREE ROOT_ITEM 0)
+leaf XXXXXXXXX items X free space XXXXX generation X owner RAID_STRIPE_TREE
+leaf XXXXXXXXX flags 0x1(WRITTEN) backref revision 1
+checksum stored <CHECKSUM>
+checksum calced <CHECKSUM>
+fs uuid <UUID>
+chunk uuid <UUID>
+ item 0 key (XXXXXX RAID_STRIPE 65536) itemoff XXXXX itemsize 40
+ encoding: RAID10
+ stripe 0 devid 1 physical XXXXXXXXX
+ stripe 1 devid 2 physical XXXXXXXXX
+ item 1 key (XXXXXX RAID_STRIPE 4096) itemoff XXXXX itemsize 40
+ encoding: RAID10
+ stripe 0 devid 3 physical XXXXXXXXX
+ stripe 1 devid 4 physical XXXXXXXXX
+total bytes XXXXXXXX
+bytes used XXXXXX
+uuid <UUID>
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023 Western Digital Cooperation. All Rights Reserved.
+#
+# FS QA Test 307
+#
+# Test on-disk layout of RAID Stripe Tree Metadata by writing 128k to a new
+# file on a pristine filesystem
+#
+. ./common/preamble
+_begin_fstest auto quick raid remount volume raid-stripe-tree
+
+. ./common/filter
+. ./common/filter.btrfs
+
+_supported_fs btrfs
+_require_btrfs_command inspect-internal dump-tree
+_require_btrfs_mkfs_feature "raid-stripe-tree"
+_require_scratch_dev_pool 4
+_require_btrfs_fs_feature "raid_stripe_tree"
+_require_btrfs_fs_feature "free_space_tree"
+_require_btrfs_free_space_tree
+_require_btrfs_no_compress
+
+test $(_get_page_size) -eq 4096 || _notrun "this tests requires 4k pagesize"
+
+test_128k_write()
+{
+ local profile=$1
+ local ndevs=$2
+
+ _scratch_dev_pool_get $ndevs
+
+ echo "==== Testing $profile ===="
+ _scratch_pool_mkfs -d $profile -m $profile -O raid-stripe-tree
+ _scratch_mount
+
+ $XFS_IO_PROG -fc "pwrite 0 128k" "$SCRATCH_MNT/foo" | _filter_xfs_io
+
+ _scratch_cycle_mount
+ md5sum "$SCRATCH_MNT/foo" | _filter_scratch
+
+ _scratch_unmount
+
+ $BTRFS_UTIL_PROG inspect-internal dump-tree -t raid_stripe $SCRATCH_DEV_POOL |\
+ _filter_stripe_tree
+
+ _scratch_dev_pool_put
+}
+
+echo "= Test 128k write to empty file ="
+test_128k_write raid0 2
+test_128k_write raid1 2
+test_128k_write raid10 4
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 307
+= Test 128k write to empty file =
+==== Testing raid0 ====
+wrote 131072/131072 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+d48858312a922db7eb86377f638dbc9f SCRATCH_MNT/foo
+
+raid stripe tree key (RAID_STRIPE_TREE ROOT_ITEM 0)
+leaf XXXXXXXXX items X free space XXXXX generation X owner RAID_STRIPE_TREE
+leaf XXXXXXXXX flags 0x1(WRITTEN) backref revision 1
+checksum stored <CHECKSUM>
+checksum calced <CHECKSUM>
+fs uuid <UUID>
+chunk uuid <UUID>
+ item 0 key (XXXXXX RAID_STRIPE 65536) itemoff XXXXX itemsize 24
+ encoding: RAID0
+ stripe 0 devid 1 physical XXXXXXXXX
+ item 1 key (XXXXXX RAID_STRIPE 65536) itemoff XXXXX itemsize 24
+ encoding: RAID0
+ stripe 0 devid 2 physical XXXXXXXXX
+total bytes XXXXXXXX
+bytes used XXXXXX
+uuid <UUID>
+==== Testing raid1 ====
+wrote 131072/131072 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+d48858312a922db7eb86377f638dbc9f SCRATCH_MNT/foo
+
+raid stripe tree key (RAID_STRIPE_TREE ROOT_ITEM 0)
+leaf XXXXXXXXX items X free space XXXXX generation X owner RAID_STRIPE_TREE
+leaf XXXXXXXXX flags 0x1(WRITTEN) backref revision 1
+checksum stored <CHECKSUM>
+checksum calced <CHECKSUM>
+fs uuid <UUID>
+chunk uuid <UUID>
+ item 0 key (XXXXXX RAID_STRIPE 131072) itemoff XXXXX itemsize 40
+ encoding: RAID1
+ stripe 0 devid 1 physical XXXXXXXXX
+ stripe 1 devid 2 physical XXXXXXXXX
+total bytes XXXXXXXX
+bytes used XXXXXX
+uuid <UUID>
+==== Testing raid10 ====
+wrote 131072/131072 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+d48858312a922db7eb86377f638dbc9f SCRATCH_MNT/foo
+
+raid stripe tree key (RAID_STRIPE_TREE ROOT_ITEM 0)
+leaf XXXXXXXXX items X free space XXXXX generation X owner RAID_STRIPE_TREE
+leaf XXXXXXXXX flags 0x1(WRITTEN) backref revision 1
+checksum stored <CHECKSUM>
+checksum calced <CHECKSUM>
+fs uuid <UUID>
+chunk uuid <UUID>
+ item 0 key (XXXXXX RAID_STRIPE 65536) itemoff XXXXX itemsize 40
+ encoding: RAID10
+ stripe 0 devid 1 physical XXXXXXXXX
+ stripe 1 devid 2 physical XXXXXXXXX
+ item 1 key (XXXXXX RAID_STRIPE 65536) itemoff XXXXX itemsize 40
+ encoding: RAID10
+ stripe 0 devid 3 physical XXXXXXXXX
+ stripe 1 devid 4 physical XXXXXXXXX
+total bytes XXXXXXXX
+bytes used XXXXXX
+uuid <UUID>
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023 Western Digital Cooperation. All Rights Reserved.
+#
+# FS QA Test 308
+#
+# Test on-disk layout of RAID Stripe Tree Metadata by writing 128k to an empty
+# file on a filesystem that has one stripe already pre-filled. Afterwards
+# overwrite a portion of the file.
+#
+. ./common/preamble
+_begin_fstest auto quick raid remount volume raid-stripe-tree
+
+. ./common/filter
+. ./common/filter.btrfs
+
+_supported_fs btrfs
+_require_btrfs_command inspect-internal dump-tree
+_require_btrfs_mkfs_feature "raid-stripe-tree"
+_require_scratch_dev_pool 4
+_require_btrfs_fs_feature "raid_stripe_tree"
+_require_btrfs_fs_feature "free_space_tree"
+_require_btrfs_free_space_tree
+_require_btrfs_no_compress
+_require_btrfs_no_nodatacow
+
+test $(_get_page_size) -eq 4096 || _notrun "this tests requires 4k pagesize"
+
+test_128k_write_overwrite()
+{
+ local profile=$1
+ local ndevs=$2
+
+ _scratch_dev_pool_get $ndevs
+
+ echo "==== Testing $profile ===="
+ _scratch_pool_mkfs -d $profile -m $profile -O raid-stripe-tree
+ _scratch_mount
+
+ $XFS_IO_PROG -fc "pwrite -W 0 32k" "$SCRATCH_MNT/bar" | _filter_xfs_io
+ $XFS_IO_PROG -fc "pwrite -W 0 128k" "$SCRATCH_MNT/foo" | _filter_xfs_io
+ $XFS_IO_PROG -fc "pwrite -W 64k 8k" "$SCRATCH_MNT/foo" | _filter_xfs_io
+
+ _scratch_cycle_mount
+ md5sum "$SCRATCH_MNT/foo" | _filter_scratch
+
+ _scratch_unmount
+
+ $BTRFS_UTIL_PROG inspect-internal dump-tree -t raid_stripe $SCRATCH_DEV_POOL |\
+ _filter_stripe_tree
+
+ _scratch_dev_pool_put
+}
+
+echo "= Test 128k write to empty file with 1st stripe partially prefilled then overwrite ="
+test_128k_write_overwrite raid0 2
+test_128k_write_overwrite raid1 2
+test_128k_write_overwrite raid10 4
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 308
+= Test 128k write to empty file with 1st stripe partially prefilled then overwrite =
+==== Testing raid0 ====
+wrote 32768/32768 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 131072/131072 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 8192/8192 bytes at offset 65536
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+d48858312a922db7eb86377f638dbc9f SCRATCH_MNT/foo
+
+raid stripe tree key (RAID_STRIPE_TREE ROOT_ITEM 0)
+leaf XXXXXXXXX items X free space XXXXX generation X owner RAID_STRIPE_TREE
+leaf XXXXXXXXX flags 0x1(WRITTEN) backref revision 1
+checksum stored <CHECKSUM>
+checksum calced <CHECKSUM>
+fs uuid <UUID>
+chunk uuid <UUID>
+ item 0 key (XXXXXX RAID_STRIPE 32768) itemoff XXXXX itemsize 24
+ encoding: RAID0
+ stripe 0 devid 1 physical XXXXXXXXX
+ item 1 key (XXXXXX RAID_STRIPE 32768) itemoff XXXXX itemsize 24
+ encoding: RAID0
+ stripe 0 devid 1 physical XXXXXXXXX
+ item 2 key (XXXXXX RAID_STRIPE 65536) itemoff XXXXX itemsize 24
+ encoding: RAID0
+ stripe 0 devid 2 physical XXXXXXXXX
+ item 3 key (XXXXXX RAID_STRIPE 32768) itemoff XXXXX itemsize 24
+ encoding: RAID0
+ stripe 0 devid 1 physical XXXXXXXXX
+ item 4 key (XXXXXX RAID_STRIPE 8192) itemoff XXXXX itemsize 24
+ encoding: RAID0
+ stripe 0 devid 1 physical XXXXXXXXX
+total bytes XXXXXXXX
+bytes used XXXXXX
+uuid <UUID>
+==== Testing raid1 ====
+wrote 32768/32768 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 131072/131072 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 8192/8192 bytes at offset 65536
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+d48858312a922db7eb86377f638dbc9f SCRATCH_MNT/foo
+
+raid stripe tree key (RAID_STRIPE_TREE ROOT_ITEM 0)
+leaf XXXXXXXXX items X free space XXXXX generation X owner RAID_STRIPE_TREE
+leaf XXXXXXXXX flags 0x1(WRITTEN) backref revision 1
+checksum stored <CHECKSUM>
+checksum calced <CHECKSUM>
+fs uuid <UUID>
+chunk uuid <UUID>
+ item 0 key (XXXXXX RAID_STRIPE 32768) itemoff XXXXX itemsize 40
+ encoding: RAID1
+ stripe 0 devid 1 physical XXXXXXXXX
+ stripe 1 devid 2 physical XXXXXXXXX
+ item 1 key (XXXXXX RAID_STRIPE 131072) itemoff XXXXX itemsize 40
+ encoding: RAID1
+ stripe 0 devid 1 physical XXXXXXXXX
+ stripe 1 devid 2 physical XXXXXXXXX
+ item 2 key (XXXXXX RAID_STRIPE 8192) itemoff XXXXX itemsize 40
+ encoding: RAID1
+ stripe 0 devid 1 physical XXXXXXXXX
+ stripe 1 devid 2 physical XXXXXXXXX
+total bytes XXXXXXXX
+bytes used XXXXXX
+uuid <UUID>
+==== Testing raid10 ====
+wrote 32768/32768 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 131072/131072 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 8192/8192 bytes at offset 65536
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+d48858312a922db7eb86377f638dbc9f SCRATCH_MNT/foo
+
+raid stripe tree key (RAID_STRIPE_TREE ROOT_ITEM 0)
+leaf XXXXXXXXX items X free space XXXXX generation X owner RAID_STRIPE_TREE
+leaf XXXXXXXXX flags 0x1(WRITTEN) backref revision 1
+checksum stored <CHECKSUM>
+checksum calced <CHECKSUM>
+fs uuid <UUID>
+chunk uuid <UUID>
+ item 0 key (XXXXXX RAID_STRIPE 32768) itemoff XXXXX itemsize 40
+ encoding: RAID10
+ stripe 0 devid 1 physical XXXXXXXXX
+ stripe 1 devid 2 physical XXXXXXXXX
+ item 1 key (XXXXXX RAID_STRIPE 32768) itemoff XXXXX itemsize 40
+ encoding: RAID10
+ stripe 0 devid 1 physical XXXXXXXXX
+ stripe 1 devid 2 physical XXXXXXXXX
+ item 2 key (XXXXXX RAID_STRIPE 65536) itemoff XXXXX itemsize 40
+ encoding: RAID10
+ stripe 0 devid 3 physical XXXXXXXXX
+ stripe 1 devid 4 physical XXXXXXXXX
+ item 3 key (XXXXXX RAID_STRIPE 32768) itemoff XXXXX itemsize 40
+ encoding: RAID10
+ stripe 0 devid 1 physical XXXXXXXXX
+ stripe 1 devid 2 physical XXXXXXXXX
+ item 4 key (XXXXXX RAID_STRIPE 8192) itemoff XXXXX itemsize 40
+ encoding: RAID10
+ stripe 0 devid 1 physical XXXXXXXXX
+ stripe 1 devid 2 physical XXXXXXXXX
+total bytes XXXXXXXX
+bytes used XXXXXX
+uuid <UUID>
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) Meta Platforms, Inc. and affiliates.
+#
+# FS QA Test 309
+#
+# Try to snapshot a deleted subvolume.
+#
+. ./common/preamble
+_begin_fstest auto quick snapshot subvol
+
+_supported_fs btrfs
+_require_scratch
+_require_test_program t_snapshot_deleted_subvolume
+_fixed_by_kernel_commit 7081929ab257 \
+ "btrfs: don't abort filesystem when attempting to snapshot deleted subvolume"
+
+_scratch_mkfs >> $seqres.full 2>&1 || _fail "mkfs failed"
+_scratch_mount
+
+"$here/src/t_snapshot_deleted_subvolume" "$SCRATCH_MNT"
+# Make sure the filesystem didn't go read-only.
+touch "$SCRATCH_MNT/foo"
+
+echo "Silence is golden"
+status=0
+exit
--- /dev/null
+QA output created by 309
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2024 SUSE Linux Products GmbH. All Rights Reserved.
+#
+# FS QA Test 310
+#
+# Make sure reading on an compressed inline extent is behaving correctly
+#
+. ./common/preamble
+_begin_fstest auto quick compress
+
+# Import common functions.
+# . ./common/filter
+
+# real QA test starts here
+
+# Modify as appropriate.
+_supported_fs btrfs
+_require_scratch
+
+# This test require inlined compressed extents creation, and all the writes
+# are designed for 4K sector size.
+_require_btrfs_inline_extents_creation
+_require_btrfs_support_sectorsize 4096
+
+_fixed_by_kernel_commit e01a83e12604 \
+ "Revert \"btrfs: zstd: fix and simplify the inline extent decompression\""
+
+# The correct md5 for the correct 4K file filled with "0xcd"
+md5sum_correct="5fed275e7617a806f94c173746a2a723"
+
+workload()
+{
+ local algo="$1"
+
+ echo "=== Testing compression algorithm ${algo} ===" >> $seqres.full
+ _scratch_mkfs >> $seqres.full
+ _scratch_mount -o compress=${algo}
+
+ _pwrite_byte 0xcd 0 4k "$SCRATCH_MNT/inline_file" > /dev/null
+ result=$(_md5_checksum "$SCRATCH_MNT/inline_file")
+ echo "after initial write, md5sum=${result}" >> $seqres.full
+ if [ "$result" != "$md5sum_correct" ]; then
+ _fail "initial write results incorrect content for \"$algo\""
+ fi
+ # Writeback data to get correct fiemap result, or we got FIEMAP_DEALLOC
+ # without compression/inline flags.
+ sync
+
+ $XFS_IO_PROG -c "fiemap -v" $SCRATCH_MNT/inline_file | tail -n 1 > $tmp.fiemap
+ cat $tmp.fiemap >> $seqres.full
+ # Make sure we got an inlined compressed file extent.
+ # 0x200 means inlined, 0x100 means not block aligned, 0x8 means encoded
+ # (compressed in this case), and 0x1 means the last extent.
+ if ! grep -q "0x309" $tmp.fiemap; then
+ rm -f -- $tmp.fiemap
+ _notrun "No compressed inline extent created, maybe subpage?"
+ fi
+ rm -f -- $tmp.fiemap
+
+ # Unmount to clear the page cache.
+ _scratch_cycle_mount
+
+ # For v6.8-rc1 without the revert or the newer fix, this would
+ # lead to VM_BUG_ON() thus crash
+ result=$(_md5_checksum "$SCRATCH_MNT/inline_file")
+ echo "after cycle mount, md5sum=${result}" >> $seqres.full
+ if [ "$result" != "$md5sum_correct" ]; then
+ _fail "read for compressed inline extent failed for \"$algo\""
+ fi
+ _scratch_unmount
+ _check_scratch_fs
+}
+
+algo_list=($(_btrfs_compression_algos))
+for algo in ${algo_list[@]}; do
+ workload $algo
+done
+
+echo "Silence is golden"
+
+status=0
+exit
--- /dev/null
+QA output created by 310
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2024 Oracle. All Rights Reserved.
+#
+# FS QA Test 311
+#
+# Mount the device twice check if the reflink works, this helps to
+# ensure device is mounted as the same device.
+#
+. ./common/preamble
+_begin_fstest auto quick subvol tempfsid
+
+# Override the default cleanup function.
+_cleanup()
+{
+ cd /
+ $UMOUNT_PROG $mnt1 > /dev/null 2>&1
+ rm -r -f $tmp.*
+ rm -r -f $mnt1
+}
+
+. ./common/filter.btrfs
+. ./common/reflink
+
+# Modify as appropriate.
+_supported_fs btrfs
+_require_cp_reflink
+_require_scratch
+_require_btrfs_fs_feature temp_fsid
+
+mnt1=$TEST_DIR/$seq/mnt1
+mkdir -p $mnt1
+
+same_dev_mount()
+{
+ echo ---- $FUNCNAME ----
+
+ _scratch_mkfs >> $seqres.full 2>&1
+
+ _scratch_mount
+ $XFS_IO_PROG -fc 'pwrite -S 0x61 0 9000' $SCRATCH_MNT/foo | \
+ _filter_xfs_io
+
+ echo Mount the device again to a different mount point
+ _mount $SCRATCH_DEV $mnt1
+
+ _cp_reflink $SCRATCH_MNT/foo $mnt1/bar
+ echo Checksum of reflinked files
+ md5sum $SCRATCH_MNT/foo | _filter_scratch
+ md5sum $mnt1/bar | _filter_test_dir
+
+ check_fsid $SCRATCH_DEV
+}
+
+same_dev_subvol_mount()
+{
+ echo ---- $FUNCNAME ----
+ _scratch_mkfs >> $seqres.full 2>&1
+
+ _scratch_mount
+ $BTRFS_UTIL_PROG subvolume create $SCRATCH_MNT/subvol | _filter_scratch
+
+ $XFS_IO_PROG -fc 'pwrite -S 0x61 0 9000' $SCRATCH_MNT/subvol/foo | \
+ _filter_xfs_io
+
+ echo Mounting a subvol
+ _mount -o subvol=subvol $SCRATCH_DEV $mnt1
+
+ _cp_reflink $SCRATCH_MNT/subvol/foo $mnt1/bar
+ echo Checksum of reflinked files
+ md5sum $SCRATCH_MNT/subvol/foo | _filter_scratch
+ md5sum $mnt1/bar | _filter_test_dir
+
+ check_fsid $SCRATCH_DEV
+}
+
+same_dev_mount
+
+_scratch_unmount
+_cleanup
+mkdir -p $mnt1
+
+same_dev_subvol_mount
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 311
+---- same_dev_mount ----
+wrote 9000/9000 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Mount the device again to a different mount point
+Checksum of reflinked files
+42d69d1a6d333a7ebdf64792a555e392 SCRATCH_MNT/foo
+42d69d1a6d333a7ebdf64792a555e392 TEST_DIR/311/mnt1/bar
+On disk fsid: FSID
+Metadata uuid: FSID
+Temp fsid: FSID
+Tempfsid status: 0
+---- same_dev_subvol_mount ----
+Create subvolume 'SCRATCH_MNT/subvol'
+wrote 9000/9000 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Mounting a subvol
+Checksum of reflinked files
+42d69d1a6d333a7ebdf64792a555e392 SCRATCH_MNT/subvol/foo
+42d69d1a6d333a7ebdf64792a555e392 TEST_DIR/311/mnt1/bar
+On disk fsid: FSID
+Metadata uuid: FSID
+Temp fsid: FSID
+Tempfsid status: 0
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2024 Oracle. All Rights Reserved.
+#
+# FS QA Test 312
+#
+# On a clone a device check to see if tempfsid is activated.
+#
+. ./common/preamble
+_begin_fstest auto quick clone tempfsid
+
+_cleanup()
+{
+ cd /
+ $UMOUNT_PROG $mnt1 > /dev/null 2>&1
+ rm -r -f $tmp.*
+ rm -r -f $mnt1
+}
+
+. ./common/filter.btrfs
+. ./common/reflink
+
+_supported_fs btrfs
+_require_scratch_dev_pool 2
+_scratch_dev_pool_get 2
+_require_btrfs_fs_feature temp_fsid
+
+mnt1=$TEST_DIR/$seq/mnt1
+mkdir -p $mnt1
+
+create_cloned_devices()
+{
+ local dev1=$1
+ local dev2=$2
+
+ echo -n Creating cloned device...
+ _mkfs_dev -fq -b $((1024 * 1024 * 300)) $dev1
+
+ _mount $dev1 $SCRATCH_MNT
+
+ $XFS_IO_PROG -fc 'pwrite -S 0x61 0 9000' $SCRATCH_MNT/foo | \
+ _filter_xfs_io
+ $UMOUNT_PROG $SCRATCH_MNT
+ # device dump of $dev1 to $dev2
+ dd if=$dev1 of=$dev2 bs=300M count=1 conv=fsync status=none || \
+ _fail "dd failed: $?"
+ echo done
+}
+
+mount_cloned_device()
+{
+ echo ---- $FUNCNAME ----
+ create_cloned_devices ${SCRATCH_DEV_NAME[0]} ${SCRATCH_DEV_NAME[1]}
+
+ echo Mounting original device
+ _mount ${SCRATCH_DEV_NAME[0]} $SCRATCH_MNT
+ $XFS_IO_PROG -fc 'pwrite -S 0x61 0 9000' $SCRATCH_MNT/foo | \
+ _filter_xfs_io
+ check_fsid ${SCRATCH_DEV_NAME[0]}
+
+ echo Mounting cloned device
+ _mount ${SCRATCH_DEV_NAME[1]} $mnt1 || \
+ _fail "mount failed, tempfsid didn't work"
+
+ echo cp reflink must fail
+ _cp_reflink $SCRATCH_MNT/foo $mnt1/bar 2>&1 | \
+ _filter_testdir_and_scratch
+
+ check_fsid ${SCRATCH_DEV_NAME[1]}
+}
+
+mount_cloned_device
+
+_scratch_dev_pool_put
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 312
+---- mount_cloned_device ----
+Creating cloned device...wrote 9000/9000 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+done
+Mounting original device
+wrote 9000/9000 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+On disk fsid: FSID
+Metadata uuid: FSID
+Temp fsid: FSID
+Tempfsid status: 0
+Mounting cloned device
+cp reflink must fail
+cp: failed to clone 'TEST_DIR/312/mnt1/bar' from 'SCRATCH_MNT/foo': Invalid cross-device link
+On disk fsid: FSID
+Metadata uuid: FSID
+Temp fsid: TEMPFSID
+Tempfsid status: 1
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2024 Oracle. All Rights Reserved.
+#
+# FS QA Test 313
+#
+# Functional test for the tempfsid, clone devices created using the mkfs option.
+#
+. ./common/preamble
+_begin_fstest auto quick clone tempfsid
+
+_cleanup()
+{
+ cd /
+ $UMOUNT_PROG $mnt1 > /dev/null 2>&1
+ rm -r -f $tmp.*
+ rm -r -f $mnt1
+}
+
+. ./common/filter.btrfs
+. ./common/reflink
+
+_supported_fs btrfs
+_require_cp_reflink
+_require_scratch_dev_pool 2
+_require_btrfs_fs_feature temp_fsid
+
+_scratch_dev_pool_get 2
+
+mnt1=$TEST_DIR/$seq/mnt1
+mkdir -p $mnt1
+
+echo ---- clone_uuids_verify_tempfsid ----
+mkfs_clone ${SCRATCH_DEV_NAME[0]} ${SCRATCH_DEV_NAME[1]}
+
+echo Mounting original device
+_mount ${SCRATCH_DEV_NAME[0]} $SCRATCH_MNT
+check_fsid ${SCRATCH_DEV_NAME[0]}
+
+echo Mounting cloned device
+_mount ${SCRATCH_DEV_NAME[1]} $mnt1
+check_fsid ${SCRATCH_DEV_NAME[1]}
+
+$XFS_IO_PROG -fc 'pwrite -S 0x61 0 9000' $SCRATCH_MNT/foo | _filter_xfs_io
+echo cp reflink must fail
+_cp_reflink $SCRATCH_MNT/foo $mnt1/bar 2>&1 | _filter_testdir_and_scratch
+
+_scratch_dev_pool_put
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 313
+---- clone_uuids_verify_tempfsid ----
+Mounting original device
+On disk fsid: FSID
+Metadata uuid: FSID
+Temp fsid: FSID
+Tempfsid status: 0
+Mounting cloned device
+On disk fsid: FSID
+Metadata uuid: FSID
+Temp fsid: TEMPFSID
+Tempfsid status: 1
+wrote 9000/9000 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+cp reflink must fail
+cp: failed to clone 'TEST_DIR/313/mnt1/bar' from 'SCRATCH_MNT/foo': Invalid cross-device link
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2024 Oracle. All Rights Reserved.
+#
+# FS QA Test 314
+#
+# Send and receive functionality test between a normal and
+# tempfsid filesystem.
+#
+. ./common/preamble
+_begin_fstest auto quick snapshot send tempfsid
+
+_cleanup()
+{
+ cd /
+ $UMOUNT_PROG $tempfsid_mnt 2>/dev/null
+ rm -r -f $tmp.*
+ rm -r -f $sendfile
+ rm -r -f $tempfsid_mnt
+}
+
+. ./common/filter.btrfs
+
+_supported_fs btrfs
+_require_scratch_dev_pool 2
+_require_btrfs_fs_feature temp_fsid
+
+_scratch_dev_pool_get 2
+
+# mount point for the tempfsid device
+tempfsid_mnt=$TEST_DIR/$seq/tempfsid_mnt
+sendfile=$TEST_DIR/$seq/replicate.send
+
+send_receive_tempfsid()
+{
+ local src=$1
+ local dst=$2
+
+ # Use first 2 devices from the SCRATCH_DEV_POOL
+ mkfs_clone ${SCRATCH_DEV} ${SCRATCH_DEV_NAME[1]}
+ _scratch_mount
+ _mount ${SCRATCH_DEV_NAME[1]} ${tempfsid_mnt}
+
+ $XFS_IO_PROG -fc 'pwrite -S 0x61 0 9000' ${src}/foo | _filter_xfs_io
+ $BTRFS_UTIL_PROG subvolume snapshot -r ${src} ${src}/snap1 | \
+ _filter_testdir_and_scratch
+
+ echo Send ${src} | _filter_testdir_and_scratch
+ $BTRFS_UTIL_PROG send -f ${sendfile} ${src}/snap1 2>&1 | \
+ _filter_testdir_and_scratch
+ echo Receive ${dst} | _filter_testdir_and_scratch
+ $BTRFS_UTIL_PROG receive -f ${sendfile} ${dst} | \
+ _filter_testdir_and_scratch
+ echo -e -n "Send:\t"
+ md5sum ${src}/foo | _filter_testdir_and_scratch
+ echo -e -n "Recv:\t"
+ md5sum ${dst}/snap1/foo | _filter_testdir_and_scratch
+}
+
+mkdir -p $tempfsid_mnt
+
+echo -e \\nFrom non-tempfsid ${SCRATCH_MNT} to tempfsid ${tempfsid_mnt} | \
+ _filter_testdir_and_scratch
+send_receive_tempfsid $SCRATCH_MNT $tempfsid_mnt
+
+_scratch_unmount
+_cleanup
+mkdir -p $tempfsid_mnt
+
+echo -e \\nFrom tempfsid ${tempfsid_mnt} to non-tempfsid ${SCRATCH_MNT} | \
+ _filter_testdir_and_scratch
+send_receive_tempfsid $tempfsid_mnt $SCRATCH_MNT
+
+_scratch_dev_pool_put
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 314
+
+From non-tempfsid SCRATCH_MNT to tempfsid TEST_DIR/314/tempfsid_mnt
+wrote 9000/9000 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Create a readonly snapshot of 'SCRATCH_MNT' in 'SCRATCH_MNT/snap1'
+Send SCRATCH_MNT
+At subvol SCRATCH_MNT/snap1
+Receive TEST_DIR/314/tempfsid_mnt
+At subvol snap1
+Send: 42d69d1a6d333a7ebdf64792a555e392 SCRATCH_MNT/foo
+Recv: 42d69d1a6d333a7ebdf64792a555e392 TEST_DIR/314/tempfsid_mnt/snap1/foo
+
+From tempfsid TEST_DIR/314/tempfsid_mnt to non-tempfsid SCRATCH_MNT
+wrote 9000/9000 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Create a readonly snapshot of 'TEST_DIR/314/tempfsid_mnt' in 'TEST_DIR/314/tempfsid_mnt/snap1'
+Send TEST_DIR/314/tempfsid_mnt
+At subvol TEST_DIR/314/tempfsid_mnt/snap1
+Receive SCRATCH_MNT
+At subvol snap1
+Send: 42d69d1a6d333a7ebdf64792a555e392 TEST_DIR/314/tempfsid_mnt/foo
+Recv: 42d69d1a6d333a7ebdf64792a555e392 SCRATCH_MNT/snap1/foo
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2024 Oracle. All Rights Reserved.
+#
+# FS QA Test 315
+#
+# Verify if the seed and device add to a tempfsid filesystem fails
+# and balance devices is successful.
+#
+. ./common/preamble
+_begin_fstest auto quick volume seed balance tempfsid
+
+_cleanup()
+{
+ cd /
+ $UMOUNT_PROG $tempfsid_mnt 2>/dev/null
+ rm -r -f $tmp.*
+ rm -r -f $tempfsid_mnt
+}
+
+. ./common/filter.btrfs
+
+_supported_fs btrfs
+_require_scratch_dev_pool 3
+_require_btrfs_fs_feature temp_fsid
+
+_scratch_dev_pool_get 3
+
+# mount point for the tempfsid device
+tempfsid_mnt=$TEST_DIR/$seq/tempfsid_mnt
+
+_filter_mount_error()
+{
+ # There are two different errors that occur at the output when
+ # mounting fails; as shown below, pick out the common part. And,
+ # remove the dmesg line.
+
+ # mount: <mnt-point>: mount(2) system call failed: File exists.
+
+ # mount: <mnt-point>: fsconfig system call failed: File exists.
+ # dmesg(1) may have more information after failed mount system call.
+
+ grep -v dmesg | _filter_test_dir | sed -e "s/mount(2)\|fsconfig//g"
+}
+
+seed_device_must_fail()
+{
+ echo ---- $FUNCNAME ----
+
+ mkfs_clone ${SCRATCH_DEV} ${SCRATCH_DEV_NAME[1]}
+
+ $BTRFS_TUNE_PROG -S 1 ${SCRATCH_DEV}
+ $BTRFS_TUNE_PROG -S 1 ${SCRATCH_DEV_NAME[1]}
+
+ _scratch_mount 2>&1 | _filter_scratch
+ _mount ${SCRATCH_DEV_NAME[1]} ${tempfsid_mnt} 2>&1 | _filter_mount_error
+}
+
+device_add_must_fail()
+{
+ echo ---- $FUNCNAME ----
+
+ mkfs_clone ${SCRATCH_DEV} ${SCRATCH_DEV_NAME[1]}
+ _scratch_mount
+ _mount ${SCRATCH_DEV_NAME[1]} ${tempfsid_mnt}
+
+ $XFS_IO_PROG -fc 'pwrite -S 0x61 0 9000' $SCRATCH_MNT/foo | \
+ _filter_xfs_io
+
+$BTRFS_UTIL_PROG device add -f ${SCRATCH_DEV_NAME[2]} ${tempfsid_mnt} 2>&1 | \
+ grep -v "Performing full device TRIM" | _filter_scratch_pool
+
+ echo Balance must be successful
+ _run_btrfs_balance_start ${tempfsid_mnt}
+}
+
+mkdir -p $tempfsid_mnt
+
+seed_device_must_fail
+
+_scratch_unmount
+_cleanup
+mkdir -p $tempfsid_mnt
+
+device_add_must_fail
+
+_scratch_dev_pool_put
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 315
+---- seed_device_must_fail ----
+mount: SCRATCH_MNT: WARNING: source write-protected, mounted read-only.
+mount: TEST_DIR/315/tempfsid_mnt: system call failed: File exists.
+---- device_add_must_fail ----
+wrote 9000/9000 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+ERROR: error adding device 'SCRATCH_DEV': Invalid argument
+Balance must be successful
+Done, had to relocate 3 out of 3 chunks
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2024 SUSE Linux Products GmbH. All Rights Reserved.
+#
+# FS QA Test 316
+#
+# Make sure btrfs qgroup won't leak its reserved data space if qgroup is
+# marked inconsistent.
+#
+# This exercises a regression introduced in v6.1 kernel by the following commit:
+#
+# e15e9f43c7ca ("btrfs: introduce BTRFS_QGROUP_RUNTIME_FLAG_NO_ACCOUNTING to skip qgroup accounting")
+#
+. ./common/preamble
+_begin_fstest auto quick qgroup
+
+_supported_fs btrfs
+_require_scratch
+_require_qgroup_rescan
+
+_fixed_by_kernel_commit d139ded8b9cd \
+ "btrfs: qgroup: always free reserved space for extent records"
+
+_scratch_mkfs >> $seqres.full
+_scratch_mount
+
+$BTRFS_UTIL_PROG quota enable $SCRATCH_MNT
+_qgroup_rescan $SCRATCH_MNT >> $seqres.full
+
+$BTRFS_UTIL_PROG qgroup create 1/0 $SCRATCH_MNT >> $seqres.full
+$BTRFS_UTIL_PROG subvolume create $SCRATCH_MNT/subv1 >> $seqres.full
+
+# This would mark qgroup inconsistent, as the snapshot belongs to a different
+# higher level qgroup, we have to do full rescan on both source and snapshot.
+# This can be very slow for large subvolumes, so btrfs only marks qgroup
+# inconsistent and let users to determine when to do a full rescan
+$BTRFS_UTIL_PROG subvolume snapshot -i 1/0 $SCRATCH_MNT/subv1 $SCRATCH_MNT/snap1 >> $seqres.full
+
+# This write would lead to a qgroup extent record holding the reserved 128K.
+# And for unpatched kernels, the reserved space would not be freed properly
+# due to qgroup is inconsistent.
+_pwrite_byte 0xcd 0 128K $SCRATCH_MNT/foobar >> $seqres.full
+
+# The qgroup leak detection is only triggered at unmount time.
+_scratch_unmount
+
+# Check the dmesg warning for data rsv leak.
+#
+# If CONFIG_BTRFS_DEBUG is enabled, we would have a kernel warning with
+# backtrace, but for release builds, it's just a warning line.
+# So here we manually check the warning message.
+if _dmesg_since_test_start | grep -q "leak"; then
+ _fail "qgroup data reserved space leaked"
+fi
+
+echo "Silence is golden"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 316
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2024 Western Digital Corporation. All Rights Reserved.
+#
+# FS QA Test 317
+#
+# Test that btrfs convert can ony be run to convert to supported profiles on a
+# zoned filesystem
+#
+. ./common/preamble
+_begin_fstest auto volume raid convert
+
+_fixed_by_kernel_commit 5906333cc4af \
+ "btrfs: zoned: don't skip block group profile checks on conventional zones"
+
+. common/filter.btrfs
+
+_supported_fs btrfs
+_require_scratch_dev_pool 4
+_require_zoned_device "$SCRATCH_DEV"
+
+devs=( $SCRATCH_DEV_POOL )
+
+# Create and mount single device FS
+_scratch_mkfs -msingle -dsingle 2>&1 >> $seqres.full || _fail "mkfs failed"
+_scratch_mount
+
+# Convert FS to metadata/system DUP
+_run_btrfs_balance_start -f -mconvert=dup -sconvert=dup $SCRATCH_MNT 2>&1 |\
+ _filter_balance_convert
+
+# Convert FS to data DUP, must fail
+_run_btrfs_balance_start -dconvert=dup $SCRATCH_MNT 2>&1 |\
+ _filter_balance_convert
+
+# Add device
+$BTRFS_UTIL_PROG device add ${devs[1]} $SCRATCH_MNT | _filter_device_add
+
+# Convert FS to data RAID1, must fail
+_run_btrfs_balance_start -dconvert=raid1 $SCRATCH_MNT 2>&1 |\
+ _filter_balance_convert | head -1
+
+# Convert FS to data RAID0, must fail
+_run_btrfs_balance_start -dconvert=raid0 $SCRATCH_MNT 2>&1 |\
+ _filter_balance_convert | head -1
+
+# Add device
+$BTRFS_UTIL_PROG device add ${devs[2]} $SCRATCH_MNT | _filter_device_add
+
+# Convert FS to data RAID5, must fail
+_run_btrfs_balance_start -f -dconvert=raid5 $SCRATCH_MNT 2>&1 |\
+ _filter_balance_convert | head -1
+
+# Add device
+$BTRFS_UTIL_PROG device add ${devs[3]} $SCRATCH_MNT | _filter_device_add
+
+# Convert FS to data RAID10, must fail
+_run_btrfs_balance_start -dconvert=raid10 $SCRATCH_MNT 2>&1 |\
+ _filter_balance_convert | head -1
+
+# Convert FS to data RAID6, must fail
+_run_btrfs_balance_start -f -dconvert=raid6 $SCRATCH_MNT 2>&1 |\
+ _filter_balance_convert | head -1
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 317
+Done, had to relocate X out of X chunks
+ERROR: error during balancing 'SCRATCH_MNT': Invalid argument
+There may be more info in syslog - try dmesg | tail
+ERROR: error during balancing 'SCRATCH_MNT': Invalid argument
+ERROR: error during balancing 'SCRATCH_MNT': Invalid argument
+ERROR: error during balancing 'SCRATCH_MNT': Invalid argument
+ERROR: error during balancing 'SCRATCH_MNT': Invalid argument
+ERROR: error during balancing 'SCRATCH_MNT': Invalid argument
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2021 Facebook. All Rights Reserved.
+#
+# FS QA Test No. 320
+#
+# Test qgroups to validate the creation works, the counters are sane, rescan
+# works, and we do not get failures when we write less than the limit amount.
+#
+. ./common/preamble
+_begin_fstest auto qgroup limit
+
+# Import common functions.
+. ./common/filter
+
+_supported_fs btrfs
+_require_scratch
+_require_qgroup_rescan
+_require_btrfs_qgroup_report
+_require_scratch_qgroup
+
+# Test to make sure we can actually turn it on and it makes sense
+_basic_test()
+{
+ echo "=== basic test ===" >> $seqres.full
+ _run_btrfs_util_prog subvolume create $SCRATCH_MNT/a
+ _run_btrfs_util_prog quota enable $SCRATCH_MNT/a
+ _qgroup_rescan $SCRATCH_MNT
+ subvolid=$(_btrfs_get_subvolid $SCRATCH_MNT a)
+ $BTRFS_UTIL_PROG qgroup show $units $SCRATCH_MNT | grep $subvolid >> \
+ $seqres.full 2>&1
+ [ $? -eq 0 ] || _fail "couldn't find our subvols quota group"
+ run_check $FSSTRESS_PROG -d $SCRATCH_MNT/a -w -p 1 -n 2000 \
+ $FSSTRESS_AVOID
+ _run_btrfs_util_prog subvolume snapshot $SCRATCH_MNT/a \
+ $SCRATCH_MNT/b
+
+ # the shared values of both the original subvol and snapshot should
+ # match
+ a_shared=$($BTRFS_UTIL_PROG qgroup show $units $SCRATCH_MNT | grep "0/$subvolid")
+ a_shared=$(echo $a_shared | $AWK_PROG '{ print $2 }')
+ echo "subvol a id=$subvolid" >> $seqres.full
+ subvolid=$(_btrfs_get_subvolid $SCRATCH_MNT b)
+ echo "subvol b id=$subvolid" >> $seqres.full
+ b_shared=$($BTRFS_UTIL_PROG qgroup show $units $SCRATCH_MNT | grep "0/$subvolid")
+ b_shared=$(echo $b_shared | $AWK_PROG '{ print $2 }')
+ $BTRFS_UTIL_PROG qgroup show $units $SCRATCH_MNT >> $seqres.full
+ [ $b_shared -eq $a_shared ] || _fail "shared values don't match"
+}
+
+#enable quotas, do some work, check our values and then rescan and make sure we
+#come up with the same answer
+_rescan_test()
+{
+ echo "=== rescan test ===" >> $seqres.full
+ # first with a blank subvol
+ _run_btrfs_util_prog subvolume create $SCRATCH_MNT/a
+ _run_btrfs_util_prog quota enable $SCRATCH_MNT/a
+ subvolid=$(_btrfs_get_subvolid $SCRATCH_MNT a)
+ run_check $FSSTRESS_PROG -d $SCRATCH_MNT/a -w -p 1 -n 2000 \
+ $FSSTRESS_AVOID
+ sync
+ output=$($BTRFS_UTIL_PROG qgroup show $units $SCRATCH_MNT | grep "0/$subvolid")
+ echo "qgroup values before rescan: $output" >> $seqres.full
+ refer=$(echo $output | $AWK_PROG '{ print $2 }')
+ excl=$(echo $output | $AWK_PROG '{ print $3 }')
+ _qgroup_rescan $SCRATCH_MNT
+ output=$($BTRFS_UTIL_PROG qgroup show $units $SCRATCH_MNT | grep "0/$subvolid")
+ echo "qgroup values after rescan: $output" >> $seqres.full
+ [ $refer -eq $(echo $output | $AWK_PROG '{ print $2 }') ] || \
+ _fail "reference values don't match after rescan"
+ [ $excl -eq $(echo $output | $AWK_PROG '{ print $3 }') ] || \
+ _fail "exclusive values don't match after rescan"
+}
+
+#basic noexceed limit testing
+_limit_test_noexceed()
+{
+ echo "=== limit not exceed test ===" >> $seqres.full
+ _run_btrfs_util_prog subvolume create $SCRATCH_MNT/a
+ _run_btrfs_util_prog quota enable $SCRATCH_MNT
+ subvolid=$(_btrfs_get_subvolid $SCRATCH_MNT a)
+ _run_btrfs_util_prog qgroup limit 5M 0/$subvolid $SCRATCH_MNT
+ _ddt of=$SCRATCH_MNT/a/file bs=4M count=1 >> $seqres.full 2>&1
+ [ $? -eq 0 ] || _fail "should have been allowed to write"
+}
+
+units=`_btrfs_qgroup_units`
+
+_scratch_mkfs > /dev/null 2>&1
+_scratch_mount
+_basic_test
+_scratch_unmount
+_check_scratch_fs
+
+_scratch_mkfs > /dev/null 2>&1
+_scratch_mount
+_rescan_test
+_scratch_unmount
+_check_scratch_fs
+
+_scratch_mkfs > /dev/null 2>&1
+_scratch_mount
+_limit_test_noexceed
+
+# success, all done
+echo "Silence is golden"
+status=0
+exit
--- /dev/null
+QA output created by 320
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023 Meta Platforms, Inc. All Rights Reserved.
+#
+# FS QA Test No. btrfs/330
+#
+# Test mounting one subvolume as ro and another as rw
+#
+. ./common/preamble
+_begin_fstest auto quick subvol
+
+_cleanup()
+{
+ rm -rf $TEST_DIR/$seq
+}
+
+# Import common functions.
+. ./common/filter.btrfs
+
+# real QA test starts here
+_supported_fs btrfs
+_require_scratch
+
+$MOUNT_PROG -V | grep -q 'fd-based-mount'
+[ "$?" -eq 0 ] && _notrun "mount uses the new mount api"
+
+_scratch_mkfs >> $seqres.full 2>&1 || _fail "mkfs failed"
+_scratch_mount
+
+# Create our subvolumes to mount
+$BTRFS_UTIL_PROG subvolume create $SCRATCH_MNT/foo | _filter_scratch
+$BTRFS_UTIL_PROG subvolume create $SCRATCH_MNT/bar | _filter_scratch
+
+_scratch_unmount
+
+mkdir -p $TEST_DIR/$seq/foo
+mkdir -p $TEST_DIR/$seq/bar
+
+_mount -t btrfs -o subvol=foo,ro $SCRATCH_DEV $TEST_DIR/$seq/foo
+_mount -t btrfs -o subvol=bar,rw $SCRATCH_DEV $TEST_DIR/$seq/bar
+
+echo "making sure foo is read only"
+touch $TEST_DIR/$seq/foo/baz > /dev/null 2&>1
+ls $TEST_DIR/$seq/foo
+
+echo "making sure bar allows writes"
+touch $TEST_DIR/$seq/bar/qux
+ls $TEST_DIR/$seq/bar
+
+$UMOUNT_PROG $TEST_DIR/$seq/foo
+$UMOUNT_PROG $TEST_DIR/$seq/bar
+
+status=0 ; exit
--- /dev/null
+QA output created by 330
+Create subvolume 'SCRATCH_MNT/foo'
+Create subvolume 'SCRATCH_MNT/bar'
+making sure foo is read only
+making sure bar allows writes
+qux
include $(BUILDRULES)
-install:
+install: default
$(INSTALL) -m 755 -d $(TARGET_DIR)
$(INSTALL) -m 755 $(TESTS) $(TARGET_DIR)
$(INSTALL) -m 644 group.list $(TARGET_DIR)
local copies=$4
local c1=$(get_copyfrom_total_copies)
local s1=$(get_copyfrom_total_size)
+ local hascopyfrom=$(_fs_options $TEST_DEV | grep "copyfrom")
local sum
- if [ ! -d $metrics_dir ]; then
+ if [ ! -d "$metrics_dir" ]; then
return # skip metrics check if debugfs isn't mounted
fi
+ if [ -z "$hascopyfrom" ]; then
+ return # ... or if we don't have copyfrom mount option
+ fi
sum=$(($c0+$copies))
if [ $sum -ne $c1 ]; then
*
800000 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 >cccccccccccccccc<
*
-c000000
+c00000
#
# mkdir files limit
# truncate files/file -s 10G
-# setfattr limit -n ceph.quota.max_bytes -v 1000000
+# setfattr limit -n ceph.quota.max_bytes -v 1048576
# mv files limit/
#
# Because we're creating a new file and truncating it, we have Fx caps and thus
}
# set quota to 1m
-$SETFATTR_PROG -n ceph.quota.max_bytes -v 1000000 $dest
+$SETFATTR_PROG -n ceph.quota.max_bytes -v 1048576 $dest
# set quota to 20g
-$SETFATTR_PROG -n ceph.quota.max_bytes -v 20000000000 $orig2
+$SETFATTR_PROG -n ceph.quota.max_bytes -v 21474836480 $orig2
#
# The following 2 testcases shall fail with either -EXDEV or -EDQUOT
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2022 SUSE Linux Products GmbH. All Rights Reserved.
+#
+# FS QA Test 005
+#
+# Make sure statfs reports correct total size when:
+# 1. using a directory with 'max_byte' quota as base for a mount
+# 2. using a subdirectory of the above directory with 'max_files' quota
+#
+. ./common/preamble
+_begin_fstest auto quick quota
+
+_supported_fs ceph
+_require_scratch
+_exclude_test_mount_option "test_dummy_encryption"
+
+_scratch_mount
+mkdir -p "$SCRATCH_MNT/quota-dir/subdir"
+
+# set quota
+quota=$((2 ** 30)) # 1G
+$SETFATTR_PROG -n ceph.quota.max_bytes -v "$quota" "$SCRATCH_MNT/quota-dir"
+$SETFATTR_PROG -n ceph.quota.max_files -v "$quota" "$SCRATCH_MNT/quota-dir/subdir"
+_scratch_unmount
+
+SCRATCH_DEV_ORIG="$SCRATCH_DEV"
+SCRATCH_DEV="$SCRATCH_DEV/quota-dir" _scratch_mount
+echo ceph quota size is $(_get_total_space "$SCRATCH_MNT") bytes
+SCRATCH_DEV="$SCRATCH_DEV_ORIG/quota-dir" _scratch_unmount
+
+SCRATCH_DEV="$SCRATCH_DEV_ORIG/quota-dir/subdir" _scratch_mount
+echo subdir ceph quota size is $(_get_total_space "$SCRATCH_MNT") bytes
+SCRATCH_DEV="$SCRATCH_DEV_ORIG/quota-dir/subdir" _scratch_unmount
+
+echo "Silence is golden"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 005
+ceph quota size is 1073741824 bytes
+subdir ceph quota size is 1073741824 bytes
+Silence is golden
include $(BUILDRULES)
-install:
+install: default
$(INSTALL) -m 755 -d $(TARGET_DIR)
$(INSTALL) -m 755 $(TESTS) $(TARGET_DIR)
$(INSTALL) -m 644 group.list $(TARGET_DIR)
include $(BUILDRULES)
-install:
+install: default
$(INSTALL) -m 755 -d $(TARGET_DIR)
$(INSTALL) -m 755 $(TESTS) $(TARGET_DIR)
$(INSTALL) -m 644 group.list $(TARGET_DIR)
#
seqfull=$0
. ./common/preamble
-_begin_fstest auto prealloc quick zero
+_begin_fstest auto prealloc quick zero fiemap
# Import common functions.
. ./common/filter
_require_scratch
_require_scratch_ext4_feature "bigalloc"
-BLOCK_SIZE=$(get_page_size)
-$MKFS_EXT4_PROG -F -b $BLOCK_SIZE -O bigalloc -C $(($BLOCK_SIZE * 16)) -g 256 $SCRATCH_DEV 512m \
+BLOCK_SIZE=$(_get_page_size)
+features=bigalloc
+if echo "${MOUNT_OPTIONS}" | grep -q 'test_dummy_encryption' ; then
+ features+=",encrypt"
+fi
+$MKFS_EXT4_PROG -F -b $BLOCK_SIZE -O $features -C $(($BLOCK_SIZE * 16)) -g 256 $SCRATCH_DEV 512m \
>> $seqres.full 2>&1
_scratch_mount
echo "++ check fs (2)" >> $seqres.full
_check_scratch_fs >> $seqres.full 2>&1
-egrep -q '(did not fix|makes no progress)' $seqres.full && echo "e2fsck failed" | tee -a $seqres.full
-if [ "$(wc -l < "$ROUND2_LOG")" -ne 8 ]; then
+grep -E -q '(did not fix|makes no progress)' $seqres.full && echo "e2fsck failed" | tee -a $seqres.full
+if [ "$(wc -l < "$ROUND2_LOG")" -ne 7 ]; then
echo "e2fsck did not fix everything" | tee -a $seqres.full
fi
echo "finished fuzzing" | tee -a "$seqres.full"
# see how the kernel and e2fsck deal with it.
#
. ./common/preamble
-_begin_fstest fuzzers
+_begin_fstest fuzzers prealloc
# Override the default cleanup function.
_cleanup()
# see how the kernel and e2fsck deal with it.
#
. ./common/preamble
-_begin_fstest fuzzers punch
+_begin_fstest fuzzers punch prealloc
# Override the default cleanup function.
_cleanup()
# Block size
BLOCK_SIZE=4096
-if [[ $(get_page_size) -ne $BLOCK_SIZE ]]; then
+if [[ $(_get_page_size) -ne $BLOCK_SIZE ]]; then
_exclude_scratch_mount_option dax
fi
# Use large inodes to have enough space for experimentation
# "ext4: make sure enough credits are reserved for dioread_nolock writes"
#
. ./common/preamble
-_begin_fstest auto quick quota
+_begin_fstest auto quick quota fiemap prealloc
# Import common functions.
. ./common/filter
_exclude_scratch_mount_option dax
_require_command "$RESIZE2FS_PROG" resize2fs
-$MKFS_EXT4_PROG -F -b 1024 -E "resize=262144" $SCRATCH_DEV 32768 >> $seqres.full 2>&1
+encrypt=
+if echo "${MOUNT_OPTIONS}" | grep -q 'test_dummy_encryption' ; then
+ encrypt="-O encrypt"
+fi
+$MKFS_EXT4_PROG -F -b 1024 -E "resize=262144" $encrypt $SCRATCH_DEV 32768 >> $seqres.full 2>&1
+if [ $? -ne 0 ]; then
+ _notrun "Can't make file system with a block size of 1024"
+fi
_scratch_mount
echo "Resizing to 262145 blocks"
_supported_fs ext4
_require_scratch
_require_test_program "t_get_file_time"
-
-echo "Silence is golden"
+_require_metadata_journaling
echo "Test timestamps with 256 inode size one device $SCRATCH_DEV" >$seqres.full
_scratch_mkfs -t ext3 -I 256 >> $seqres.full 2>&1
fi
# Check difference between file time and current time
-_within_tolerance "sec_atime" $sec_atime $sec 1
-_within_tolerance "sec_mtime" $sec_mtime $sec 1
-_within_tolerance "sec_ctime" $sec_ctime $sec 1
+_within_tolerance "sec_atime" $sec_atime $sec 1 -v
+_within_tolerance "sec_mtime" $sec_mtime $sec 1 -v
+_within_tolerance "sec_ctime" $sec_ctime $sec 1 -v
_scratch_unmount >> $seqres.full 2>&1
-# Test mount to ext3 then mount back to ext4 and check timestamp again
-_mount -t ext3 `_scratch_mount_options $*` || _fail "ext3 mount failed"
-_scratch_unmount >> $seqres.full 2>&1
+# Test mount to ext3 then mount back to ext4 and check timestamp again. We
+# ignore if ext3 failed to mount. It can happen because some mount options are
+# incompatible with ext3. Still the test makes sense.
+_mount -t ext3 `_scratch_mount_options $*` >> $seqres.full 2>&1 && _scratch_unmount >> $seqres.full 2>&1
_scratch_mount
nsec_atime2=`$here/src/t_get_file_time $SCRATCH_MNT/tmp_file atime nsec`
QA output created by 044
-Silence is golden
+sec_atime is in range
+sec_mtime is in range
+sec_ctime is in range
_require_scratch
_require_test_program "t_create_short_dirs"
_require_test_program "t_create_long_dirs"
-_require_dumpe2fs "$DUMPE2FS_PROG" dumpe2fs
+_require_dumpe2fs
echo "Silence is golden"
checkpoint_journal=$here/src/checkpoint_journal
_require_test_program "checkpoint_journal"
-# convert output from stat<journal_inode> to list of block numbers
-get_journal_extents() {
- inode_info=$($DEBUGFS_PROG $SCRATCH_DEV -R "stat <8>" 2>> $seqres.full)
- echo -e "\nJournal info:" >> $seqres.full
- echo "$inode_info" >> $seqres.full
-
- extents_line=$(echo "$inode_info" | awk '/EXTENTS:/{ print NR; exit }')
- get_extents=$(echo "$inode_info" | sed -n "$(($extents_line + 1))"p)
-
- # get just the physical block numbers
- get_extents=$(echo "$get_extents" | perl -pe 's|\(.*?\):||g' | sed -e 's/, /\n/g' | perl -pe 's|(\d+)-(\d+)|\1 \2|g')
-
- echo "$get_extents"
-}
-
-# checks all extents are zero'd out except for the superblock
-# arg 1: extents (output of get_journal_extents())
-check_extents() {
- echo -e "\nChecking extents:" >> $seqres.full
- echo "$1" >> $seqres.full
-
- super_block="true"
- echo "$1" | while IFS= read line; do
- start_block=$(echo $line | cut -f1 -d' ')
- end_block=$(echo $line | cut -f2 -d' ' -s)
-
- # if first block of journal, shouldn't be wiped
- if [ "$super_block" == "true" ]; then
- super_block="false"
-
- #if super block only block in this extent, skip extent
- if [ -z "$end_block" ]; then
- continue;
- fi
- start_block=$(($start_block + 1))
- fi
-
- if [ ! -z "$end_block" ]; then
- blocks=$(($end_block - $start_block + 1))
- else
- blocks=1
- fi
-
- check=$(od $SCRATCH_DEV --skip-bytes=$(($start_block * $blocksize)) --read-bytes=$(($blocks * $blocksize)) -An -v | sed -e 's/[0 \t\n\r]//g')
-
- [ ! -z "$check" ] && echo "error" && break
- done
-}
-
testdir="${SCRATCH_MNT}/testdir"
_scratch_mkfs_sized $((64 * 1024 * 1024)) >> $seqres.full 2>&1
# call ioctl to checkpoint and zero-fill journal blocks
$checkpoint_journal $SCRATCH_MNT --erase=zeroout || _fail "ioctl returned error"
-extents=$(get_journal_extents)
-
# check journal blocks zeroed out
-ret=$(check_extents "$extents")
-[ "$ret" = "error" ] && _fail "Journal was not zero-filled"
+$DEBUGFS_PROG $SCRATCH_DEV -R "cat <8>" 2> /dev/null | od >> $seqres.full
+check=$($DEBUGFS_PROG $SCRATCH_DEV -R "cat <8>" 2> /dev/null | \
+ od --skip-bytes="$blocksize" -An -v | sed -e '/^[0 \t]*$/d')
+
+[ ! -z "$check" ] && _fail "Journal was not zeroed"
_scratch_unmount >> $seqres.full 2>&1
mnt oldalloc removed
mnt orlov removed
mnt -t user_xattr
- mnt nouser_xattr
if _has_kernel_config CONFIG_EXT4_FS_POSIX_ACL; then
mnt -t acl
mnt barrier=1 barrier
mnt barrier=99 barrier
mnt -t nobarrier
- mnt i_version
mnt stripe=512
only_ext4 mnt delalloc
only_ext4 mnt -t nodelalloc
mnt noinit_itable
mnt max_dir_size_kb=4096
- if _has_kernel_config CONFIG_FS_ENCRYPTION; then
- mnt test_dummy_encryption
- mnt test_dummy_encryption=v1
- mnt test_dummy_encryption=v2
- not_mnt test_dummy_encryption=v3
- not_mnt test_dummy_encryption=
- else
- mnt test_dummy_encryption ^test_dummy_encryption
- mnt test_dummy_encryption=v1 ^test_dummy_encryption=v1
- mnt test_dummy_encryption=v2 ^test_dummy_encryption=v2
- mnt test_dummy_encryption=v3 ^test_dummy_encryption=v3
- not_mnt test_dummy_encryption=
- fi
-
if _has_kernel_config CONFIG_FS_ENCRYPTION_INLINE_CRYPT; then
mnt inlinecrypt
else
mnt_then_not_remount defaults jqfmt=vfsv1
remount defaults grpjquota=,usrjquota= ignored
+ echo "== Testing the test_dummy_encryption option" >> $seqres.full
+ # Since kernel commit 5f41fdaea63d ("ext4: only allow
+ # test_dummy_encryption when supported"), the test_dummy_encryption
+ # option is only allowed when the filesystem has the encrypt feature and
+ # the kernel has CONFIG_FS_ENCRYPTION. The encrypt feature requirement
+ # implies that this option is never allowed on ext2 or ext3 mounts.
+ if [[ $fstype == ext4 ]] && _has_kernel_config CONFIG_FS_ENCRYPTION; then
+ do_mkfs -O encrypt $SCRATCH_DEV ${SIZE}k
+ mnt test_dummy_encryption
+ mnt test_dummy_encryption=v1
+ mnt test_dummy_encryption=v2
+ not_mnt test_dummy_encryption=bad
+ not_mnt test_dummy_encryption=
+ # Can't be set or changed on remount.
+ mnt_then_not_remount defaults test_dummy_encryption
+ mnt_then_not_remount test_dummy_encryption=v1 test_dummy_encryption=v2
+ do_mkfs -O ^encrypt $SCRATCH_DEV ${SIZE}k
+ fi
+ not_mnt test_dummy_encryption
+ not_mnt test_dummy_encryption=v1
+ not_mnt test_dummy_encryption=v2
+ not_mnt test_dummy_encryption=bad
+ not_mnt test_dummy_encryption=
+
done #for fstype in ext2 ext3 ext4; do
$UMOUNT_PROG $SCRATCH_MNT > /dev/null 2>&1
# ext4_valid_extent_entries())
. ./common/preamble
-_begin_fstest auto quick dangerous_fuzzers
+_begin_fstest auto quick dangerous_fuzzers prealloc punch
# Import common functions
. ./common/filter
# real QA test starts here
_supported_fs ext4
-_require_test
_require_scratch_nocheck
_require_xfs_io_command "falloc"
_require_xfs_io_command "pwrite"
_require_command "$DEBUGFS_PROG" debugfs
# In order to accurately construct the damaged extent in the following
-# test steps, the blocksize is set to 1024 here
-_scratch_mkfs "-b 1024" > $seqres.full 2>&1
+# test steps, the block size is set to 1024 here
+_scratch_mkfs_blocksized 1024 >> $seqres.full 2>&1
_scratch_mount
TEST_FILE="${SCRATCH_MNT}/testfile"
echo "Silence is golden"
# The 1K blocksize is designed for debugfs.
+_exclude_scratch_mount_option dax
_scratch_mkfs "-F -O quota -b 1024" > $seqres.full 2>&1
# Start from 0, fill block 1 with 6,replace the original 2.
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Google, Inc. All Rights Reserved.
+#
+# Test the set/get UUID ioctl.
+#
+
+. ./common/preamble
+_begin_fstest auto ioctl
+
+# Override the default cleanup function.
+_cleanup()
+{
+ cd /
+ rm -r -f $tmp.*
+ kill -9 $fsstress_pid 2>/dev/null;
+ wait > /dev/null 2>&1
+}
+
+# Import common functions.
+. ./common/filter
+
+# real QA test starts here
+_supported_fs ext4
+_require_scratch
+_require_test_program uuid_ioctl
+_require_command $UUIDGEN_PROG uuidgen
+
+UUID_IOCTL=$here/src/uuid_ioctl
+
+# If the ioctl is not supported by the kernel, then skip test.
+current_uuid=$($UUID_IOCTL get $SCRATCH_MNT 2>&1)
+if [[ "$current_uuid" =~ ^Inappropriate[[:space:]]ioctl ]]; then
+ _notrun "UUID ioctls are not supported by kernel."
+fi
+
+# metadata_csum_seed must be set to decouple checksums from the uuid.
+# Otherwise, checksums need to be recomputed when the uuid changes, which
+# is not supported by the ioctl.
+_scratch_mkfs_ext4 -O metadata_csum_seed >> $seqres.full 2>&1
+_scratch_mount
+
+# Begin fsstress while modifying UUID
+fsstress_args=$(_scale_fsstress_args -d $SCRATCH_MNT -p 15 -n 999999)
+$FSSTRESS_PROG $fsstress_args >> $seqres.full &
+fsstress_pid=$!
+
+for n in $(seq 1 20); do
+ new_uuid=$($UUIDGEN_PROG)
+
+ echo "Setting UUID to ${new_uuid}" >> $seqres.full 2>&1
+ $UUID_IOCTL set $SCRATCH_MNT $new_uuid
+
+ current_uuid=$($UUID_IOCTL get $SCRATCH_MNT)
+ echo "$UUID_IOCTL get $SCARTCH_MNT: $current_uuid" >> $seqres.full 2>&1
+ if [[ "$current_uuid" != "$new_uuid" ]]; then
+ echo "Current UUID ($current_uuid) does not equal what was sent with the ioctl ($new_uuid)"
+ fi
+done
+
+# success, all done
+echo "Silence is golden"
+status=0
+exit
--- /dev/null
+QA output created by 057
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 HUAWEI. All Rights Reserved.
+#
+# FS QA Test 058
+#
+# Set 256 blocks in a block group, then inject I/O pressure,
+# it will trigger off kernel BUG in ext4_mb_mark_diskspace_used
+#
+# Regression test for commit
+# a08f789d2ab5 ext4: fix bug_on ext4_mb_use_inode_pa
+#
+. ./common/preamble
+_begin_fstest auto quick
+
+# real QA test starts here
+
+_supported_fs ext4
+_fixed_by_kernel_commit a08f789d2ab5 \
+ "ext4: fix bug_on ext4_mb_use_inode_pa"
+_require_scratch
+
+# set 256 blocks in a block group
+_scratch_mkfs -g 256 >> $seqres.full 2>&1 || _fail "mkfs failed"
+_scratch_mount
+
+$FSSTRESS_PROG -d $SCRATCH_MNT/stress -n 1000 >> $seqres.full 2>&1
+
+echo "Silence is golden"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 058
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 HUAWEI. All Rights Reserved.
+#
+# FS QA Test No. 059
+#
+# A regression test for b55c3cd102a6 ("ext4: add reserved GDT blocks check").
+# Make sure there's not kernel crash, if resize an ext4 which resize_inode
+# feature is disabled but has reserved GDT blocks.
+#
+. ./common/preamble
+_begin_fstest auto resize quick
+
+# real QA test starts here
+_supported_fs ext4
+_fixed_by_kernel_commit b55c3cd102a6 \
+ "ext4: add reserved GDT blocks check"
+
+_require_command "$RESIZE2FS_PROG" resize2fs
+_require_command "$DEBUGFS_PROG" debugfs
+_require_scratch_size_nocheck $((1024 * 1024))
+
+# Initalize a 512M ext4 fs with resize_inode feature disabled
+dev_size=$((512 * 1024 * 1024))
+MKFS_OPTIONS="-O ^resize_inode $MKFS_OPTIONS" _scratch_mkfs_sized $dev_size \
+ >>$seqres.full 2>&1 || _fail "mkfs failed"
+
+# Force some reserved GDT blocks to trigger the bug
+$DEBUGFS_PROG -w -R "set_super_value s_reserved_gdt_blocks 100" $SCRATCH_DEV \
+ >>$seqres.full 2>&1
+$DEBUGFS_PROG -R "show_super_stats -h" $SCRATCH_DEV 2>/dev/null | \
+ grep "Reserved GDT blocks"
+
+_scratch_mount
+
+# Expect no crash from this resize operation
+$RESIZE2FS_PROG $SCRATCH_DEV 1G >> $seqres.full 2>&1
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 059
+Reserved GDT blocks: 100
# Make a small ext4 fs with extents disabled & mount it
features="^extents"
if grep -q 64bit /etc/mke2fs.conf ; then
- features="^extents,^64bit"
+ features+=",^64bit"
+fi
+if echo "${MOUNT_OPTIONS}" | grep -q 'test_dummy_encryption' ; then
+ features+=",encrypt"
fi
-blksz=$(get_page_size)
+blksz=$(_get_page_size)
$MKFS_EXT4_PROG -F -b $blksz -O "$features" $SCRATCH_DEV 512m >> $seqres.full 2>&1
_scratch_mount
# Check data integrity during defrag compacting
#
. ./common/preamble
-_begin_fstest auto ioctl rw defrag
+_begin_fstest auto ioctl rw defrag prealloc
# Import common functions.
. ./common/filter
out=$SCRATCH_MNT/fsstress.$$
args=`_scale_fsstress_args -p4 -n999 -f setattr=1 $FSSTRESS_AVOID -d $out`
echo "fsstress $args" >> $seqres.full
- $FSSTRESS_PROG $args > /dev/null 2>&1
+ $FSSTRESS_PROG $args >> $seqres.full
find $out -type f > $out.list
cat $out.list | xargs md5sum > $out.md5sum
usage=`du -sch $out | tail -n1 | gawk '{ print $1 }'`
# So if ioctl was performed twice then inode's layout should not change.
#
. ./common/preamble
-_begin_fstest auto ioctl rw prealloc quick defrag
+_begin_fstest auto ioctl rw prealloc quick defrag fiemap
PIDS=""
include $(BUILDRULES)
-install:
+install: default
$(INSTALL) -m 755 -d $(TARGET_DIR)
$(INSTALL) -m 755 $(TESTS) $(TARGET_DIR)
$(INSTALL) -m 644 group.list $(TARGET_DIR)
# In f2fs, up to 3.4KB of data can be embedded into 4KB-sized inode block.
#
. ./common/preamble
-_begin_fstest auto quick rw
+_begin_fstest auto quick rw prealloc
# Import common functions.
. ./common/filter
# just test LZ4.
. ./common/preamble
-_begin_fstest auto quick rw encrypt compress
+_begin_fstest auto quick rw encrypt compress fiemap
. ./common/filter
. ./common/f2fs
{
$here/src/fscrypt-crypt-util "$@" \
--decrypt \
- --block-size=$block_size \
+ --data-unit-size=$block_size \
--file-nonce=$nonce \
--kdf=HKDF-SHA512 \
AES-256-XTS \
$TEST_RAW_KEY_HEX
}
head -c $num_compressible_bytes $tmp.raw \
- | decrypt_blocks --block-number=1 > $tmp.decrypted
+ | decrypt_blocks --data-unit-index=1 > $tmp.decrypted
dd if=$tmp.raw bs=$cluster_bytes skip=$num_compressible_clusters status=none \
- | decrypt_blocks --block-number=$num_compressible_blocks \
+ | decrypt_blocks --data-unit-index=$num_compressible_blocks \
>> $tmp.decrypted
# Decompress the compressed clusters using the lz4 command-line tool.
include $(BUILDRULES)
-install:
+install: default
$(INSTALL) -m 755 -d $(TARGET_DIR)
$(INSTALL) -m 755 $(TESTS) $(TARGET_DIR)
$(INSTALL) -m 644 group.list $(TARGET_DIR)
# Test fallocate FALLOC_FL_ZERO_RANGE
#
. ./common/preamble
-_begin_fstest auto quick prealloc zero
+_begin_fstest auto quick prealloc zero fiemap
# Import common functions.
. ./common/filter
# For the type of tests, check the description of _test_generic_punch
# in common/rc.
. ./common/preamble
-_begin_fstest auto quick prealloc punch collapse
+_begin_fstest auto quick prealloc punch collapse fiemap
# Import common functions.
# we need to include common/punch to get defination fo filter functions
# For the type of tests, check the description of _test_generic_punch
# in common/rc.
. ./common/preamble
-_begin_fstest auto quick prealloc punch collapse
+_begin_fstest auto quick prealloc punch collapse fiemap
# Import common functions.
# we need to include common/punch to get defination fo filter functions
# Also check for file system consistency after completing this operation
# for each blocksize.
. ./common/preamble
-_begin_fstest auto prealloc collapse
+_begin_fstest auto prealloc collapse fiemap
# Import common functions.
. ./common/filter
testfile=$SCRATCH_MNT/$seq.$$
BLOCKS=10240
-BSIZE=`_get_block_size $SCRATCH_MNT`
+BSIZE=`_get_file_block_size $SCRATCH_MNT`
length=$(($BLOCKS * $BSIZE))
# Import common functions.
. ./common/filter
+. ./common/fail_make_request
_supported_fs generic
_require_scratch
_require_block_device $SCRATCH_DEV
_require_fail_make_request
-SYSFS_BDEV=`_sysfs_dev $SCRATCH_DEV`
-
-allow_fail_make_request()
-{
- echo "Allow global fail_make_request feature"
- echo 100 > $DEBUGFS_MNT/fail_make_request/probability
- echo 9999999 > $DEBUGFS_MNT/fail_make_request/times
- echo 0 > /sys/kernel/debug/fail_make_request/verbose
-}
-
-disallow_fail_make_request()
-{
- echo "Disallow global fail_make_request feature"
- echo 0 > $DEBUGFS_MNT/fail_make_request/probability
- echo 0 > $DEBUGFS_MNT/fail_make_request/times
-}
-
-start_fail_scratch_dev()
-{
- echo "Force SCRATCH_DEV device failure"
- echo " echo 1 > $SYSFS_BDEV/make-it-fail" >> $seqres.full
- echo 1 > $SYSFS_BDEV/make-it-fail
-}
-
-stop_fail_scratch_dev()
-{
- echo "Make SCRATCH_DEV device operable again"
- echo " echo 0 > $SYSFS_BDEV/make-it-fail" >> $seqres.full
- echo 0 > $SYSFS_BDEV/make-it-fail
-}
-
# Override the default cleanup function.
_cleanup()
{
kill $fs_pid $fio_pid &> /dev/null
- disallow_fail_make_request
+ _disallow_fail_make_request
cd /
rm -r -f $tmp.*
}
RUN_TIME=$((20+10*$TIME_FACTOR))
+test -n "$SOAK_DURATION" && RUN_TIME="$SOAK_DURATION"
NUM_JOBS=$((4*LOAD_FACTOR))
BLK_DEV_SIZE=`blockdev --getsz $SCRATCH_DEV`
FILE_SIZE=$((BLK_DEV_SIZE * 512))
# Let's it work for awhile, and force device failure
sleep $RUN_TIME
- start_fail_scratch_dev
+ _start_fail_scratch_dev
# After device turns in to failed state filesystem may yet not know about
# that so buffered write(2) may succeed, but any integrity operations
# such as (sync, fsync, fdatasync, direct-io) should fail.
run_check _scratch_unmount
# Once filesystem was umounted no one is able to write to block device
# It is now safe to bring device back to normal state
- stop_fail_scratch_dev
+ _stop_fail_scratch_dev
# In order to check that filesystem is able to recover journal on mount(2)
# perform mount/umount, after that all errors should be fixed
_scratch_mkfs >> $seqres.full 2>&1 || _fail "mkfs failed"
_scratch_mount
-allow_fail_make_request
+_allow_fail_make_request
_workout
status=$?
exit
fi
}
-# set fs-specific max_attrs and max_attrval_size values. The parameter
-# @max_attrval_namelen is required for filesystems which take into account attr
-# name lengths (including namespace prefix) when determining limits.
+# set fs-specific max_attrs
_attr_get_max()
{
- local max_attrval_namelen="$1"
-
# set maximum total attr space based on fs type
case "$FSTYP" in
- xfs|udf|pvfs2|9p|ceph|nfs)
+ xfs|udf|pvfs2|9p|ceph|fuse|nfs|ceph-fuse)
max_attrs=1000
;;
ext2|ext3|ext4)
let max_attrs=$((($BLOCK_SIZE - 32) / (16 + 12 + 16 )))
fi
;;
+ ubifs)
+ LEB_SIZE=`_get_leb_size $TEST_DEV`
+ # On UBIFS, the number of xattrs has to be less than 50% LEB size
+ # divided by 160 (inode size)
+ let max_attrs=$((($LEB_SIZE / 2 / 160) - 1))
+ ;;
*)
# Assume max ~1 block of attrs
BLOCK_SIZE=`_get_block_size $TEST_DIR`
# overhead
let max_attrs=$BLOCK_SIZE/40
esac
+}
+
+# set fs-specific max_attrval_size values. The parameter @max_attrval_namelen is
+# required for filesystems which take into account attr name lengths (including
+# namespace prefix) when determining limits; parameter @filename is required for
+# filesystems that need to take into account already existing attrs.
+_attr_get_maxval_size()
+{
+ local max_attrval_namelen="$1"
+ local filename="$2"
# Set max attr value size in bytes based on fs type
case "$FSTYP" in
pvfs2)
max_attrval_size=8192
;;
- xfs|udf|9p|ceph)
+ xfs|udf|9p|fuse)
max_attrval_size=65536
;;
bcachefs)
# the underlying filesystem, so just use the lowest value above.
max_attrval_size=1024
;;
+ ceph)
+ # CephFS does not have a maximum value for attributes. Instead,
+ # it imposes a maximum size for the full set of xattrs
+ # names+values, which by default is 64K. Compute the maximum
+ # taking into account the already existing attributes
+ local size=$(getfattr --dump -e hex $filename 2>/dev/null | \
+ awk -F "=0x" '/^user/ {len += length($1) + length($2) / 2} END {print len}')
+ local selinux_size=$(getfattr -n 'security.selinux' --dump -e hex $filename 2>/dev/null | \
+ awk -F "=0x" '/^security/ {len += length($1) + length($2) / 2} END {print len}')
+ if [ -z $size ]; then
+ size=0
+ fi
+ if [ -z $selinux_size ]; then
+ selinux_size=0
+ fi
+ max_attrval_size=$((65536 - $size - $selinux_size - $max_attrval_namelen))
+ ;;
*)
# Assume max ~1 block of attrs
BLOCK_SIZE=`_get_block_size $TEST_DIR`
_attr -r fish $testfile
_attr_list $testfile
-max_attrval_name="long_attr" # add 5 for "user." prefix
-_attr_get_max "$(( 5 + ${#max_attrval_name} ))"
+_attr_get_max
echo "*** add lots of attributes"
v=0
_attr_list $testfile
echo "*** really long value"
+max_attrval_name="long_attr" # add 5 for "user." prefix
+_attr_get_maxval_size "$(( 5 + ${#max_attrval_name} ))" "$testfile"
+
dd if=/dev/zero bs=1 count=$max_attrval_size 2>/dev/null \
| _attr -s "$max_attrval_name" $testfile >/dev/null
# For the type of tests, check the description of _test_generic_punch
# in common/rc.
. ./common/preamble
-_begin_fstest auto quick prealloc punch collapse
+_begin_fstest auto quick prealloc punch collapse fiemap
# Import common functions.
# we need to include common/punch to get defination fo filter functions
# For the type of tests, check the description of _test_generic_punch
# in common/rc.
. ./common/preamble
-_begin_fstest auto quick prealloc punch collapse
+_begin_fstest auto quick prealloc punch collapse fiemap
# Import common functions.
# we need to include common/punch to get defination fo filter functions
_scratch_mkfs > /dev/null 2>&1
_scratch_mount
+_require_congruent_file_oplen $SCRATCH_MNT 4096
$XFS_IO_PROG -f \
-c "pwrite 185332 55756" \
# are always read back as zeroes.
#
. ./common/preamble
-_begin_fstest auto quick rw
+_begin_fstest auto quick rw fiemap prealloc
# Override the default cleanup function.
_cleanup()
# disk's image file is performed by the host).
#
. ./common/preamble
-_begin_fstest auto stress trim
+_begin_fstest auto stress trim prealloc
# Override the default cleanup function.
_cleanup()
{
+ [ -n "${create_pids}" ] && kill ${create_pids[@]}
+ [ -n "${fallocate_pids}" ] && kill ${fallocate_pids[@]}
+ [ -n "${trim_pids}" ] && kill ${trim_pids[@]}
+ wait
rm -fr $tmp
}
_require_scratch
_require_xfs_io_command "falloc"
+echo "Silence is golden"
+
# Keep allocating and deallocating 1G of data space with the goal of creating
# and deleting 1 block group constantly. The intention is to race with the
# fstrim loop below.
create_files()
{
local prefix=$1
+ local now=$(date '+%s')
+ local end_time=$(( now + (TIME_FACTOR * 30) ))
for ((n = 0; n < 4; n++)); do
mkdir $SCRATCH_MNT/$n
echo "Failed creating file $n/${prefix}_$i" >>$seqres.full
break
fi
+ if [ "$(date '+%s')" -ge $end_time ]; then
+ echo "runtime exceeded @ $i files" >> $seqres.full
+ break
+ fi
done
) &
create_pids[$n]=$!
done
wait ${create_pids[@]}
-
+ unset create_pids
}
_scratch_mkfs >>$seqres.full 2>&1
kill ${fallocate_pids[@]}
kill ${trim_pids[@]}
wait
+unset fallocate_pids
+unset trim_pids
-# The fstests framework will now check for fs consistency with fsck.
-# The trimming was racy and caused some btree nodes to get full of zeroes on
-# disk, which obviously caused fs metadata corruption. The race often lead
-# to missing free space entries in a block group's free space cache too.
-
-echo "Silence is golden"
status=0
exit
# stale data exposure can occur.
#
. ./common/preamble
-_begin_fstest shutdown rw punch zero
+_begin_fstest shutdown rw punch zero prealloc auto quick
# Import common functions.
. ./common/filter
img=$SCRATCH_MNT/$seq.img
mnt=$SCRATCH_MNT/$seq.mnt
file=$mnt/file
- size=25M
-
- # f2fs-utils 1.9.0 needs at least 38 MB space for f2fs image. However,
- # f2fs-utils 1.14.0 needs at least 52 MB. Not sure if it will change
- # again. So just set it 128M.
- if [ $FSTYP == "f2fs" ]; then
- size=128M
- fi
+ size=$(_small_fs_size_mb 25)M
# Create an fs on a small, initialized image. The pattern is written to
# the image to detect stale data exposure.
# Test for NULL files problem
#
. ./common/preamble
-_begin_fstest shutdown metadata log auto
+_begin_fstest shutdown metadata log auto fiemap
# Import common functions.
. ./common/filter
# Test for NULL files problem
#
. ./common/preamble
-_begin_fstest shutdown metadata log auto
+_begin_fstest shutdown metadata log auto fiemap
# Import common functions.
. ./common/filter
# Test for NULL files problem
#
. ./common/preamble
-_begin_fstest shutdown metadata log auto
+_begin_fstest shutdown metadata log auto fiemap
# Import common functions.
. ./common/filter
# Test for NULL files problem
#
. ./common/preamble
-_begin_fstest shutdown metadata log auto
+_begin_fstest shutdown metadata log auto fiemap
# Import common functions.
. ./common/filter
# test inode size is on disk after fsync
#
. ./common/preamble
-_begin_fstest shutdown metadata rw auto
+_begin_fstest shutdown metadata rw auto fiemap
# Import common functions.
. ./common/filter
# test inode size is on disk after sync
#
. ./common/preamble
-_begin_fstest shutdown metadata rw auto
+_begin_fstest shutdown metadata rw auto fiemap
# Import common functions.
. ./common/filter
# test inode size is on disk after sync - expose log replay bug
#
. ./common/preamble
-_begin_fstest shutdown metadata rw auto
+_begin_fstest shutdown metadata rw auto fiemap
# Import common functions.
. ./common/filter
# For the type of tests, check the description of _test_generic_punch
# in common/rc.
. ./common/preamble
-_begin_fstest auto quick prealloc punch insert
+_begin_fstest auto quick prealloc punch insert fiemap
# Import common functions.
# we need to include common/punch to get defination fo filter functions
# For the type of tests, check the description of _test_generic_punch
# in common/rc.
. ./common/preamble
-_begin_fstest auto quick prealloc punch insert
+_begin_fstest auto quick prealloc punch insert fiemap
# Import common functions.
# we need to include common/punch to get defination fo filter functions
# For the type of tests, check the description of _test_generic_punch
# in common/rc.
. ./common/preamble
-_begin_fstest auto quick prealloc punch insert
+_begin_fstest auto quick prealloc punch insert fiemap
# Import common functions.
# we need to include common/punch to get defination fo filter functions
# For the type of tests, check the description of _test_generic_punch
# in common/rc.
. ./common/preamble
-_begin_fstest auto quick prealloc punch insert
+_begin_fstest auto quick prealloc punch insert fiemap
# Import common functions.
# we need to include common/punch to get defination fo filter functions
# on the previously inserted ranges to test merge code of collapse
# range. Also check for data integrity and file system consistency.
. ./common/preamble
-_begin_fstest auto quick prealloc collapse insert
+_begin_fstest auto quick prealloc collapse insert fiemap
# Import common functions.
. ./common/filter
src=$SCRATCH_MNT/testfile
dest=$SCRATCH_MNT/testfile.dest
BLOCKS=100
-BSIZE=`_get_block_size $SCRATCH_MNT`
+BSIZE=`_get_file_block_size $SCRATCH_MNT`
length=$(($BLOCKS * $BSIZE))
# Write file
# Override the default cleanup function.
_cleanup()
{
- cd /
-
- trap 0 1 2 3 15
+ # Make sure $SCRATCH_MNT is unfreezed
+ xfs_freeze -u $SCRATCH_MNT 2>/dev/null
+ [ -n "$pid" ] && kill -9 $pid 2>/dev/null
+ wait $pid
+ cd /
+ rm -f $tmp.*
}
# Import common functions.
# We do both read & write IO - not only is this more realistic,
# but it also potentially tests atime updates
FSSTRESS_ARGS=`_scale_fsstress_args -d $STRESS_DIR -p $procs -n $nops $FSSTRESS_AVOID`
- $FSSTRESS_PROG $FSSTRESS_ARGS > /dev/null 2>&1
+ $FSSTRESS_PROG $FSSTRESS_ARGS >>$seqres.full
done
rm -r $STRESS_DIR/*
rmdir $STRESS_DIR
} &
+pid=$!
# start fstest -m loop in a background block; this gets us mmap coverage
{
rm -rf $FSTEST_DIR/*
rmdir $FSTEST_DIR
} &
+pid="$pid $!"
i=0
let ITERATIONS=$ITERATIONS-1
rm $tmp.running
# wait for fsstresses to finish
-wait
+wait $pid
+unset pid
exit 1
mnt=$TEST_DIR/mnt_$seq
mkdir -p $mnt
+size=$(_small_fs_size_mb 300)
+lvsize=$((size * 85 / 100)) # ~256M
+
# make sure there's enough disk space for 256M lv, test for 300M here in case
# lvm uses some space for metadata
-_scratch_mkfs_sized $((300 * 1024 * 1024)) >>$seqres.full 2>&1
+_scratch_mkfs_sized $((size * 1024 * 1024)) >>$seqres.full 2>&1
$LVM_PROG vgcreate -f $vgname $SCRATCH_DEV >>$seqres.full 2>&1
# We use yes pipe instead of 'lvcreate --yes' because old version of lvm
# (like 2.02.95 in RHEL6) don't support --yes option
-yes | $LVM_PROG lvcreate -L 256M -n $lvname $vgname >>$seqres.full 2>&1
+yes | $LVM_PROG lvcreate -L ${lvsize}M -n $lvname $vgname >>$seqres.full 2>&1
# wait for lvcreation to fully complete
$UDEV_SETTLE_PROG >>$seqres.full 2>&1
{
# in case it's still suspended and/or mounted
$DMSETUP_PROG resume $lvdev >/dev/null 2>&1
+ [ -n "$pid" ] && kill -9 $pid 2>/dev/null
+ wait $pid
$UMOUNT_PROG $lvdev >/dev/null 2>&1
_dmsetup_remove $node
}
pid="$pid $!"
wait $pid
+unset pid
status=0
exit
# preallocated space.
#
. ./common/preamble
-_begin_fstest auto quick prealloc
+_begin_fstest auto quick prealloc fiemap
status=0 # success is the default!
_require_xfs_io_command "falloc"
_require_xfs_io_command "fiemap"
+# If the truncation sizes (5M/7M) aren't aligned with the file allocation unit
+# length, then the FIEMAP output will show blocks beyond EOF. That will cause
+# trouble with the golden output, so skip this test if that will be the case.
+_require_congruent_file_oplen $TEST_DIR $((5 * 1048576))
+_require_congruent_file_oplen $TEST_DIR $((7 * 1048576))
+
# First test to make sure that truncating at i_size trims the preallocated bit
# past i_size
$XFS_IO_PROG -f -c "falloc -k 0 10M" -c "pwrite 0 5M" -c "truncate 5M"\
# Run the fiemap (file extent mapping) tester with preallocation enabled
#
. ./common/preamble
-_begin_fstest auto quick prealloc
+_begin_fstest auto quick prealloc fiemap
# Import common functions.
. ./common/filter
_require_test_program "fiemap-tester"
-# FIEMAP test doesn't like finding unwritten blocks after it punches out
-# a partial rt extent.
-test "$FSTYP" = "xfs" && \
- _require_file_block_size_equals_fs_block_size $SCRATCH_MNT
-
seed=`date +%s`
echo "using seed $seed" >> $seqres.full
#
#!/bin/bash
. ./common/preamble
-_begin_fstest acl auto quick
+_begin_fstest acl auto quick perms
# Import common functions.
. ./common/filter
# ENOSPC.
#
. ./common/preamble
-_begin_fstest auto quick attr enospc
+_begin_fstest auto quick attr enospc prealloc
_register_cleanup "_cleanup" 25
# which pulls out an earlier mod
#
. ./common/preamble
-_begin_fstest acl auto quick
+_begin_fstest acl auto quick perms
# Override the default cleanup function.
_cleanup()
physical=`blockdev --getpbsz $SCRATCH_DEV`
logical=`blockdev --getss $SCRATCH_DEV`
+size=$(_small_fs_size_mb 300)
+lvsize=$((size * 91 / 100))
# _get_scsi_debug_dev returns a scsi debug device with 128M in size by default
-SCSI_DEBUG_DEV=`_get_scsi_debug_dev ${physical:-512} ${logical:-512} 0 300`
+SCSI_DEBUG_DEV=`_get_scsi_debug_dev ${physical:-512} ${logical:-512} 0 $size`
test -b "$SCSI_DEBUG_DEV" || _notrun "Failed to initialize scsi debug device"
echo "SCSI debug device $SCSI_DEBUG_DEV" >>$seqres.full
$LVM_PROG vgcreate -f $vgname $SCSI_DEBUG_DEV $SCRATCH_DEV >>$seqres.full 2>&1
# We use yes pipe instead of 'lvcreate --yes' because old version of lvm
# (like 2.02.95 in RHEL6) don't support --yes option
-yes | $LVM_PROG lvcreate -i 2 -I 4m -L 275m -n $lvname $vgname \
+yes | $LVM_PROG lvcreate -i 2 -I 4m -L ${lvsize}m -n $lvname $vgname \
>>$seqres.full 2>&1
# wait for lv creation to fully complete
$UDEV_SETTLE_PROG >>$seqres.full 2>&1
# - Modify the reflinked file
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap
# Override the default cleanup function.
_cleanup()
# - Modify one of the copies
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap
# Override the default cleanup function.
_cleanup()
# - Delete the original (moved) file, check that the copy still exists.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap
# Override the default cleanup function.
_cleanup()
echo "Create the original files"
blksz=65536
+_require_congruent_file_oplen $TEST_DIR $blksz
_pwrite_byte 0x61 $((blksz * 2)) $((blksz * 6)) $testdir/file1 >> $seqres.full
_pwrite_byte 0x61 $((blksz * 2)) $((blksz * 6)) $testdir/file2 >> $seqres.full
_test_cycle_mount
echo "Create the original files"
blksz=65536
+_require_congruent_file_oplen $TEST_DIR $blksz
_pwrite_byte 0x61 $((blksz * 2)) $((blksz * 6)) $testdir/file1 >> $seqres.full
_pwrite_byte 0x62 $((blksz * 2)) $((blksz * 6)) $testdir/file2 >> $seqres.full
_test_cycle_mount
echo "Create the original files"
blksz=65536
+_require_congruent_file_oplen $TEST_DIR $blksz
_pwrite_byte 0x61 0 $((blksz * 8)) $testdir/file1 >> $seqres.full
_pwrite_byte 0x62 0 $((blksz * 8)) $testdir/file2 >> $seqres.full
_pwrite_byte 0x63 0 $((blksz * 8)) $testdir/file3 >> $seqres.full
echo "Create the original files"
blksz=65536
+_require_congruent_file_oplen $TEST_DIR $blksz
_pwrite_byte 0x61 $((blksz * 2)) $((blksz * 6)) $testdir/file1 >> $seqres.full
_pwrite_byte 0x61 $((blksz * 2)) $((blksz * 6)) $testdir/file2 >> $seqres.full
_test_cycle_mount
echo "Create the original files"
blksz=65536
+_require_congruent_file_oplen $TEST_DIR $blksz
_pwrite_byte 0x61 $((blksz * 2)) $((blksz * 6)) $testdir/file1 >> $seqres.full
_pwrite_byte 0x62 $((blksz * 2)) $((blksz * 6)) $testdir/file2 >> $seqres.full
_test_cycle_mount
_require_test
_require_user
+_require_unix_perm_checking
my_test_subdir=$TEST_DIR/123subdir
# ftruncate test, modified from CXFSQA tests cxfs_ftrunc and cxfs_trunc
#
. ./common/preamble
-_begin_fstest other pnfs
+_begin_fstest other pnfs auto
# Import common functions.
. ./common/filter
_require_scratch
_require_user
_require_chmod
+_require_unix_perm_checking
_scratch_mkfs >/dev/null 2>&1
_scratch_mount "-o nosuid"
echo "Create the original files"
blksz=65536
+_require_congruent_file_oplen $TEST_DIR $blksz
_pwrite_byte 0x61 0 $((blksz + 37)) $testdir/file1 >> $seqres.full
_pwrite_byte 0x61 0 $((blksz + 37)) $testdir/file2 >> $seqres.full
_pwrite_byte 0x62 0 $((blksz + 37)) $testdir/file3 >> $seqres.full
echo "Create the original files"
blksz=65536
+_require_congruent_file_oplen $TEST_DIR $blksz
_pwrite_byte 0x61 0 $((blksz + 37)) $testdir/file1 >> $seqres.full
_pwrite_byte 0x61 0 $((blksz + 37)) $testdir/file2 >> $seqres.full
_pwrite_byte 0x62 0 $((blksz + 37)) $testdir/file3 >> $seqres.full
# extents, and non-matches; but actually dedupe real matches.
#
. ./common/preamble
-_begin_fstest auto clone dedupe
+_begin_fstest auto clone dedupe prealloc
# Override the default cleanup function.
_cleanup()
echo "Create the original file blocks"
blksz=65536
+_require_congruent_file_oplen $TEST_DIR $blksz
_pwrite_byte 0x61 0 $blksz $testdir/file1 >> $seqres.full
_pwrite_byte 0x62 $blksz $((blksz * 2)) $testdir/file1 >> $seqres.full
# real QA test starts here
_require_test_reflink
_require_cp_reflink
-_require_odirect
+_require_odirect 512
testdir=$TEST_DIR/test-$seq
rm -rf $testdir
# - Check that the reflinked areas are still there.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
# Override the default cleanup function.
_cleanup()
echo "Create the original files"
blksz=65536
+_require_congruent_file_oplen $TEST_DIR $blksz
_pwrite_byte 0x61 0 $((blksz * 5 + 37)) $testdir/file1 >> $seqres.full
_reflink_range $testdir/file1 $blksz $testdir/file2 $blksz \
# - Check that the reflinked areas are still there.
#
. ./common/preamble
-_begin_fstest auto quick clone collapse
+_begin_fstest auto quick clone collapse prealloc
# Override the default cleanup function.
_cleanup()
echo "Create the original files"
blksz=65536
+_require_congruent_file_oplen $TEST_DIR $blksz
_pwrite_byte 0x61 0 $blksz $testdir/file1 >> $seqres.full
_pwrite_byte 0x62 $blksz $blksz $testdir/file1 >> $seqres.full
_pwrite_byte 0x63 $((blksz * 2)) $blksz $testdir/file1 >> $seqres.full
mkdir $testdir
echo "Create the original file blocks"
-blksz="$(_get_block_size $testdir)"
+blksz="$(_get_file_block_size $testdir)"
blks=2000
margin='15%'
sz=$((blksz * blks))
# "funshare" refers to fallocate copy-on-writing the shared blocks
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone unshare
# Override the default cleanup function.
_cleanup()
mkdir $testdir2
echo "Create the original files"
-blksz="$(_get_block_size $testdir1)"
+blksz="$(_get_file_block_size $testdir1)"
blks=1000
margin='7%'
sz=$((blksz * blks))
loops=512
nr_loops=$((loops - 1))
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
echo "Initialize files"
echo >> $seqres.full
loops=512
nr_loops=$((loops - 1))
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
echo "Initialize files"
echo >> $seqres.full
loops=512
nr_loops=$((loops - 1))
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
echo "Initialize files"
echo >> $seqres.full
_scratch_cycle_mount
fbytes() {
- egrep -v '(61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61|62 62 62 62 62 62 62 62 62 62 62 62 62 62 62 62)'
+ grep -E -v '(61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61|62 62 62 62 62 62 62 62 62 62 62 62 62 62 62 62)'
}
reader() {
loops=512
nr_loops=$((loops - 1))
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
echo "Initialize files"
echo >> $seqres.full
_scratch_cycle_mount
fbytes() {
- egrep -v '(61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61|62 62 62 62 62 62 62 62 62 62 62 62 62 62 62 62)'
+ grep -E -v '(61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61|62 62 62 62 62 62 62 62 62 62 62 62 62 62 62 62)'
}
reader() {
loops=1024
nr_loops=$((loops - 1))
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
echo "Initialize files"
echo >> $seqres.full
loops=1024
nr_loops=$((loops - 1))
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
echo "Initialize files"
echo >> $seqres.full
fnr=19
echo "Create extents"
-truncate -s $(( (2 ** i) * blksz)) "$testdir/file1"
for i in $(seq 0 $fnr); do
echo " ++ Reflink size $i, $((2 ** i)) blocks" >> "$seqres.full"
n=$(( (2 ** i) * blksz))
# This test is motivated by a bug found in btrfs.
#
. ./common/preamble
-_begin_fstest auto quick prealloc metadata punch log
+_begin_fstest auto quick prealloc metadata punch log fiemap
# Override the default cleanup function.
_cleanup()
echo "Create the original files"
blksz=65536
+_require_congruent_file_oplen $TEST_DIR $blksz
_pwrite_byte 0x61 0 $((blksz * 256)) $testdir/file1 >> $seqres.full
_pwrite_byte 0x62 0 $((blksz * 256)) $testdir/file2 >> $seqres.full
_pwrite_byte 0x62 0 $((blksz * 2)) $testdir/file2.chk >> $seqres.full
echo "Create the original files"
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
nr=64
filesize=$((blksz * nr))
_pwrite_byte 0x61 0 $filesize $testdir/file1 >> $seqres.full
echo "Create the original files"
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
nr=64
filesize=$((blksz * nr))
_pwrite_byte 0x61 0 $filesize $testdir/file1 >> $seqres.full
# - Check that the files are now different where we say they're different.
#
. ./common/preamble
-_begin_fstest auto clone punch
+_begin_fstest auto clone punch prealloc
# Override the default cleanup function.
_cleanup()
echo "Create the original files"
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
nr=1024
filesize=$((blksz * nr))
_pwrite_byte 0x61 0 $filesize $testdir/file1 >> $seqres.full
# - Check that the files are now different where we say they're different.
#
. ./common/preamble
-_begin_fstest auto clone punch
+_begin_fstest auto clone punch prealloc
# Override the default cleanup function.
_cleanup()
. ./common/reflink
# real QA test starts here
+
+# btrfs can't fragment free space. This test is unreliable on NFS, as it
+# depends on the exported filesystem.
+_supported_fs ^btrfs ^nfs
_require_scratch_reflink
_require_cp_reflink
_require_xfs_io_command "falloc"
_require_xfs_io_command "fpunch"
-test $FSTYP = "btrfs" && _notrun "Can't fragment free space on btrfs."
_require_odirect
_fragment_freesp()
echo "Create the original files"
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
nr=1024
filesize=$((blksz * nr))
_pwrite_byte 0x61 0 $filesize $testdir/file1 >> $seqres.full
# - Check that the files are now different where we say they're different.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
# Override the default cleanup function.
_cleanup()
echo "Create the original files"
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
nr=64
filesize=$((blksz * nr))
_weave_reflink_unwritten $blksz $nr $testdir/file1 $testdir/file3 >> $seqres.full
# - Check that the files are now different where we say they're different.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
# Override the default cleanup function.
_cleanup()
echo "Create the original files"
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
nr=64
filesize=$((blksz * nr))
_weave_reflink_unwritten $blksz $nr $testdir/file1 $testdir/file3 >> $seqres.full
# - Check that the files are now different where we say they're different.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
# Override the default cleanup function.
_cleanup()
echo "Create the original files"
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
nr=64
filesize=$((blksz * nr))
_weave_reflink_holes $blksz $nr $testdir/file1 $testdir/file3 >> $seqres.full
# - Check that the files are now different where we say they're different.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
# Override the default cleanup function.
_cleanup()
echo "Create the original files"
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
nr=64
filesize=$((blksz * nr))
_weave_reflink_holes $blksz $nr $testdir/file1 $testdir/file3 >> $seqres.full
testfile=$TEST_DIR/testfile
rm -f $testfile
+# Preload every binary used between sampling time1 and time2 so that loading
+# them has minimal overhead even if the root fs is hosted over a slow network.
+# Also don't put pipe and tee creation in that critical section.
+for i in echo stat sleep cat; do
+ $i --help &>/dev/null
+done
+
echo test >$testfile
-time1=`_access_time $testfile | tee -a $seqres.full`
+time1=`_access_time $testfile`
+echo $time1 >> $seqres.full
echo "sleep for $delay seconds"
sleep $delay # sleep to allow time to move on for access
cat $testfile
-time2=`_access_time $testfile | tee -a $seqres.full`
+time2=`_access_time $testfile`
+echo $time2 >> $seqres.full
cd /
_test_cycle_mount
-time3=`_access_time $testfile | tee -a $seqres.full`
+time3=`_access_time $testfile`
+echo $time3 >> $seqres.full
delta1=`expr $time2 - $time1`
delta2=`expr $time3 - $time1`
# Test permission checks in ->setattr
#
. ./common/preamble
-_begin_fstest metadata auto quick
+_begin_fstest metadata auto quick perms
_register_cleanup "_cleanup_files"
tag="added by qa $seq"
# - Check that the files are now different where we say they're different.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
# Override the default cleanup function.
_cleanup()
echo "Create the original files"
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
nr=64
filesize=$((blksz * nr))
_weave_reflink_holes $blksz $nr $testdir/file1 $testdir/file3 >> $seqres.full
# - Check that the files are now different where we say they're different.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
# Override the default cleanup function.
_cleanup()
echo "Create the original files"
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
nr=64
filesize=$((blksz * nr))
_weave_reflink_holes $blksz $nr $testdir/file1 $testdir/file3 >> $seqres.full
# - Check that the files are now different where we say they're different.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
# Override the default cleanup function.
_cleanup()
echo "Create the original files"
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
nr=64
filesize=$((blksz * nr))
_weave_reflink_regular $blksz $nr $testdir/file1 $testdir/file3 >> $seqres.full
# - Check that the files are now different where we say they're different.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
# Override the default cleanup function.
_cleanup()
echo "Create the original files"
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
nr=64
filesize=$((blksz * nr))
_weave_reflink_regular $blksz $nr $testdir/file1 $testdir/file3 >> $seqres.full
# - Check that the files are now different where we say they're different.
#
. ./common/preamble
-_begin_fstest auto quick clone punch
+_begin_fstest auto quick clone punch prealloc
# Override the default cleanup function.
_cleanup()
echo "Create the original files"
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
nr=64
filesize=$((blksz * nr))
_weave_reflink_rainbow $blksz $nr $testdir/file1 $testdir/file3 >> $seqres.full
# - Check that the files are now different where we say they're different.
#
. ./common/preamble
-_begin_fstest auto quick clone punch
+_begin_fstest auto quick clone punch prealloc
# Override the default cleanup function.
_cleanup()
echo "Create the original files"
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
nr=64
filesize=$((blksz * nr))
_weave_reflink_rainbow $blksz $nr $testdir/file1 $testdir/file3 >> $seqres.full
# unlink the file.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
# Override the default cleanup function.
_cleanup()
echo "Create the original files"
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
nr=64
filesize=$((blksz * nr))
_pwrite_byte 0x61 0 $filesize $testdir/file1 >> $seqres.full
# the golden output; we can only compare to a check file.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
# Override the default cleanup function.
_cleanup()
# the golden output; we can only compare to a check file.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
# Override the default cleanup function.
_cleanup()
# the golden output; we can only compare to a check file.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
# Override the default cleanup function.
_cleanup()
_require_quota
_require_user
_require_group
+_require_odirect
test_files()
{
# the golden output; we can only compare to a check file.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
# Override the default cleanup function.
_cleanup()
# the golden output; we can only compare to a check file.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
# Override the default cleanup function.
_cleanup()
# File alignment tests
#
. ./common/preamble
-_begin_fstest auto quick
+_begin_fstest auto quick prealloc
# Import common functions.
. ./common/filter
# Run the fiemap (file extent mapping) tester
#
. ./common/preamble
-_begin_fstest auto quick
+_begin_fstest auto quick fiemap
# Import common functions.
. ./common/filter
_require_test_program "fiemap-tester"
-# FIEMAP test doesn't like finding unwritten blocks after it punches out
-# a partial rt extent.
-test "$FSTYP" = "xfs" && \
- _require_file_block_size_equals_fs_block_size $SCRATCH_MNT
-
seed=`date +%s`
echo "using seed $seed" >> $fiemaplog
# the golden output; we can only compare to a check file.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
# Override the default cleanup function.
_cleanup()
# the golden output; we can only compare to a check file.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
# Override the default cleanup function.
_cleanup()
# See also http://marc.info/?l=linux-btrfs&m=127434445620298&w=2
#
. ./common/preamble
-_begin_fstest auto quick acl
+_begin_fstest auto quick acl perms
# Import common functions.
. ./common/filter
# the golden output; we can only compare to a check file.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
# Override the default cleanup function.
_cleanup()
# According to the rename(2) manpage you can get either EEXIST or ENOTEMPTY as an
# error for trying to rename a non-empty directory, so just catch the error for
# ENOTMEMPTY and replace it with the EEXIST output so that either result passes
+# Also, mv v9.4+ modified error message when a nonempty destination directory fails
+# to be overwriteen
_filter_directory_not_empty()
{
- sed -e "s,Directory not empty,File exists,g"
+ sed -e "s,Directory not empty,File exists,g" \
+ -e "s,cannot move .* to \(.*\):\(.*\),cannot overwrite \1:\2,g"
}
QA output created by 245
-mv: cannot move 'TEST_DIR/test-mv/ab/aa/' to 'TEST_DIR/test-mv/aa': File exists
+mv: cannot overwrite 'TEST_DIR/test-mv/aa': File exists
# corrupts the filesystem (data/metadata).
#
. ./common/preamble
-_begin_fstest ioctl trim
+_begin_fstest ioctl trim auto
tmp=`mktemp -d`
trap "_cleanup; exit \$status" 0 1 3
kill $mypid 2> /dev/null
}
-_guess_max_minlen()
+# Set FSTRIM_{MIN,MAX}_MINLEN to the lower and upper bounds of the -m(inlen)
+# parameter to fstrim on the scratch filesystem.
+set_minlen_constraints()
{
- mmlen=100000
- while [ $mmlen -gt 1 ]; do
+ local mmlen
+
+ for ((mmlen = 100000; mmlen > 0; mmlen /= 2)); do
+ $FSTRIM_PROG -l $(($mmlen*2))k -m ${mmlen}k $SCRATCH_MNT &> /dev/null && break
+ done
+ test $mmlen -gt 0 || \
+ _notrun "could not determine maximum FSTRIM minlen param"
+ FSTRIM_MAX_MINLEN=$mmlen
+
+ for ((mmlen = 1; mmlen < FSTRIM_MAX_MINLEN; mmlen *= 2)); do
$FSTRIM_PROG -l $(($mmlen*2))k -m ${mmlen}k $SCRATCH_MNT &> /dev/null && break
- mmlen=$(($mmlen/2))
done
- echo $mmlen
+ test $mmlen -le $FSTRIM_MAX_MINLEN || \
+ _notrun "could not determine minimum FSTRIM minlen param"
+ FSTRIM_MIN_MINLEN=$mmlen
+}
+
+# Set FSTRIM_{MIN,MAX}_LEN to the lower and upper bounds of the -l(ength)
+# parameter to fstrim on the scratch filesystem.
+set_length_constraints()
+{
+ local mmlen
+
+ for ((mmlen = 100000; mmlen > 0; mmlen /= 2)); do
+ $FSTRIM_PROG -l ${mmlen}k $SCRATCH_MNT &> /dev/null && break
+ done
+ test $mmlen -gt 0 || \
+ _notrun "could not determine maximum FSTRIM length param"
+ FSTRIM_MAX_LEN=$mmlen
+
+ for ((mmlen = 1; mmlen < FSTRIM_MAX_LEN; mmlen *= 2)); do
+ $FSTRIM_PROG -l ${mmlen}k $SCRATCH_MNT &> /dev/null && break
+ done
+ test $mmlen -le $FSTRIM_MAX_LEN || \
+ _notrun "could not determine minimum FSTRIM length param"
+ FSTRIM_MIN_LEN=$mmlen
}
##
##
fstrim_loop()
{
+ set_minlen_constraints
+ set_length_constraints
+ echo "MINLEN max=$FSTRIM_MAX_MINLEN min=$FSTRIM_MIN_MINLEN" >> $seqres.full
+ echo "LENGTH max=$FSTRIM_MAX_LEN min=$FSTRIM_MIN_LEN" >> $seqres.full
+
trap "_destroy_fstrim; exit \$status" 2 15
- fsize=$($DF_PROG | grep $SCRATCH_MNT | grep $SCRATCH_DEV | awk '{print $3}')
- mmlen=$(_guess_max_minlen)
+ fsize=$(_discard_max_offset_kb "$SCRATCH_MNT" "$SCRATCH_DEV")
while true ; do
- step=$((RANDOM*$RANDOM+4))
- minlen=$(((RANDOM*($RANDOM%2+1))%$mmlen))
+ while true; do
+ step=$((RANDOM*$RANDOM+4))
+ test "$step" -ge "$FSTRIM_MIN_LEN" && break
+ done
+ while true; do
+ minlen=$(( (RANDOM * (RANDOM % 2 + 1)) % FSTRIM_MAX_MINLEN ))
+ test "$minlen" -ge "$FSTRIM_MIN_MINLEN" && break
+ done
+
start=$RANDOM
if [ $((RANDOM%10)) -gt 7 ]; then
$FSTRIM_PROG $SCRATCH_MNT &
}
nproc=20
-content=$here
+
+# Copy $here to the scratch fs and make coipes of the replica. The fstests
+# output (and hence $seqres.full) could be in $here, so we need to snapshot
+# $here before computing file checksums.
+content=$SCRATCH_MNT/orig
+mkdir -p $content
+cp -axT $here/ $content/
mkdir -p $tmp
# Test Generic fallocate hole punching
#
. ./common/preamble
-_begin_fstest auto quick prealloc punch
+_begin_fstest auto quick prealloc punch fiemap
# Import common functions.
. ./common/filter
local file_len=$(( $(( $hole_len + $hole_interval )) * $iterations ))
local path=`dirname $file_name`
local hole_offset=0
+ local start_time
+ local stop_time
if [ $# -ne 5 ]
then
-c "fsync" $file_name &> /dev/null
chmod 666 $file_name
+ start_time="$(date +%s)"
+ stop_time=$(( start_time + (30 * TIME_FACTOR) ))
+
# All files are created as a non root user to prevent reserved blocks
# from being consumed.
_fill_fs $(( 1024 * 1024 * 1024 )) $path/fill $block_size 1 \
for (( i=0; i<$iterations; i++ ))
do
+ test "$(date +%s)" -ge "$stop_time" && break
+
# This part must not be done as root in order to
# test that reserved blocks are used when needed
_user_do "$XFS_IO_PROG -f -c \"fpunch $hole_offset $hole_len\" $file_name"
_require_batched_discard $SCRATCH_MNT
-fssize=$($DF_PROG -k | grep "$SCRATCH_MNT" | grep "$SCRATCH_DEV" | awk '{print $3}')
+fssize=$(_discard_max_offset_kb "$SCRATCH_MNT" "$SCRATCH_DEV")
beyond_eofs=$(_math "$fssize*2048")
max_64bit=$(_math "2^64 - 1")
start=$(_math "$base*$agsize*$bsize")
len=$start
export MKFS_OPTIONS="-F -b $bsize -g $agsize"
+ if echo "${MOUNT_OPTIONS}" | grep -q 'test_dummy_encryption' ; then
+ MKFS_OPTIONS+=" -O encrypt"
+ fi
;;
xfs)
agsize=65538
# the golden output; we can only compare to a check file.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone unshare
# Import common functions.
. ./common/filter
out=$SCRATCH_MNT/fsstress.$$
args=`_scale_fsstress_args -p128 -n999999999 -f setattr=1 $FSSTRESS_AVOID -d $out`
echo "fsstress $args" >> $seqres.full
- $FSSTRESS_PROG $args > /dev/null 2>&1 &
+ $FSSTRESS_PROG $args &>> $seqres.full &
pid=$!
echo "Run dd writers in parallel"
for ((i=0; i < num_iterations; i++))
# Import common functions.
. ./common/filter
. ./common/quota
+. ./common/attr
# Disable all sync operations to get higher load
FSSTRESS_AVOID="$FSSTRESS_AVOID -ffsync=0 -fsync=0 -ffdatasync=0"
_require_scratch
_require_command "$KILLALL_PROG" killall
_require_command "$SETCAP_PROG" setcap
+_require_attrs security
_scratch_mkfs_sized $((512 * 1024 * 1024)) >> $seqres.full 2>&1
_scratch_mount "-o usrquota,grpquota"
cd $SCRATCH_MNT/origin
- _disksize=`$DF_PROG -B 1 $SCRATCH_MNT | tail -1 | $AWK_PROG '{ print $5 }'`
+ _disksize=$(_get_available_space $SCRATCH_MNT)
+ _free_inodes=$(_get_free_inode $SCRATCH_MNT)
+ # Some filesystems do not limit number of inodes and return 0
+ if [ $_free_inodes -eq 0 ]; then
+ # Guess one block per inode
+ _free_inodes=$(($_disksize / $block_size))
+ fi
+ # Leave some slack for directories etc.
+ _free_inodes=$(($_free_inodes - $_free_inodes/8))
_disksize=$(($_disksize / 3))
- _num=$(($_disksize / $count / $threads / $block_size))
+ _num=$(($_disksize / $count / $block_size))
+ if [ $_num -gt $_free_inodes ]; then
+ _num=$_free_inodes
+ fi
+ _num=$(($_num/$threads))
_count=$count
while [ $_i -lt $_num ]
do
_scratch_mkfs_sized $((2 * 1024 * 1024 * 1024)) >>$seqres.full 2>&1
_scratch_mount
+# Certain filesystems such as XFS require sufficient free blocks to handle the
+# worst-case directory expansion as a result of a creat() call. If the fs
+# block size is very large (e.g. 64k) then the number of blocks required for
+# the creat() call can represent far more free space than the 256K left at the
+# end of this test. Therefore, create the file that the last dd will write to
+# now when we know there's enough free blocks.
+later_file=$SCRATCH_MNT/later
+touch $later_file
+
# this file will get removed to create 256k of free space after ENOSPC
# conditions are created.
dd if=/dev/zero of=$SCRATCH_MNT/tmp1 bs=256K count=1 >>$seqres.full 2>&1
# Try to write more than available space in chunks that will allow at least one
# full write to succeed.
-dd if=/dev/zero of=$SCRATCH_MNT/tmp1 bs=128k count=8 >>$seqres.full 2>&1
+dd if=/dev/zero of=$later_file bs=128k count=8 >>$seqres.full 2>&1
echo "Bytes written until ENOSPC:" >>$seqres.full
-du $SCRATCH_MNT/tmp1 >>$seqres.full
+du $later_file >>$seqres.full
# And at least some of it should succeed.
-_filesize=`_get_filesize $SCRATCH_MNT/tmp1`
+_filesize=`_get_filesize $later_file`
[ $_filesize -lt $((128 * 1024)) ] && \
_fail "Partial write until enospc failed; wrote $_filesize bytes."
. ./common/preamble
_begin_fstest auto quota freeze
+# Override the default cleanup function.
+_cleanup()
+{
+ # Make sure $SCRATCH_MNT is unfreezed
+ xfs_freeze -u $SCRATCH_MNT 2>/dev/null
+ [ -n "$pid" ] && kill -9 $pid 2>/dev/null
+ wait $pid
+ cd /
+ rm -f $tmp.*
+}
+
# Import common functions.
. ./common/filter
. ./common/quota
sleep 1
xfs_freeze -u $SCRATCH_MNT
wait $pid
-_scratch_unmount
+unset pid
# Failure comes in the form of a deadlock.
# - Check that the files are now different where we say they're different.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
# Import common functions.
. ./common/filter
echo "Create the original files"
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
nr=64
filesize=$((blksz * nr))
_sweave_reflink_regular $blksz $nr $testdir/file1 $testdir/file3 >> $seqres.full
# Override the default cleanup function.
_cleanup()
{
- eval "rm -f $BASE_TEST_FILE.*"
+ rm -f $BASE_TEST_FILE*
}
_run_seek_sanity_test $BASE_TEST_FILE > $seqres.full 2>&1 ||
# SEEK_DATA/SEEK_HOLE copy tests.
#
. ./common/preamble
-_begin_fstest auto quick other seek
+_begin_fstest auto quick other seek prealloc
# Import common functions.
. ./common/filter
# - Check that the files are now different where we say they're different.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
# Import common functions.
. ./common/filter
echo "Create the original files"
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
nr=64
filesize=$((blksz * nr))
_sweave_reflink_regular $blksz $nr $testdir/file1 $testdir/file3 >> $seqres.full
# - Check that the files are now different where we say they're different.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
# Import common functions.
. ./common/filter
echo "Create the original files"
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
nr=64
filesize=$((blksz * nr))
_sweave_reflink_unwritten $blksz $nr $testdir/file1 $testdir/file3 >> $seqres.full
# - Check that the files are now different where we say they're different.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
# Import common functions.
. ./common/filter
echo "Create the original files"
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
nr=64
filesize=$((blksz * nr))
_sweave_reflink_unwritten $blksz $nr $testdir/file1 $testdir/file3 >> $seqres.full
# - Check that the files are now different where we say they're different.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
# Import common functions.
. ./common/filter
echo "Create the original files"
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
nr=64
filesize=$((blksz * nr))
_sweave_reflink_holes $blksz $nr $testdir/file1 $testdir/file3 >> $seqres.full
# - Check that the files are now different where we say they're different.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
# Import common functions.
. ./common/filter
echo "Create the original files"
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
nr=64
filesize=$((blksz * nr))
_sweave_reflink_holes $blksz $nr $testdir/file1 $testdir/file3 >> $seqres.full
# - Check that the files are now different where we say they're different.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
# Import common functions.
. ./common/filter
echo "Create the original files"
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
nr=64
filesize=$((blksz * nr))
_sweave_reflink_holes $blksz $nr $testdir/file1 $testdir/file3 >> $seqres.full
# real QA test starts here
-# Modify as appropriate.
-_supported_fs generic
+# NFS will optimize away the on-the-wire lookup before attempting to
+# create a new file (since that means an extra round trip).
+_supported_fs ^nfs
+
_require_scratch
_require_symlinks
_require_mknod
# - Check that the files are now different where we say they're different.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
# Import common functions.
. ./common/filter
echo "Create the original files"
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
nr=64
filesize=$((blksz * nr))
_sweave_reflink_holes $blksz $nr $testdir/file1 $testdir/file3 >> $seqres.full
fnr=26 # 2^26 reflink extents should be enough to find a slow op?
timeout=8 # guarantee a good long run...
echo "Find a reflink size that takes a long time"
-truncate -s $(( (2 ** i) * blksz)) $testdir/file1
for i in $(seq 0 $fnr); do
echo " ++ Reflink size $i, $((2 ** i)) blocks" >> $seqres.full
n=$(( (2 ** i) * blksz))
fnr=26 # 2^26 reflink extents should be enough to find a slow op?
timeout=8 # guarantee a good long run...
echo "Find a reflink size that takes a long time"
-truncate -s $(( (2 ** i) * blksz)) $testdir/file1
for i in $(seq 0 $fnr); do
echo " ++ Reflink size $i, $((2 ** i)) blocks" >> $seqres.full
n=$(( (2 ** i) * blksz))
# Test will operate on huge sparsed files so ENOSPC is expected.
#
. ./common/preamble
-_begin_fstest auto aio enospc rw stress
+_begin_fstest auto aio enospc rw stress prealloc
fio_config=$tmp.fio
fio_out=$tmp.fio.out
_require_odirect
_require_aio
_require_block_device $SCRATCH_DEV
+_require_xfs_io_command "falloc"
NUM_JOBS=$((4*LOAD_FACTOR))
BLK_DEV_SIZE=`blockdev --getsz $SCRATCH_DEV`
FILE_SIZE=$((BLK_DEV_SIZE * 512))
-max_file_size=$(_get_max_file_size)
+max_file_size=$(_get_max_file_size $TEST_DIR)
if [ $max_file_size -lt $FILE_SIZE ]; then
FILE_SIZE=$max_file_size
fi
EOF
_require_fio $fio_config
-_require_xfs_io_command "falloc"
_scratch_mkfs >> $seqres.full 2>&1
_scratch_mount
# - Check the number of extents.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap
# Import common functions.
. ./common/filter
# - Check the number of extents.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap
# Import common functions.
. ./common/filter
# charged for buffered copy on write.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap
# Import common functions.
. ./common/filter
# regression test of sorts.
#
. ./common/preamble
-_begin_fstest auto metadata log
+_begin_fstest auto metadata log prealloc
# Override the default cleanup function.
_cleanup()
# Test SGID inheritance on subdirectories
#
. ./common/preamble
-_begin_fstest auto quick
+_begin_fstest auto quick perms
# Import common functions.
. ./common/filter
_require_test
_require_user
_require_chown
+_require_sgid_inheritance
rm -rf $TEST_DIR/$seq-dir
# Test Generic fallocate hole punching w/o unwritten extent
#
. ./common/preamble
-_begin_fstest auto quick punch
+_begin_fstest auto quick punch fiemap
# Import common functions.
. ./common/filter
_require_ugid_map
_require_userns
_require_chown
+_require_use_local_uidgid
qa_user_id=`id -u $qa_user`
_filter_output()
# the ACL was flushed and brought back from disk.
#
. ./common/preamble
-_begin_fstest acl attr auto quick
+_begin_fstest acl attr auto quick perms
# Override the default cleanup function.
_cleanup()
# https://patchwork.kernel.org/patch/3046931/
#
. ./common/preamble
-_begin_fstest acl auto quick
+_begin_fstest acl auto quick perms
# Import common functions.
. ./common/filter
# Sanity check for defrag utility.
#
. ./common/preamble
-_begin_fstest auto fsr quick defrag
+_begin_fstest auto fsr quick defrag prealloc
PIDS=""
# charged for directio copy on write.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap
# Import common functions.
. ./common/filter
# Ensure that we can't go over the hard block limit when reflinking.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap
# Import common functions.
. ./common/filter
# Ensure that we can't go over the hard block limit when CoWing a file.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap
# Import common functions.
. ./common/filter
_scratch_mkfs >>$seqres.full 2>&1
_require_metadata_journaling $SCRATCH_DEV
+
+if [ $FSTYP = "f2fs" ]; then
+ export MOUNT_OPTIONS="-o fsync_mode=strict $MOUNT_OPTIONS"
+fi
+
_init_flakey
_mount_flakey
# devices that don't support write_same or discard.
#
. ./common/preamble
-_begin_fstest blockdev rw punch collapse insert zero
+_begin_fstest blockdev rw punch collapse insert zero prealloc
_register_cleanup "_cleanup" BUS
# Which btrfs will soft lock up and return wrong shared flag.
#
. ./common/preamble
-_begin_fstest auto clone
+_begin_fstest auto clone fiemap
# Import common functions.
. ./common/filter
_scratch_mkfs > /dev/null 2>&1
_scratch_mount
-blocksize=$((128 * 1024))
+blocksize=$(_get_file_block_size $SCRATCH_MNT)
+_require_congruent_file_oplen $SCRATCH_MNT $blocksize
file="$SCRATCH_MNT/tmp"
# Golden output is for $LOAD_FACTOR == 1 case
+# and assumes a normal blocksize of 4K
orig_nr=8192
-orig_last_extent=$(($orig_nr * $blocksize / 512))
-orig_end=$(($orig_last_extent + $blocksize / 512 - 1))
+orig_blocksize=4096
+orig_last_extent=$(($orig_nr * $orig_blocksize / 512))
+orig_end=$(($orig_last_extent + $orig_blocksize / 512 - 1))
# Real output
nr=$(($orig_nr * $LOAD_FACTOR))
end=$(($last_extent + $blocksize / 512 - 1))
# write the initial block for later reflink
-_pwrite_byte 0xcdcdcdcd 0 $blocksize $file | _filter_xfs_io
+_pwrite_byte 0xcdcdcdcd 0 $blocksize $file > /dev/null
# use reflink to create the rest of the file, whose all extents are all
# pointing to the first extent
QA output created by 352
-wrote 131072/131072 bytes at offset 0
-XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
-0: [0..2097151]: shared
-1: [2097152..2097407]: shared|last
+0: [0..65535]: shared
+1: [65536..65543]: shared|last
# This caused SHARED flag only occurs after sync.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap
# Import common functions.
. ./common/filter
_scratch_mkfs > /dev/null 2>&1
_scratch_mount
-blocksize=64k
+blocksize=$(_get_file_block_size $SCRATCH_MNT)
+
file1="$SCRATCH_MNT/file1"
file2="$SCRATCH_MNT/file2"
+extmap1="$SCRATCH_MNT/extmap1"
+extmap2="$SCRATCH_MNT/extmap2"
# write the initial file
-_pwrite_byte 0xcdcdcdcd 0 $blocksize $file1 | _filter_xfs_io
+_pwrite_byte 0xcdcdcdcd 0 $blocksize $file1 > /dev/null
# reflink initial file
-_reflink_range $file1 0 $file2 0 $blocksize | _filter_xfs_io
+_reflink_range $file1 0 $file2 0 $blocksize > /dev/null
# check their fiemap to make sure it's correct
-echo "before sync:"
-echo "$file1" | _filter_scratch
-$XFS_IO_PROG -c "fiemap -v" $file1 | _filter_fiemap_flags
-echo "$file2" | _filter_scratch
-$XFS_IO_PROG -c "fiemap -v" $file2 | _filter_fiemap_flags
+$XFS_IO_PROG -c "fiemap -v" $file1 | _filter_fiemap_flags > $extmap1
+$XFS_IO_PROG -c "fiemap -v" $file2 | _filter_fiemap_flags > $extmap2
+
+cmp -s $extmap1 $extmap2 || echo "mismatched extent maps before sync"
# sync and recheck, to make sure the fiemap doesn't change just
# due to sync
sync
-echo "after sync:"
-echo "$file1" | _filter_scratch
-$XFS_IO_PROG -c "fiemap -v" $file1 | _filter_fiemap_flags
-echo "$file2" | _filter_scratch
-$XFS_IO_PROG -c "fiemap -v" $file2 | _filter_fiemap_flags
+$XFS_IO_PROG -c "fiemap -v" $file1 | _filter_fiemap_flags > $extmap1
+$XFS_IO_PROG -c "fiemap -v" $file2 | _filter_fiemap_flags > $extmap2
+
+cmp -s $extmap1 $extmap2 || echo "mismatched extent maps after sync"
+
+echo "Silence is golden"
# success, all done
status=0
QA output created by 353
-wrote 65536/65536 bytes at offset 0
-XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
-linked 65536/65536 bytes at offset 0
-XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
-before sync:
-SCRATCH_MNT/file1
-0: [0..127]: shared|last
-SCRATCH_MNT/file2
-0: [0..127]: shared|last
-after sync:
-SCRATCH_MNT/file1
-0: [0..127]: shared|last
-SCRATCH_MNT/file2
-0: [0..127]: shared|last
+Silence is golden
# Test clear of suid/sgid on direct write.
#
. ./common/preamble
-_begin_fstest auto quick
+_begin_fstest auto quick perms
# Import common functions.
. ./common/filter
. ./common/reflink
# real QA test starts here
+
+# For NFS, a reflink is just a CLONE operation, and after that
+# point it's dealt with by the server.
+_supported_fs ^nfs
+
_require_scratch_swapfile
_require_scratch_reflink
_require_cp_reflink
blocks=64
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
echo "Initialize file"
_pwrite_byte 0x61 0 $((blocks * blksz)) $testdir/file >> $seqres.full
blocks=64
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
nr=4
halfway=$((blocks / 2 * blksz))
quarter=$((blocks / 4 * blksz))
# RichACL apply-masks test
#
. ./common/preamble
-_begin_fstest auto quick richacl
+_begin_fstest auto quick richacl perms
# Import common functions.
# RichACL auto-inheritance test
#
. ./common/preamble
-_begin_fstest auto quick richacl
+_begin_fstest auto quick richacl perms
# Import common functions.
# RichACL basic test
#
. ./common/preamble
-_begin_fstest auto quick richacl
+_begin_fstest auto quick richacl perms
# Import common functions.
. ./common/attr
# RichACL chmod test
#
. ./common/preamble
-_begin_fstest auto quick richacl
+_begin_fstest auto quick richacl perms
# Import common functions.
# RichACL chown test
#
. ./common/preamble
-_begin_fstest auto quick richacl
+_begin_fstest auto quick richacl perms
# Import common functions.
# RichACL create test
#
. ./common/preamble
-_begin_fstest auto quick richacl
+_begin_fstest auto quick richacl perms
# Import common functions.
# RichACL ctime test
#
. ./common/preamble
-_begin_fstest auto quick richacl
+_begin_fstest auto quick richacl perms
# Import common functions.
# RichACL delete test
#
. ./common/preamble
-_begin_fstest auto quick richacl
+_begin_fstest auto quick richacl perms
# Import common functions.
# RichACL write-vs-append test
#
. ./common/preamble
-_begin_fstest auto quick richacl
+_begin_fstest auto quick richacl perms
# Import common functions.
# Check that bmap/fiemap accurately report shared extents.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap prealloc
_register_cleanup "_cleanup" BUS
blocks=5
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
sz=$((blocks * blksz))
echo "Create the original files"
# owning group.
#
. ./common/preamble
-_begin_fstest auto quick acl
+_begin_fstest auto quick acl perms
# Import common functions.
. ./common/filter
_require_metadata_journaling $SCRATCH_DEV
_scratch_mount
-for i in $(seq 1 $((50 * TIME_FACTOR)) ); do
+while _soak_loop_running $((50 * TIME_FACTOR)); do
($FSSTRESS_PROG $FSSTRESS_AVOID -d $SCRATCH_MNT -n 999999 -p 4 >> $seqres.full &) \
> /dev/null 2>&1
# SPDX-License-Identifier: GPL-2.0
# Copyright (c) 2016 Red Hat, Inc. All Rights Reserved.
#
-# FS QA Test 999
+# FS QA Test 390
#
# Multi-threads freeze/unfreeze testing. This's a stress test case,
# it won't do functional check.
_cleanup()
{
cd /
- # Make sure $SCRATCH_MNT is unfreezed
+ # Kill freeze loops and make sure $SCRATCH_MNT is unfreezed
+ [ -n "$freeze_pids" ] && kill -9 $freeze_pids 2>/dev/null
+ wait $freeze_pids
xfs_freeze -u $SCRATCH_MNT 2>/dev/null
+ [ -n "$fsstress_pid" ] && kill -9 $fsstress_pid 2>/dev/null
+ wait $fsstress_pid
rm -f $tmp.*
}
wait $fsstress_pid
result=$?
+unset fsstress_pid
wait $freeze_pids
+unset freeze_pids
# Exit with fsstress return value
status=$result
# to spurious -EEXIST failures from direct I/O reads.
#
. ./common/preamble
-_begin_fstest auto quick rw
+_begin_fstest auto quick rw prealloc
# Override the default cleanup function.
_cleanup()
#
# Test inode's metadata after fsync or fdatasync calls.
# In the case of fsync, filesystem should recover all the inode metadata, while
-# recovering i_blocks and i_size at least for fdatasync.
+# recovering for fdatasync it should at least recovery i_size.
#
. ./common/preamble
_begin_fstest shutdown auto quick metadata punch
# fsync or fdatasync
if [ $sync_mode = "fsync" ]; then
- stat_opt='-c "b: %b s: %s a: %x m: %y c: %z"'
+ stat_opt='-c "s: %s a: %x m: %y c: %z"'
else
- stat_opt='-c "b: %b s: %s"'
+ stat_opt='-c "s: %s"'
fi
before=`stat "$stat_opt" $testfile`
{
cd /
ulimit -f unlimited
- rm -f $tmp.*
+ rm -f $tmp.* $TEST_DIR/$seq.*
}
# Import common functions.
# each block insert.
#
. ./common/preamble
-_begin_fstest auto quick insert
+_begin_fstest auto quick insert prealloc
testfile=$TEST_DIR/$seq.file
pattern=$tmp.pattern
_require_xfs_io_command "falloc"
_require_xfs_io_command "finsert"
-blksize=`_get_block_size $TEST_DIR`
+blksize=`_get_file_block_size $TEST_DIR`
# Generate a block with a repeating number represented as 4 bytes decimal.
# The test generates unique pattern for each block in order to observe a
-f chown=1 \
-f getdents=1 \
-f fiemap=1 \
- -d $target >/dev/null
+ -d $target >>$seqres.full
sync
}
-f chown=1 \
-f getdents=1 \
-f fiemap=1 \
- -d $target >/dev/null
+ -d $target >>$seqres.full
sync
}
{
local type=$1
- _scratch_mkfs >$seqres.full 2>&1
+ _scratch_mkfs >>$seqres.full 2>&1
_get_mount -t $FSTYP $SCRATCH_DEV $MNTHEAD
$MOUNT_PROG --make-"${type}" $MNTHEAD
mkdir $mpA $mpB $mpC
-f chown=1 \
-f getdents=1 \
-f fiemap=1 \
- -d $target >/dev/null
+ -d $target >>$seqres.full
sync
}
# mmap direct/buffered io between DAX and non-DAX mountpoints.
#
. ./common/preamble
-_begin_fstest auto quick dax
+_begin_fstest auto quick dax prealloc
# Import common functions.
. ./common/filter
_supported_fs generic
+_require_hugepages
_require_test
_require_scratch_dax_mountopt "dax"
_require_test_program "feature"
# block mapping extent.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap prealloc
_register_cleanup "_cleanup" BUS
blocks=32
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
sz=$((blocks * blksz))
echo "Create the original files"
_require_scratch
fs_size=$((128 * 1024 * 1024))
-page_size=$(get_page_size)
+page_size=$(_get_page_size)
# We will never reach this number though
nr_files=$(($fs_size / $page_size))
# delayed allocations.
#
. ./common/preamble
-_begin_fstest auto quick
+_begin_fstest auto quick prealloc
# Import common functions.
. ./common/filter
# block to hold extended attributes.
#
. ./common/preamble
-_begin_fstest auto quick attr
+_begin_fstest auto quick attr fiemap
_register_cleanup "_cleanup" BUS
# Override the default cleanup function.
_cleanup()
{
- rm -f $tmp.* $BASE_TEST_FILE.*
+ rm -f $tmp.* $BASE_TEST_FILE*
}
# Import common functions.
echo "Format and mount"
_scratch_mkfs > $seqres.full 2>&1
-_dmerror_init
+_dmerror_init no_log
_dmerror_mount
_require_fs_space $SCRATCH_MNT 65536
# in the owning group and directory has default ACLs.
#
. ./common/preamble
-_begin_fstest auto quick acl
+_begin_fstest auto quick acl perms
# Import common functions.
. ./common/filter
# Override the default cleanup function.
_cleanup()
{
- rm -f $tmp.* $BASE_TEST_FILE.*
+ rm -f $tmp.* $BASE_TEST_FILE*
}
# Import common functions.
_require_scratch
_require_xfs_io_command "truncate"
_require_xfs_io_command "fpunch"
+_require_odirect
# format and mount
_scratch_mkfs > $seqres.full 2>&1
_cleanup()
{
cd /
- rm -f $tmp.* $BASE_TEST_FILE
+ rm -f $tmp.* $BASE_TEST_FILE*
}
# Import common functions.
testf "combmark_\xe1\x80\x9c\xe1\x80\xaf\xe1\x80\xad.txt" "combining marks"
echo "Uniqueness of keys?"
-crazy_keys="$(_getfattr --absolute-names -d "${testfile}" | egrep -c '(french_|chinese_|greek_|arabic_|urk)')"
+crazy_keys="$(_getfattr --absolute-names -d "${testfile}" | grep -E -c '(french_|chinese_|greek_|arabic_|urk)')"
expected_keys=11
test "${crazy_keys}" -ne "${expected_keys}" && echo "Expected ${expected_keys} keys, saw ${crazy_keys}."
_supported_fs generic
_require_test
_require_scratch_nocheck
+_require_no_logdev
_require_log_writes
_require_dm_target thin-pool
rm -rf $SANITY_DIR
mkdir $SANITY_DIR
-devsize=$((1024*1024*200 / 512)) # 200m phys/virt size
+size=$(_small_fs_size_mb 200) # 200m phys/virt size
+devsize=$((1024*1024*size / 512))
csize=$((1024*64 / 512)) # 64k cluster size
lowspace=$((1024*1024 / 512)) # 1m low space threshold
seeds=(0 0 0 0)
# Run fsx for a while
for j in `seq 0 $((NUM_FILES-1))`; do
- run_check $here/ltp/fsx $FSX_OPTS -S ${seeds[$j]} -j $j $SCRATCH_MNT/testfile$j &
+ run_check $here/ltp/fsx $FSX_OPTS $FSX_AVOID -S ${seeds[$j]} -j $j $SCRATCH_MNT/testfile$j &
done
wait
_supported_fs generic
_require_test
_require_scratch_reflink
+_require_no_logdev
_require_cp_reflink
_require_log_writes
_require_dm_target thin-pool
rm -rf $SANITY_DIR
mkdir $SANITY_DIR
-devsize=$((1024*1024*200 / 512)) # 200m phys/virt size
+size=$(_small_fs_size_mb 200) # 200m phys/virt size
+devsize=$((1024*1024*size / 512))
csize=$((1024*64 / 512)) # 64k cluster size
lowspace=$((1024*1024 / 512)) # 1m low space threshold
for j in `seq 0 $((NUM_FILES-1))`; do
# clone the clone from prev iteration which may have already mutated
_cp_reflink $SCRATCH_MNT/testfile$((j-1)) $SCRATCH_MNT/testfile$j
- run_check $here/ltp/fsx $FSX_OPTS -S 0 -j $j $SCRATCH_MNT/testfile$j &
+ run_check $here/ltp/fsx $FSX_OPTS $FSX_AVOID -S 0 -j $j $SCRATCH_MNT/testfile$j &
done
wait
# Override the default cleanup function.
_cleanup()
{
+ # Make sure $SCRATCH_MNT is unfreezed
+ xfs_freeze -u $SCRATCH_MNT 2>/dev/null
cd /
rm -f $tmp.*
$UMOUNT_PROG $SCRATCH_MNT >>$seqres.full 2>&1
_require_command $LVM_PROG lvm
_require_command "$THIN_CHECK_PROG" thin_check
_require_freeze
+_require_odirect
vgname=vg_$seq
lvname=lv_$seq
virtsize=300
newpsize=300
+# Check whether the filesystem has shutdown or remounted read-only. Shutdown
+# behavior can differ based on filesystem and configuration. Some fs' may not
+# have remounted without an additional write while others may have shutdown but
+# do not necessarily reflect read-only state in the mount options. Check both
+# here by first trying a simple write and following with an explicit ro check.
+is_shutdown_or_ro()
+{
+ ro=0
+
+ # if the fs has not shutdown, this may help trigger a remount-ro
+ touch $SCRATCH_MNT/newfile > /dev/null 2>&1 || ro=1
+
+ _fs_options /dev/mapper/$vgname-$snapname | grep -w "ro" > /dev/null
+ [ $? == 0 ] && ro=1
+
+ echo $ro
+}
+
# Ensure we have enough disk space
_scratch_mkfs_sized $((350 * 1024 * 1024)) >>$seqres.full 2>&1
# - The filesystem stays in Read-Write mode, but can be frozen/thawed
# without getting stuck.
if [ $ret -ne 0 ]; then
- # freeze failed, filesystem should reject further writes and remount
- # as readonly. Sometimes the previous write process won't trigger
- # ro-remount, e.g. on ext3/4, do additional touch here to make sure
- # filesystems see the metadata I/O error.
- touch $SCRATCH_MNT/newfile >/dev/null 2>&1
- ISRO=$(_fs_options /dev/mapper/$vgname-$snapname | grep -w "ro")
- if [ -n "$ISRO" ]; then
+ # freeze failed, filesystem should reject further writes
+ ISRO=`is_shutdown_or_ro`
+ if [ $ISRO == 1 ]; then
echo "Test OK"
else
echo "Freeze failed and FS isn't Read-Only. Test Failed"
. ./common/filter
# real QA test starts here
-_supported_fs generic
+_supported_fs ^nfs
_require_aiodio aio-dio-append-write-read-race
_require_test_program "feature"
# that inode metadata will be unchanged after recovery.
#
. ./common/preamble
-_begin_fstest shutdown auto quick metadata
+_begin_fstest shutdown auto quick metadata prealloc
# Import common functions.
. ./common/filter
# the bug on XFS.
#
. ./common/preamble
-_begin_fstest auto quick punch zero
+_begin_fstest auto quick punch zero prealloc
file=$TEST_DIR/$seq.fsx
{
cd /
_log_writes_cleanup
- _dmthin_cleanup
rm -f $tmp.*
}
# Import common functions.
. ./common/filter
-. ./common/dmthin
. ./common/dmlogwrites
# real QA test starts here
_supported_fs generic
-_require_scratch_nocheck
+_require_scratch
+_require_no_logdev
_require_log_writes_dax_mountopt "dax"
-_require_dm_target thin-pool
_require_xfs_io_command "mmap" "-S"
_require_xfs_io_command "log_writes"
+_require_command "$BLKDISCARD_PROG" blkdiscard
-devsize=$((1024*1024*200 / 512)) # 200m phys/virt size
-csize=$((1024*64 / 512)) # 64k cluster size
-lowspace=$((1024*1024 / 512)) # 1m low space threshold
-
-# Use a thin device to provide deterministic discard behavior. Discards are used
-# by the log replay tool for fast zeroing to prevent out-of-order replay issues.
-_dmthin_init $devsize $devsize $csize $lowspace
+MAPPED_LEN=$((512 * 1024 * 1024)) # 512 MiB
+LEN=$((1024 * 1024)) # 1 MiB
-_log_writes_init $DMTHIN_VOL_DEV
+_log_writes_init $SCRATCH_DEV $MAPPED_LEN
_log_writes_mkfs >> $seqres.full 2>&1
_log_writes_mount -o dax
-LEN=$((1024 * 1024)) # 1 MiB
-
$XFS_IO_PROG -t -c "truncate $LEN" -c "mmap -S 0 $LEN" -c "mwrite 0 $LEN" \
-c "log_writes -d $LOGWRITES_NAME -m preunmap" \
-f $SCRATCH_MNT/test
# Unmount the scratch dir and tear down the log writes target
_log_writes_unmount
_log_writes_remove
-_dmthin_check_fs
+_check_scratch_fs
-# destroy previous filesystem so we can be sure our rebuild works
-_mkfs_dev $DMTHIN_VOL_DEV >> $seqres.full 2>&1
+# Forcibly zero the mapped range of scratch device and destroy
+# previous filesystem so we can be sure our rebuild works.
+# Note that blkdiscard -z will fall back to writing buffers of zeroes
+# if scratch device doesn't support write zeroes operation(i.e.
+# REQ_OP_WRITE_ZEROES).
+$BLKDISCARD_PROG -fzl $MAPPED_LEN $SCRATCH_DEV >> $seqres.full 2>&1
+_scratch_mkfs >> $seqres.full 2>&1
# check pre-unmap state
-_log_writes_replay_log preunmap $DMTHIN_VOL_DEV
-_dmthin_mount
+_log_writes_replay_log preunmap $SCRATCH_DEV
+_scratch_mount
# We should see $SCRATCH_MNT/test as having 1 MiB in block allocations
du -sh $SCRATCH_MNT/test | _filter_scratch | _filter_spaces
#! /bin/bash
# SPDX-License-Identifier: GPL-2.0
-# Copyright (c) 2017, SUSE Linux Products. All Rights Reserved.
+# Copyright (C) 2023 SUSE Linux Products GmbH. All Rights Reserved.
#
-# FS QA Test No. 471
+# FS QA Test 471
#
-# write a file with RWF_NOWAIT and it would fail because there are no
-# blocks allocated. Create a file with direct I/O and re-write it
-# using RWF_NOWAIT. I/O should finish within 50 microsecods since
-# block allocations are already performed.
+# Test that if names are added to a directory after an opendir(3) call and
+# before a rewinddir(3) call, future readdir(3) calls will return the names.
+# This is mandated by POSIX:
+#
+# https://pubs.opengroup.org/onlinepubs/007904875/functions/rewinddir.html
#
. ./common/preamble
-_begin_fstest auto quick rw
+_begin_fstest auto quick dir
-# Import common functions.
-. ./common/populate
-. ./common/filter
-. ./common/attr
+_cleanup()
+{
+ cd /
+ rm -fr $tmp.*
+ rm -fr $target_dir
+}
-# real QA test starts here
-_require_odirect
+_supported_fs generic
_require_test
-_require_xfs_io_command pwrite -N
-
-# Remove reminiscence of previously run tests
-testdir=$TEST_DIR/$seq
-if [ -e $testdir ]; then
- rm -Rf $testdir
-fi
-
-mkdir $testdir
-
-# Btrfs is a COW filesystem, so a RWF_NOWAIT write will always fail with -EAGAIN
-# when writing to a file range except if it's a NOCOW file and an extent for the
-# range already exists or if it's a COW file and preallocated/unwritten extent
-# exists in the target range. So to make sure that the last write succeeds on
-# all filesystems, use a NOCOW file on btrfs.
-if [ $FSTYP == "btrfs" ]; then
- _require_chattr C
- # Zoned btrfs does not support NOCOW
- _require_non_zoned_device $TEST_DEV
- touch $testdir/f1
- $CHATTR_PROG +C $testdir/f1
-fi
-
-# Create a file with pwrite nowait (will fail with EAGAIN)
-$XFS_IO_PROG -f -d -c "pwrite -N -V 1 -b 1M 0 1M" $testdir/f1
-
-# Write the file without nowait
-$XFS_IO_PROG -f -d -c "pwrite -S 0xaa -W -w -V 1 -b 1M 0 8M" $testdir/f1 | _filter_xfs_io
+_require_test_program rewinddir-test
-time_taken=`$XFS_IO_PROG -d -c "pwrite -S 0xbb -N -V 1 -b 1M 2M 1M" $testdir/f1 | awk '/^1/ {print $5}'`
+[ $FSTYP == "btrfs" ] && _fixed_by_kernel_commit e60aa5da14d0 \
+ "btrfs: refresh dir last index during a rewinddir(3) call"
-# RWF_NOWAIT should finish within a short period of time so we are choosing
-# a conservative value of 50 ms. Anything longer means it is waiting
-# for something in the kernel which would be a fail.
-if (( $(echo "$time_taken < 0.05" | bc -l) )); then
- echo "RWF_NOWAIT time is within limits."
-else
- echo "RWF_NOWAIT took $time_taken seconds"
-fi
+target_dir="$TEST_DIR/test-$seq"
+rm -fr $target_dir
+mkdir $target_dir
-$XFS_IO_PROG -c "pread -v 0 8M" $testdir/f1 | _filter_xfs_io_unique
+$here/src/rewinddir-test $target_dir
# success, all done
+echo "Silence is golden"
status=0
exit
QA output created by 471
-pwrite: Resource temporarily unavailable
-wrote 8388608/8388608 bytes at offset 0
-XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
-RWF_NOWAIT time is within limits.
-00000000: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
-*
-00200000: bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb ................
-*
-00300000: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
-*
-read 8388608/8388608 bytes at offset 0
-XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Silence is golden
# Create a ridiculously small swap file. Each swap file must have at least
# two pages after the header page.
echo "tiny swap" | tee -a $seqres.full
-swapfile_cycle $swapfile $(($(get_page_size) * 3))
+swapfile_cycle $swapfile $(($(_get_page_size) * 3))
status=0
exit
# Also the test used 16k holes to be compatible with 16k block filesystems
#
. ./common/preamble
-_begin_fstest broken
+_begin_fstest broken fiemap
# Import common functions.
. ./common/punch
# testing efforts.
#
. ./common/preamble
-_begin_fstest shutdown auto log metadata eio recoveryloop
+_begin_fstest shutdown auto log metadata eio recoveryloop smoketest
# Override the default cleanup function.
_cleanup()
_dmerror_init
_dmerror_mount
-for i in $(seq 1 $((50 * TIME_FACTOR)) ); do
+while _soak_loop_running $((50 * TIME_FACTOR)); do
($FSSTRESS_PROG $FSSTRESS_AVOID -d $SCRATCH_MNT -n 999999 -p $((LOAD_FACTOR * 4)) >> $seqres.full &) \
> /dev/null 2>&1
# bugs in the write path.
#
. ./common/preamble
-_begin_fstest auto rw
+_begin_fstest auto rw long_rw stress soak smoketest
# Override the default cleanup function.
_cleanup()
nr_cpus=$((LOAD_FACTOR * 4))
nr_ops=$((25000 * nr_cpus * TIME_FACTOR))
-$FSSTRESS_PROG $FSSTRESS_AVOID -w -d $SCRATCH_MNT -n $nr_ops -p $nr_cpus >> $seqres.full
+fsstress_args=(-w -d $SCRATCH_MNT -n $nr_ops -p $nr_cpus)
+test -n "$SOAK_DURATION" && fsstress_args+=(--duration="$SOAK_DURATION")
+
+$FSSTRESS_PROG $FSSTRESS_AVOID "${fsstress_args[@]}" >> $seqres.full
# success, all done
status=0
$XFS_IO_PROG -f -c "pwrite -S 0xFF 0 4096" \
$TEST_DIR/testfile >> $seqres.full 2>&1
+mk_sem()
+{
+ SEMID=$(ipcmk -S 2 | cut -d ":" -f 2 | tr -d '[:space:]')
+ if [ -z "$SEMID" ]; then
+ echo "ipcmk failed"
+ exit 1
+ fi
+ SEMKEY=$(ipcs -s | grep $SEMID | cut -d " " -f 1)
+ if [ -z "$SEMKEY" ]; then
+ echo "ipcs failed"
+ exit 1
+ fi
+}
+
+rm_sem()
+{
+ ipcrm -s $SEMID 2>/dev/null
+}
+
do_test()
{
- local soptions="$1"
- local goptions="$2"
+ local soptions
+ local goptions
# print options and getlk output for debug
echo $* >> $seqres.full 2>&1
+ mk_sem
+ soptions="$1 -K $SEMKEY"
+ goptions="$2 -K $SEMKEY"
# -s : do setlk
$here/src/t_ofd_locks $soptions $TEST_DIR/testfile &
# -g : do getlk
$here/src/t_ofd_locks $goptions $TEST_DIR/testfile | \
tee -a $seqres.full
wait $!
+ rm_sem
+ mk_sem
# add -F to clone with CLONE_FILES
- soptions="$1 -F"
+ soptions="$1 -F -K $SEMKEY"
+ goptions="$2 -K $SEMKEY"
# with -F, new locks are always file to place
$here/src/t_ofd_locks $soptions $TEST_DIR/testfile &
$here/src/t_ofd_locks $goptions $TEST_DIR/testfile | \
tee -a $seqres.full
wait $!
+ rm_sem
+ mk_sem
# add -d to dup and close
- soptions="$1 -d"
+ soptions="$1 -d -K $SEMKEY"
+ goptions="$2 -K $SEMKEY"
$here/src/t_ofd_locks $soptions $TEST_DIR/testfile &
$here/src/t_ofd_locks $goptions $TEST_DIR/testfile | \
tee -a $seqres.full
wait $!
+ rm_sem
}
# Always setlk at range [0,9], getlk at range [0,9] [5,24] or [20,29].
# Modify as appropriate.
_supported_fs generic
+_require_no_logdev
_require_command "$KILLALL_PROG" killall
# Use thin device as replay device, which requires $SCRATCH_DEV
_require_scratch_nocheck
fi
fsstress_args=$(_scale_fsstress_args -w -d $SCRATCH_MNT -n 512 -p $nr_cpus \
$FSSTRESS_AVOID)
-devsize=$((1024*1024*200 / 512)) # 200m phys/virt size
+
+size=$(_small_fs_size_mb 200) # 200m phys/virt size
+devsize=$((1024*1024*size / 512))
csize=$((1024*64 / 512)) # 64k cluster size
lowspace=$((1024*1024 / 512)) # 1m low space threshold
# are placed beyond a file's size.
#
. ./common/preamble
-_begin_fstest auto quick log metadata
+_begin_fstest auto quick log metadata fiemap prealloc
# Override the default cleanup function.
_cleanup()
_init_flakey
_mount_flakey
+# The fiemap results in the golden output requires file allocations to align to
+# 256K boundaries.
+_require_congruent_file_oplen $SCRATCH_MNT 262144
+
# Create our test files.
$XFS_IO_PROG -f -c "pwrite -S 0xea 0 256K" $SCRATCH_MNT/foo >/dev/null
# buffer: record blockdev write errors in super_block that it backs
. ./common/preamble
-_begin_fstest auto quick
+_begin_fstest auto quick eio
# Override the default cleanup function.
_cleanup()
# 7d83fb14258b ("xfs: prevent creating negative-sized file via INSERT_RANGE")
#
. ./common/preamble
-_begin_fstest auto quick insert
+_begin_fstest auto quick insert prealloc
# Override the default cleanup function.
_cleanup()
_require_xfs_io_command "truncate"
block_size=$(_get_file_block_size $TEST_DIR)
-max_file_size=$(_get_max_file_size)
+max_file_size=$(_get_max_file_size $TEST_DIR)
max_blocks=$((max_file_size / block_size))
testfile=$TEST_DIR/testfile.$seq
sed -e 's/has a [0-9]* byte value/has a NNNN byte value/g'
}
-$here/src/attr_replace_test $SCRATCH_MNT/hello
+max_attr_size=65536
+
+# attr_replace_test can't easily auto-probe the attr size for ceph because:
+# - ceph imposes a maximum value for the total xattr names+values, and
+# - ceph reports the 'object size' in the block size, which is, by default, much
+# larger than XATTR_SIZE_MAX (4M > 64k)
+# Hence, we need to provide it with a maximum size.
+[ "$FSTYP" = "ceph" ] && max_attr_size=65000
+
+$here/src/attr_replace_test -m $max_attr_size $SCRATCH_MNT/hello
$ATTR_PROG -l $SCRATCH_MNT/hello >>$seqres.full 2>&1
$ATTR_PROG -l $SCRATCH_MNT/hello | filter_attr_output
echo "Format and mount"
_scratch_mkfs > $seqres.full 2>&1
-_dmerror_init
+_dmerror_init no_log
_dmerror_mount
datalen=65536
. ./common/preamble
_begin_fstest auto quick freeze mount
+# Override the default cleanup function.
+_cleanup()
+{
+ cd /
+ # Make sure $SCRATCH_MNT is unfreezed
+ xfs_freeze -u $SCRATCH_MNT 2>/dev/null
+ rm -f $tmp.*
+}
+
# Import common functions.
. ./common/filter
# Read file while filesystem is frozen should succeed
# without blocking
-$TIMEOUT_PROG -s KILL 1s cat $testfile
+$TIMEOUT_PROG -s KILL 5s cat $testfile
xfs_freeze -u $SCRATCH_MNT
_scratch_mkfs >> $seqres.full 2>&1
_scratch_mount
+blksize=$(_get_file_block_size $SCRATCH_MNT)
+test $blksize -eq $(getconf PAGE_SIZE) || \
+ _notrun "swap file allocation unit size must match page size"
+
# We can't use _format_swapfile because we're using our custom mkswap and
# swapon.
touch "$SCRATCH_MNT/swap"
$CHATTR_PROG +C "$SCRATCH_MNT/swap" >> $seqres.full 2>&1
chmod 0600 "$SCRATCH_MNT/swap"
-$XFS_IO_PROG -c "truncate $(($(get_page_size) * 10))" "$SCRATCH_MNT/swap"
+$XFS_IO_PROG -c "truncate $(($(_get_page_size) * 10))" "$SCRATCH_MNT/swap"
"$here/src/mkswap" "$SCRATCH_MNT/swap"
"$here/src/swapon" "$SCRATCH_MNT/swap"
swapoff "$SCRATCH_MNT/swap" >/dev/null 2>&1
swapfile=$SCRATCH_MNT/swap
len=$((2 * 1048576))
-page_size=$(get_page_size)
+page_size=$(_get_page_size)
swapfile_cycle() {
local swapfile="$1"
swapfile=$SCRATCH_MNT/swap
len=$((2 * 1048576))
-page_size=$(get_page_size)
+page_size=$(_get_page_size)
swapfile_cycle() {
local swapfile="$1"
# eof to return nonzero contents.
#
. ./common/preamble
-_begin_fstest auto quick rw collapse zero
+_begin_fstest auto quick rw collapse zero prealloc
# Import common functions.
. ./common/punch
_dmthin_init $BACKING_SIZE $VIRTUAL_SIZE $CLUSTER_SIZE 0
_dmthin_set_fail
-_dmthin_mkfs
+_dmthin_try_mkfs >> $seqres.full 2>&1 || \
+ _notrun "Could not format small thinp filesystem for test"
_dmthin_mount
# There're two bugs at here, one is dm-thin bug, the other is filesystem
_require_metadata_journaling $SCRATCH_DEV
_init_flakey
_mount_flakey
+_require_congruent_file_oplen $SCRATCH_MNT 2097152
# Use file sizes and offsets/lengths for the clone operation that are multiples
# of 64Kb, so that the test works on machine with any page size.
_scratch_mkfs >>$seqres.full 2>&1
_require_metadata_journaling $SCRATCH_DEV
+
+if [ $FSTYP = "f2fs" ]; then
+ export MOUNT_OPTIONS="-o fsync_mode=strict $MOUNT_OPTIONS"
+fi
+
_init_flakey
_mount_flakey
# don't require the DAX mount option or a specific filesystem for the test.
. ./common/preamble
-_begin_fstest auto quick dax punch collapse zero
+_begin_fstest auto quick dax punch collapse zero prealloc
# Import common functions.
. ./common/filter
export MOUNT_OPTIONS=""
_scratch_mount >> $seqres.full 2>&1
+blksize=$(_get_file_block_size $SCRATCH_MNT)
+test $blksize -eq $(getconf PAGE_SIZE) || \
+ _notrun "file block size must match page size"
+
# real QA test starts here
$here/src/t_mmap_collision $TEST_DIR/testfile $SCRATCH_MNT/testfile
_supported_fs generic
_require_scratch
+_require_quota
_require_scratch_shutdown
_scratch_mkfs >/dev/null 2>&1
+_scratch_enable_pquota
_require_metadata_journaling $SCRATCH_DEV
_qmount_option "prjquota"
_qmount
# eof to return nonzero contents.
#
. ./common/preamble
-_begin_fstest auto quick rw zero
+_begin_fstest auto quick rw zero prealloc
# Import common functions.
. ./common/punch
# Import common functions.
. ./common/filter
. ./common/reflink
+. ./common/attr
# real QA test starts here
_supported_fs generic
_require_scratch_reflink
_require_command "$GETCAP_PROG" getcap
_require_command "$SETCAP_PROG" setcap
+_require_attrs security
_scratch_mkfs >>$seqres.full 2>&1
_scratch_mount
# exposure bug uncovered by shared/010.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
# Import common functions.
. ./common/filter
DONOR1=$SCRATCH_MNT/a
TARGET=$SCRATCH_MNT/b
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
$XFS_IO_PROG -f -c "pwrite -S 0x72 0 $blksz" $DONOR1 >> $seqres.full
# - Check that nothing changes in either file
#
. ./common/preamble
-_begin_fstest auto quick dedupe clone
+_begin_fstest auto quick dedupe clone fiemap
# Override the default cleanup function.
_cleanup()
echo "Create the original files"
blksz=65536
+_require_congruent_file_oplen $TEST_DIR $blksz
_pwrite_byte 0x61 $((blksz * 2)) $((blksz * 6)) $testdir/file1 >> $seqres.full
_pwrite_byte 0x61 $((blksz * 2)) $((blksz * 6)) $testdir/file2 >> $seqres.full
_pwrite_byte 0x62 $(((blksz * 6) - 33)) 1 $testdir/file2 >> $seqres.full
# 79b3dbe4adb3 fs: fix iomap_bmap position calculation
#
. ./common/preamble
-_begin_fstest auto quick
+_begin_fstest auto quick fiemap
# Import common functions.
. ./common/filter
# Long-soak directio fsx test
#
. ./common/preamble
-_begin_fstest soak long_rw
+_begin_fstest soak long_rw smoketest
# Import common functions.
. ./common/filter
fsx_args+=(-t $min_dio_sz)
fsx_args+=(-w $min_dio_sz)
fsx_args+=(-Z)
+test -n "$SOAK_DURATION" && fsx_args+=(--duration="$SOAK_DURATION")
run_fsx "${fsx_args[@]}" | sed -e '/^fsx.*/d'
# Long-soak buffered fsx test
#
. ./common/preamble
-_begin_fstest soak long_rw
+_begin_fstest soak long_rw smoketest
# Import common functions.
. ./common/filter
fsx_args+=(-p $((nr_ops / 100)))
fsx_args+=(-o $op_sz)
fsx_args+=(-l $file_sz)
+test -n "$SOAK_DURATION" && fsx_args+=(--duration="$SOAK_DURATION")
run_fsx "${fsx_args[@]}" | sed -e '/^fsx.*/d'
_scratch_mkfs >>$seqres.full 2>&1
_require_metadata_journaling $SCRATCH_DEV
+
+if [ $FSTYP = "f2fs" ]; then
+ export MOUNT_OPTIONS="-o fsync_mode=strict $MOUNT_OPTIONS"
+fi
+
_init_flakey
_mount_flakey
_scratch_mkfs >>$seqres.full 2>&1
_require_metadata_journaling $SCRATCH_DEV
+
+if [ $FSTYP = "f2fs" ]; then
+ export MOUNT_OPTIONS="-o fsync_mode=strict $MOUNT_OPTIONS"
+fi
+
_init_flakey
_mount_flakey
# real QA test starts here
_supported_fs generic
_require_scratch
+_require_xfs_io_command "-T"
_require_test_program "t_open_tmpfiles"
_scratch_mkfs >> $seqres.full 2>&1
# - Check that the files are now different where we say they're different.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap prealloc
# Import common functions.
. ./common/filter
echo "Create the original files"
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
nr=64
filesize=$((blksz * nr))
_pwrite_byte 0x64 0 $((blksz * nr)) $testdir/file2 >> $seqres.full
# - Check that the files are now different where we say they're different.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap prealloc
# Import common functions.
. ./common/filter
echo "Create the original files"
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
nr=64
filesize=$((blksz * nr))
_pwrite_byte 0x64 0 $((blksz * nr)) $testdir/file2 >> $seqres.full
# - Check that the files are now different where we say they're different.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap prealloc
# Import common functions.
. ./common/filter
echo "Create the original files"
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
nr=64
filesize=$((blksz * nr))
_pwrite_byte 0x64 0 $((blksz * nr)) $testdir/file2 >> $seqres.full
# - Check that the files are now different where we say they're different.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap prealloc
# Import common functions.
. ./common/filter
echo "Create the original files"
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
nr=64
filesize=$((blksz * nr))
_pwrite_byte 0x64 0 $((blksz * nr)) $testdir/file2 >> $seqres.full
_scratch_mount >> $seqres.full 2>&1
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
nr=2
filesize=$((blksz * nr))
testdir=$SCRATCH_MNT/test-$seq
# All operations above should not fail.
#
. ./common/preamble
-_begin_fstest auto quick clone enospc log
+_begin_fstest auto quick clone enospc log prealloc
# Override the default cleanup function.
_cleanup()
_require_metadata_journaling $SCRATCH_DEV
_init_flakey
_mount_flakey
+_require_congruent_file_oplen $SCRATCH_MNT 4096
# Create preallocated extent where we can write into
$XFS_IO_PROG -f -c 'falloc 8k 64m' "$SCRATCH_MNT/foobar" >> $seqres.full
truncsize=$(((RANDOM * diosize + RANDOM % diosize) % max_io_size_b))
$AIO_TEST -t $truncsize $oper_list $localfile
- if [ $? -ne 0 ];then
+ ret=$?
+ if [ $ret -ne 0 ];then
echo "$AIO_TEST -t $truncsize $oper_list $localfile"
echo "==========^^ Fail ^^=========="
fi
+ return $ret
}
testimes=$((LOAD_FACTOR * 100))
while [ $testimes -gt 0 ]; do
echo > $localfile
- do_test
+ do_test || break
((testimes--))
done
local prefix=$3
local i=0
- while [ $i -lt $nr_file ]; do
+ for ((i = 0; i < nr_file; i++)); do
echo -n > $dir/${prefix}_${i}
- let i=$i+1
done
}
_scratch_mount
i=0
-free_inode=`_get_free_inode $SCRATCH_MNT`
-file_per_dir=1000
-loop=$((free_inode / file_per_dir + 1))
+free_inodes=$(_get_free_inode $SCRATCH_MNT)
+# Round the number of inodes to create up to the nearest 1000, like the old
+# code did to make sure that we *cannot* allocate any more inodes at all.
+free_inodes=$(( ( (free_inodes + 999) / 1000) * 1000 ))
+nr_cpus=$(( $($here/src/feature -o) * 4 * LOAD_FACTOR ))
+echo "free inodes: $free_inodes nr_cpus: $nr_cpus" >> $seqres.full
+
+if ((free_inodes <= nr_cpus)); then
+ nr_cpus=1
+ files_per_dir=$free_inodes
+else
+ files_per_dir=$(( (free_inodes + nr_cpus - 1) / nr_cpus ))
+fi
mkdir -p $SCRATCH_MNT/testdir
+echo "nr_cpus: $nr_cpus files_per_dir: $files_per_dir" >> $seqres.full
-echo "Create $((loop * file_per_dir)) files in $SCRATCH_MNT/testdir" >>$seqres.full
-while [ $i -lt $loop ]; do
- create_file $SCRATCH_MNT/testdir $file_per_dir $i >>$seqres.full 2>&1 &
- let i=$i+1
+echo "Create $((nr_cpus * files_per_dir)) files in $SCRATCH_MNT/testdir" >>$seqres.full
+for ((i = 0; i < nr_cpus; i++)); do
+ create_file $SCRATCH_MNT/testdir $files_per_dir $i >>$seqres.full 2>&1 &
done
wait
_require_xfs_io_command "falloc"
_require_test_program swapon
_require_scratch_swapfile
+_require_odirect
_scratch_mkfs > $seqres.full 2>&1
_scratch_mount >> $seqres.full 2>&1
_require_test_program swapon
_require_scratch_nocheck
_require_block_device $SCRATCH_DEV
+_require_odirect
# We cannot create swap on a zoned device because it can cause random write IOs
_require_non_zoned_device "$SCRATCH_DEV"
test -e /dev/snapshot && _notrun "userspace hibernation to swap is enabled"
# - conditions for enabling verity
# - verity files have correct contents and size
# - can't change contents of verity files, but can change metadata
-# - can retrieve a verity file's measurement via FS_IOC_MEASURE_VERITY
+# - can retrieve a verity file's digest via FS_IOC_MEASURE_VERITY
#
. ./common/preamble
_begin_fstest auto quick verity
md5sum $file > /dev/null
}
-verify_data_unreadable()
-{
- local file=$1
-
- # try both reading just the first data block, and reading until EOF
- head -c $FSV_BLOCK_SIZE $file 2>&1 >/dev/null | filter_output
- md5sum $file |& filter_output
-}
-
_fsv_scratch_begin_subtest "Enabling verity on file with verity already enabled fails with EEXIST"
_fsv_create_enable_file $fsv_file
echo "(trying again)"
_fsv_scratch_begin_subtest "Enabling verity can be interrupted"
dd if=/dev/zero of=$fsv_file bs=1 count=0 seek=$((1 << 34)) status=none
start_time=$(date +%s)
-$FSVERITY_PROG enable $fsv_file &
+$FSVERITY_PROG enable --block-size=$FSV_BLOCK_SIZE $fsv_file &
sleep 0.5
kill %1
wait
_fsv_scratch_begin_subtest "Enabling verity on file with verity already being enabled fails with EBUSY"
dd if=/dev/zero of=$fsv_file bs=1 count=0 seek=$((1 << 34)) status=none
start_time=$(date +%s)
-$FSVERITY_PROG enable $fsv_file &
+$FSVERITY_PROG enable --block-size=$FSV_BLOCK_SIZE $fsv_file &
sleep 0.5
_fsv_enable $fsv_file |& filter_output
kill %1
_fsv_scratch_begin_subtest "verity file can be measured"
_fsv_create_enable_file $fsv_file >> $seqres.full
-_fsv_measure $fsv_file
+_fsv_measure $fsv_file | _filter_fsverity_digest
_fsv_scratch_begin_subtest "verity file can be renamed"
_fsv_create_enable_file $fsv_file
# Test files <= 1 block in size. These are a bit of a special case since there
# are no hash blocks; the root hash is calculated directly over the data block.
+_fsv_scratch_begin_subtest "verity on small files"
for size in 1 $((FSV_BLOCK_SIZE - 1)) $FSV_BLOCK_SIZE; do
- _fsv_scratch_begin_subtest "verity on $size-byte file"
head -c $size /dev/urandom > $fsv_orig_file
cp $fsv_orig_file $fsv_file
_fsv_enable $fsv_file
rm -f $fsv_file
done
-_fsv_scratch_begin_subtest "verity on 100M file (multiple levels in hash tree)"
+_fsv_scratch_begin_subtest "verity on 100MB file (multiple levels in hash tree)"
head -c 100000000 /dev/urandom > $fsv_orig_file
cp $fsv_orig_file $fsv_file
_fsv_enable $fsv_file
# verity file can be read
# verity file can be measured
-sha256:be54121da3877f8852c65136d731784f134c4dd9d95071502e80d7be9f99b263
+sha256:<digest>
# verity file can be renamed
# Trying to measure non-verity file fails with ENODATA
ERROR: FS_IOC_MEASURE_VERITY failed on 'SCRATCH_MNT/file.fsv': No data available
-# verity on 1-byte file
+# verity on small files
Files matched
-
-# verity on 4095-byte file
Files matched
-
-# verity on 4096-byte file
Files matched
-# verity on 100M file (multiple levels in hash tree)
+# verity on 100MB file (multiple levels in hash tree)
Files matched
# verity on sparse file
_fsv_scratch_begin_subtest "FS_IOC_ENABLE_VERITY doesn't require root"
echo foo > $fsv_file
chmod 666 $fsv_file
-_user_do "$FSVERITY_PROG enable $fsv_file"
+_user_do "$FSVERITY_PROG enable --block-size=$FSV_BLOCK_SIZE $fsv_file"
_fsv_scratch_begin_subtest "FS_IOC_ENABLE_VERITY requires write access"
echo foo > $fsv_file >> $seqres.full
chmod 444 $fsv_file
-_user_do "$FSVERITY_PROG enable $fsv_file" |& _filter_scratch
+_user_do "$FSVERITY_PROG enable --block-size=$FSV_BLOCK_SIZE $fsv_file" |& _filter_scratch
_fsv_scratch_begin_subtest "FS_IOC_ENABLE_VERITY requires !append-only"
echo foo > $fsv_file >> $seqres.full
$CHATTR_PROG +a $fsv_file
-$FSVERITY_PROG enable $fsv_file |& _filter_scratch
+_fsv_enable $fsv_file |& _filter_scratch
$CHATTR_PROG -a $fsv_file
_fsv_scratch_begin_subtest "FS_IOC_ENABLE_VERITY requires !immutable"
echo foo > $fsv_file >> $seqres.full
$CHATTR_PROG +i $fsv_file
-$FSVERITY_PROG enable $fsv_file |& _filter_scratch
+_fsv_enable $fsv_file |& _filter_scratch
$CHATTR_PROG -i $fsv_file
_fsv_scratch_begin_subtest "FS_IOC_MEASURE_VERITY doesn't require root"
_supported_fs generic
_require_scratch_verity
_disable_fsverity_signatures
+_require_fsverity_corruption
_scratch_mkfs_verity &>> $seqres.full
_scratch_mount
setup_zeroed_file()
{
- local len=$1
- local sparse=$2
+ local block_size=$1
+ local file_len=$2
+ local sparse=$3
if $sparse; then
- dd if=/dev/zero of=$fsv_orig_file bs=1 count=0 seek=$len \
+ dd if=/dev/zero of=$fsv_orig_file bs=1 count=0 seek=$file_len \
status=none
else
- head -c $len /dev/zero > $fsv_orig_file
+ head -c $file_len /dev/zero > $fsv_orig_file
fi
cp $fsv_orig_file $fsv_file
- _fsv_enable $fsv_file
- md5sum $fsv_file |& _filter_scratch
-}
-
-filter_sigbus()
-{
- sed -e 's/.*Bus error.*/Bus error/'
+ _fsv_enable $fsv_file --block-size=$block_size
+ cmp $fsv_orig_file $fsv_file
}
round_up_to_page_boundary()
{
local n=$1
- local page_size=$(get_page_size)
+ local page_size=$(_get_page_size)
echo $(( (n + page_size - 1) & ~(page_size - 1) ))
}
+mread()
+{
+ local file=$1
+ local offset=$2
+ local length=$3
+ local map_len=$(round_up_to_page_boundary $(_get_filesize $file))
+
+ # Some callers expect xfs_io to crash with SIGBUS due to the mread,
+ # causing the shell to print "Bus error" to stderr. To allow this
+ # message to be redirected, execute xfs_io in a new shell instance.
+ # However, for this to work reliably, we also need to prevent the new
+ # shell instance from optimizing out the fork and directly exec'ing
+ # xfs_io. The easiest way to do that is to append 'true' to the
+ # commands, so that xfs_io is no longer the last command the shell sees.
+ # Don't let it write core files to the filesystem.
+ bash -c "trap '' SIGBUS; ulimit -c 0; $XFS_IO_PROG -r $file \
+ -c 'mmap -r 0 $map_len' \
+ -c 'mread -v $offset $length'; true"
+}
+
corruption_test()
{
- local file_len=$1
- local zap_offset=$2
- local zap_len=$3
- local is_merkle_tree=${4:-false} # if true, zap tree instead of data
- local use_sparse_file=${5:-false}
- local page_aligned_eof=$(round_up_to_page_boundary $file_len)
- local measurement
+ local block_size=$1
+ local file_len=$2
+ local zap_offset=$3
+ local zap_len=$4
+ local is_merkle_tree=${5:-false} # if true, zap tree instead of data
+ local use_sparse_file=${6:-false}
+
+ local paramstr="block_size=$block_size"
+ paramstr+=", file_len=$file_len"
+ paramstr+=", zap_offset=$zap_offset"
+ paramstr+=", zap_len=$zap_len"
+ paramstr+=", is_merkle_tree=$is_merkle_tree"
+ paramstr+=", use_sparse_file=$use_sparse_file"
if $is_merkle_tree; then
local corrupt_func=_fsv_scratch_corrupt_merkle_tree
else
local corrupt_func=_fsv_scratch_corrupt_bytes
fi
+ local fs_block_size=$(_get_block_size $SCRATCH_MNT)
- local msg="Corruption test:"
- msg+=" file_len=$file_len"
- if $use_sparse_file; then
- msg+=" (sparse)"
- fi
- msg+=" zap_offset=$zap_offset"
- if $is_merkle_tree; then
- msg+=" (in Merkle tree)"
- fi
- msg+=" zap_len=$zap_len"
+ rm -rf "${SCRATCH_MNT:?}"/*
+ setup_zeroed_file $block_size $file_len $use_sparse_file
- _fsv_scratch_begin_subtest "$msg"
- setup_zeroed_file $file_len $use_sparse_file
- cmp $fsv_file $fsv_orig_file
- echo "Corrupting bytes..."
+ # Corrupt part of the file (data or Merkle tree).
head -c $zap_len /dev/zero | tr '\0' X \
| $corrupt_func $fsv_file $zap_offset
- echo "Validating corruption (reading full file)..."
+ # Reading the full file with buffered I/O should fail.
_scratch_cycle_mount
- md5sum $fsv_file |& _filter_scratch
+ if cat $fsv_file >/dev/null 2>$tmp.err; then
+ echo "Unexpectedly was able to read full file ($paramstr)"
+ elif ! grep -q 'Input/output error' $tmp.err; then
+ echo "Wrong error reading full file ($paramstr):"
+ cat $tmp.err
+ fi
- echo "Validating corruption (direct I/O)..."
+ # Reading the full file with direct I/O should fail.
_scratch_cycle_mount
- dd if=$fsv_file bs=$FSV_BLOCK_SIZE iflag=direct status=none \
- of=/dev/null |& _filter_scratch
+ if dd if=$fsv_file bs=$fs_block_size iflag=direct status=none \
+ of=/dev/null 2>$tmp.err
+ then
+ echo "Unexpectedly was able to read full file with DIO ($paramstr)"
+ elif ! grep -q 'Input/output error' $tmp.err; then
+ echo "Wrong error reading full file with DIO ($paramstr):"
+ cat $tmp.err
+ fi
- if (( zap_offset < file_len )) && ! $is_merkle_tree; then
- echo "Validating corruption (reading just corrupted part)..."
- dd if=$fsv_file bs=1 skip=$zap_offset count=$zap_len \
- of=/dev/null status=none |& _filter_scratch
+ # Reading just the corrupted part of the file should fail.
+ if ! $is_merkle_tree; then
+ if dd if=$fsv_file bs=1 skip=$zap_offset count=$zap_len \
+ of=/dev/null status=none 2>$tmp.err; then
+ echo "Unexpectedly was able to read corrupted part ($paramstr)"
+ elif ! grep -q 'Input/output error' $tmp.err; then
+ echo "Wrong error reading corrupted part ($paramstr):"
+ cat $tmp.err
+ fi
fi
- echo "Validating corruption (reading full file via mmap)..."
- bash -c "trap '' SIGBUS; $XFS_IO_PROG -r $fsv_file \
- -c 'mmap -r 0 $page_aligned_eof' \
- -c 'mread 0 $file_len'" |& filter_sigbus
+ # Reading the full file via mmap should fail.
+ mread $fsv_file 0 $file_len >/dev/null 2>$tmp.err
+ if ! grep -q 'Bus error' $tmp.err; then
+ echo "Didn't see SIGBUS when reading file via mmap"
+ cat $tmp.err
+ fi
+ # Reading just the corrupted part via mmap should fail.
if ! $is_merkle_tree; then
- echo "Validating corruption (reading just corrupted part via mmap)..."
- bash -c "trap '' SIGBUS; $XFS_IO_PROG -r $fsv_file \
- -c 'mmap -r 0 $page_aligned_eof' \
- -c 'mread $zap_offset $zap_len'" |& filter_sigbus
+ mread $fsv_file $zap_offset $zap_len >/dev/null 2>$tmp.err
+ if ! grep -q 'Bus error' $tmp.err; then
+ echo "Didn't see SIGBUS when reading corrupted part via mmap"
+ cat $tmp.err
+ fi
fi
}
-# Note: these tests just overwrite some bytes without checking their original
-# values. Therefore, make sure to overwrite at least 5 or so bytes, to make it
-# nearly guaranteed that there will be a change -- even when the test file is
-# encrypted due to the test_dummy_encryption mount option being specified.
+# Reading the last block of the file with mmap is tricky, so we need to be
+# a bit careful. Some filesystems read the last block in full, while others
+# return zeros in the last block past EOF, regardless of the contents on
+# disk. In the former, corruption should be detected and result in SIGBUS,
+# while in the latter we would expect zeros past EOF, but no error.
+corrupt_eof_block_test()
+{
+ local block_size=$1
+ local file_len=$2
+ local zap_len=$3
+
+ rm -rf "${SCRATCH_MNT:?}"/*
+ setup_zeroed_file $block_size $file_len false
+ head -c $zap_len /dev/zero | tr '\0' X \
+ | _fsv_scratch_corrupt_bytes $fsv_file $file_len
+
+ mread $fsv_file $file_len $zap_len >$tmp.out 2>$tmp.err
-corruption_test 131072 0 5
-corruption_test 131072 4091 5
-corruption_test 131072 65536 65536
-corruption_test 131072 131067 5
+ head -c $file_len /dev/zero >$tmp.zeroes
+ mread $tmp.zeroes $file_len $zap_len >$tmp.zeroes_out
-# Non-zeroed bytes in the final partial block beyond EOF should cause reads to
-# fail too. Such bytes would be visible via mmap().
-corruption_test 130999 131000 72
+ grep -q 'Bus error' $tmp.err || diff $tmp.out $tmp.zeroes_out
+}
-# Merkle tree corruption.
-corruption_test 200000 100 10 true
+test_block_size()
+{
+ local block_size=$1
+
+ # Note: these tests just overwrite some bytes without checking their
+ # original values. Therefore, make sure to overwrite at least 5 or so
+ # bytes, to make it nearly guaranteed that there will be a change --
+ # even when the test file is encrypted due to the test_dummy_encryption
+ # mount option being specified.
+ corruption_test $block_size 131072 0 5
+ corruption_test $block_size 131072 4091 5
+ corruption_test $block_size 131072 65536 65536
+ corruption_test $block_size 131072 131067 5
+
+ corrupt_eof_block_test $block_size 130999 72
+
+ # Merkle tree corruption.
+ corruption_test $block_size 200000 100 10 true
+
+ # Sparse file. Corrupting the Merkle tree should still cause reads to
+ # fail, i.e. the filesystem must verify holes.
+ corruption_test $block_size 200000 100 10 true true
+}
-# Sparse file. Corrupting the Merkle tree should still cause reads to fail,
-# i.e. the filesystem must verify holes.
-corruption_test 200000 100 10 true true
+# Always test FSV_BLOCK_SIZE. Also test some other block sizes if they happen
+# to be supported.
+_fsv_scratch_begin_subtest "Testing block_size=FSV_BLOCK_SIZE"
+test_block_size $FSV_BLOCK_SIZE
+for block_size in 1024 4096 16384 65536; do
+ _fsv_scratch_begin_subtest "Testing block_size=$block_size if supported"
+ if (( block_size == FSV_BLOCK_SIZE )); then
+ continue # Skip redundant test case.
+ fi
+ if ! _fsv_can_enable $fsv_file --block-size=$block_size; then
+ echo "block_size=$block_size is unsupported" >> $seqres.full
+ continue
+ fi
+ test_block_size $block_size
+done
# success, all done
status=0
QA output created by 574
-# Corruption test: file_len=131072 zap_offset=0 zap_len=5
-0dfbe8aa4c20b52e1b8bf3cb6cbdf193 SCRATCH_MNT/file.fsv
-Corrupting bytes...
-Validating corruption (reading full file)...
-md5sum: SCRATCH_MNT/file.fsv: Input/output error
-Validating corruption (direct I/O)...
-dd: error reading 'SCRATCH_MNT/file.fsv': Input/output error
-Validating corruption (reading just corrupted part)...
-dd: error reading 'SCRATCH_MNT/file.fsv': Input/output error
-Validating corruption (reading full file via mmap)...
-Bus error
-Validating corruption (reading just corrupted part via mmap)...
-Bus error
+# Testing block_size=FSV_BLOCK_SIZE
-# Corruption test: file_len=131072 zap_offset=4091 zap_len=5
-0dfbe8aa4c20b52e1b8bf3cb6cbdf193 SCRATCH_MNT/file.fsv
-Corrupting bytes...
-Validating corruption (reading full file)...
-md5sum: SCRATCH_MNT/file.fsv: Input/output error
-Validating corruption (direct I/O)...
-dd: error reading 'SCRATCH_MNT/file.fsv': Input/output error
-Validating corruption (reading just corrupted part)...
-dd: error reading 'SCRATCH_MNT/file.fsv': Input/output error
-Validating corruption (reading full file via mmap)...
-Bus error
-Validating corruption (reading just corrupted part via mmap)...
-Bus error
+# Testing block_size=1024 if supported
-# Corruption test: file_len=131072 zap_offset=65536 zap_len=65536
-0dfbe8aa4c20b52e1b8bf3cb6cbdf193 SCRATCH_MNT/file.fsv
-Corrupting bytes...
-Validating corruption (reading full file)...
-md5sum: SCRATCH_MNT/file.fsv: Input/output error
-Validating corruption (direct I/O)...
-dd: error reading 'SCRATCH_MNT/file.fsv': Input/output error
-Validating corruption (reading just corrupted part)...
-dd: error reading 'SCRATCH_MNT/file.fsv': Input/output error
-Validating corruption (reading full file via mmap)...
-Bus error
-Validating corruption (reading just corrupted part via mmap)...
-Bus error
+# Testing block_size=4096 if supported
-# Corruption test: file_len=131072 zap_offset=131067 zap_len=5
-0dfbe8aa4c20b52e1b8bf3cb6cbdf193 SCRATCH_MNT/file.fsv
-Corrupting bytes...
-Validating corruption (reading full file)...
-md5sum: SCRATCH_MNT/file.fsv: Input/output error
-Validating corruption (direct I/O)...
-dd: error reading 'SCRATCH_MNT/file.fsv': Input/output error
-Validating corruption (reading just corrupted part)...
-dd: error reading 'SCRATCH_MNT/file.fsv': Input/output error
-Validating corruption (reading full file via mmap)...
-Bus error
-Validating corruption (reading just corrupted part via mmap)...
-Bus error
+# Testing block_size=16384 if supported
-# Corruption test: file_len=130999 zap_offset=131000 zap_len=72
-f5cca0d7fbb8b02bc6118a9954d5d306 SCRATCH_MNT/file.fsv
-Corrupting bytes...
-Validating corruption (reading full file)...
-md5sum: SCRATCH_MNT/file.fsv: Input/output error
-Validating corruption (direct I/O)...
-dd: error reading 'SCRATCH_MNT/file.fsv': Input/output error
-Validating corruption (reading full file via mmap)...
-Bus error
-Validating corruption (reading just corrupted part via mmap)...
-Bus error
-
-# Corruption test: file_len=200000 zap_offset=100 (in Merkle tree) zap_len=10
-4a1e4325031b13f933ac4f1db9ecb63f SCRATCH_MNT/file.fsv
-Corrupting bytes...
-Validating corruption (reading full file)...
-md5sum: SCRATCH_MNT/file.fsv: Input/output error
-Validating corruption (direct I/O)...
-dd: error reading 'SCRATCH_MNT/file.fsv': Input/output error
-Validating corruption (reading full file via mmap)...
-Bus error
-
-# Corruption test: file_len=200000 (sparse) zap_offset=100 (in Merkle tree) zap_len=10
-4a1e4325031b13f933ac4f1db9ecb63f SCRATCH_MNT/file.fsv
-Corrupting bytes...
-Validating corruption (reading full file)...
-md5sum: SCRATCH_MNT/file.fsv: Input/output error
-Validating corruption (direct I/O)...
-dd: error reading 'SCRATCH_MNT/file.fsv': Input/output error
-Validating corruption (reading full file via mmap)...
-Bus error
+# Testing block_size=65536 if supported
#
# FS QA Test generic/575
#
-# Test that fs-verity is using the correct measurement values. This test
+# Test that fs-verity is using the correct file digest values. This test
# verifies that fs-verity is doing its Merkle tree-based hashing correctly,
# i.e. that it hasn't been broken by a change.
#
# real QA test starts here
_supported_fs generic
_require_scratch_verity
-if [ $FSV_BLOCK_SIZE != 4096 ]; then
- _notrun "4096-byte verity block size not supported on this platform"
-fi
_disable_fsverity_signatures
_scratch_mkfs_verity &>> $seqres.full
fsv_orig_file=$SCRATCH_MNT/file
fsv_file=$SCRATCH_MNT/file.fsv
+# Try multiple hash algorithms.
algs=(sha256 sha512)
+# Try multiple Merkle tree block sizes.
+block_sizes=(1024 4096)
+
# Try files with 0, 1, and multiple Merkle tree levels.
file_sizes=(0 4096 65536 65536 100000000)
# Try both unsalted and salted, and check that empty salt is the same as no salt
salts=('' '' '' '--salt=' '--salt=f3c93fa6fb828c0e1587e5714ecf6f56')
-# The expected file measurements are here rather than in the expected output
-# file because not all hash algorithms may be available.
-sha256_vals=(
+# The expected file digests are here rather than in the expected output file
+# because the kernel might not support all hash algorithms and block sizes.
+sha256_vals_bsize1024=(
+sha256:f2cca36b9b1b7f07814e4284b10121809133e7cb9c4528c8f6846e85fc624ffa
+sha256:ea08590a4fe9c3d6c9dafe0eedacd9dffff8f24e24f1865ee3af132a495ab087
+sha256:527496288d703686e31092f5cca7e1306b2467f00b247ad01056ee5ec35a4bb9
+sha256:527496288d703686e31092f5cca7e1306b2467f00b247ad01056ee5ec35a4bb9
+sha256:087818b23312acb15dff9ff6e2b4f601406d08bb36013542444cc15248f47016
+)
+sha256_vals_bsize4096=(
sha256:3d248ca542a24fc62d1c43b916eae5016878e2533c88238480b26128a1f1af95
sha256:babc284ee4ffe7f449377fbf6692715b43aec7bc39c094a95878904d34bac97e
sha256:011e3f2b1dc89b75d78cddcc2a1b85cd8a64b2883e5f20f277ae4c0617e0404f
sha256:011e3f2b1dc89b75d78cddcc2a1b85cd8a64b2883e5f20f277ae4c0617e0404f
sha256:9d33cab743468fcbe4edab91a275b30dd543c12dd5e6ce6f2f737f66a1558f06
)
-sha512_vals=(
+sha512_vals_bsize1024=(
+sha512:8451664f25b2ad3f24391280e0c5681cb843389c180baa719f8fdfb063f5ddfa2d1c4433e55e2b6fbb3ba6aa2df8a4f41bf56cb7e0a3b617b6919a42c80f034c
+sha512:ab3c6444ab377bbe54c604c26cad241b146d85dc29727703a0d8134f70a8172fb3fa67d171355106b69cc0a9e7e9debb335f9461b3aba44093914867f7c73233
+sha512:e6a11353c24dd7b4603cb8ffa50a7041dbea7382d4698474ccbc2d8b34f3a83d8bf16df25c64ed31ee27213a8a3cbd001fb1ccde46384c23b81305c2393c1046
+sha512:e6a11353c24dd7b4603cb8ffa50a7041dbea7382d4698474ccbc2d8b34f3a83d8bf16df25c64ed31ee27213a8a3cbd001fb1ccde46384c23b81305c2393c1046
+sha512:517d573bd50d5f3787f5766c2ac60c7af854b0901b69757b4ef8dd70aa6b30fcc10d81629ce923bdd062a01c20fad0f063a081a2f3b0814ac06547b26bedc0d9
+)
+sha512_vals_bsize4096=(
sha512:ccf9e5aea1c2a64efa2f2354a6024b90dffde6bbc017825045dce374474e13d10adb9dadcc6ca8e17a3c075fbd31336e8f266ae6fa93a6c3bed66f9e784e5abf
sha512:928922686c4caf32175f5236a7f964e9925d10a74dc6d8344a8bd08b23c228ff5792573987d7895f628f39c4f4ebe39a7367d7aeb16aaa0cd324ac1d53664e61
sha512:eab7224ce374a0a4babcb2db25e24836247f38b87806ad9be9e5ba4daac2f5b814fc0cbdfd9f1f8499b3c9a6c1b38fe08974cce49883ab4ccd04462fd2f9507f
sha512:f7083a38644880d25539488313e9e5b41a4d431a0e383945129ad2c36e3c1d0f28928a424641bb1363c12b6e770578102566acea73baf1ce8ee15336f5ba2446
)
-test_alg()
+test_alg_with_block_size()
{
local alg=$1
- local -n vals=${alg}_vals
+ local block_size=$2
+ local -n vals=${alg}_vals_bsize${block_size}
local i
local file_size
local expected actual salt_arg
- _fsv_scratch_begin_subtest "Check for expected measurement values ($alg)"
+ _fsv_scratch_begin_subtest "Testing alg=$alg, block_size=$block_size if supported"
- if ! _fsv_have_hash_algorithm $alg $fsv_file; then
- if [ "$alg" = sha256 ]; then
- _fail "Something is wrong - sha256 hash should always be available"
+ if ! _fsv_can_enable $fsv_file --hash-alg=$alg --block-size=$block_size
+ then
+ # Since this is after _require_scratch_verity, sha256 with
+ # FSV_BLOCK_SIZE must be supported.
+ if [ "$alg" = "sha256" -a "$block_size" = "$FSV_BLOCK_SIZE" ]
+ then
+ _fail "Failed to enable verity with default params"
fi
+ # This combination of parameters is unsupported; skip it.
+ echo "alg=$alg, block_size=$block_size is unsupported" >> $seqres.full
return 0
fi
head -c $file_size /dev/zero > $fsv_orig_file
cp $fsv_orig_file $fsv_file
- _fsv_enable --hash-alg=$alg $salt_arg $fsv_file
+ _fsv_enable $fsv_file --hash-alg=$alg --block-size=$block_size \
+ $salt_arg
actual=$(_fsv_measure $fsv_file)
if [ "$actual" != "$expected" ]; then
echo "Mismatch: expected $expected, kernel calculated $actual (file_size=$file_size)"
}
for alg in ${algs[@]}; do
- test_alg $alg
+ for block_size in ${block_sizes[@]}; do
+ test_alg_with_block_size $alg $block_size
+ done
done
# success, all done
QA output created by 575
-# Check for expected measurement values (sha256)
+# Testing alg=sha256, block_size=1024 if supported
-# Check for expected measurement values (sha512)
+# Testing alg=sha256, block_size=4096 if supported
+
+# Testing alg=sha512, block_size=1024 if supported
+
+# Testing alg=sha512, block_size=4096 if supported
_require_scratch_verity
_require_scratch_encryption
_require_command "$KEYCTL_PROG" keyctl
+_require_fsverity_corruption
_disable_fsverity_signatures
_scratch_mkfs_encrypted_verity &>> $seqres.full
otherfile=$SCRATCH_MNT/otherfile
othersigfile=$tmp.othersig
+sign()
+{
+ _fsv_sign "$@" | _filter_scratch | _filter_fsverity_digest
+}
+
# Setup
echo -e "\n# Generating certificates and private keys"
echo -e "\n# Generating file and signing it for fs-verity"
head -c 100000 /dev/zero > $fsv_orig_file
for suffix in '' '.2'; do
- _fsv_sign $fsv_orig_file $sigfile$suffix --key=$keyfile$suffix \
- --cert=$certfile$suffix | _filter_scratch
+ sign $fsv_orig_file $sigfile$suffix --key=$keyfile$suffix \
+ --cert=$certfile$suffix
done
echo -e "\n# Signing a different file for fs-verity"
head -c 100000 /dev/zero | tr '\0' 'X' > $otherfile
-_fsv_sign $otherfile $othersigfile --key=$keyfile --cert=$certfile \
- | _filter_scratch
+sign $otherfile $othersigfile --key=$keyfile --cert=$certfile
# Actual tests
echo -e "\n# Testing salt"
reset_fsv_file
-_fsv_sign $fsv_orig_file $sigfile.salted --key=$keyfile --cert=$certfile \
- --salt=abcd | _filter_scratch
+sign $fsv_orig_file $sigfile.salted --key=$keyfile --cert=$certfile --salt=abcd
_fsv_enable $fsv_file --signature=$sigfile.salted --salt=abcd
cmp $fsv_file $fsv_orig_file
echo -e "\n# Testing non-default hash algorithm"
-if _fsv_have_hash_algorithm sha512 $fsv_file; then
+if _fsv_can_enable $fsv_file --hash-alg=sha512; then
reset_fsv_file
- _fsv_sign $fsv_orig_file $sigfile.sha512 --key=$keyfile \
- --cert=$certfile --hash-alg=sha512 > /dev/null
+ sign $fsv_orig_file $sigfile.sha512 --key=$keyfile --cert=$certfile \
+ --hash-alg=sha512 > /dev/null
_fsv_enable $fsv_file --signature=$sigfile.sha512 --hash-alg=sha512
cmp $fsv_file $fsv_orig_file
fi
echo -e "\n# Testing empty file"
+rm -f $fsv_file
echo -n > $fsv_file
-_fsv_sign $fsv_file $sigfile.emptyfile --key=$keyfile --cert=$certfile | \
- _filter_scratch
+sign $fsv_file $sigfile.emptyfile --key=$keyfile --cert=$certfile
_fsv_enable $fsv_file --signature=$sigfile.emptyfile
# success, all done
# Enabling fs.verity.require_signatures
# Generating file and signing it for fs-verity
-Signed file 'SCRATCH_MNT/file' (sha256:ecabbfca4efd69a721be824965da10d27900b109549f96687b35a4d91d810dac)
-Signed file 'SCRATCH_MNT/file' (sha256:ecabbfca4efd69a721be824965da10d27900b109549f96687b35a4d91d810dac)
+Signed file 'SCRATCH_MNT/file' (sha256:<digest>)
+Signed file 'SCRATCH_MNT/file' (sha256:<digest>)
# Signing a different file for fs-verity
-Signed file 'SCRATCH_MNT/otherfile' (sha256:b2a419c5a8c767a78c6275d6729794bf51e52ddf8713e31d12a93d61d961f49f)
+Signed file 'SCRATCH_MNT/otherfile' (sha256:<digest>)
# Enabling verity with valid signature (should succeed)
ERROR: FS_IOC_ENABLE_VERITY failed on 'SCRATCH_MNT/file.fsv': Bad message
# Testing salt
-Signed file 'SCRATCH_MNT/file' (sha256:1cb173bcd199133eb80e9ea4f0f741001b9e73227aa8812685156f2bc8ff45f5)
+Signed file 'SCRATCH_MNT/file' (sha256:<digest>)
# Testing non-default hash algorithm
# Testing empty file
-Signed file 'SCRATCH_MNT/file.fsv' (sha256:3d248ca542a24fc62d1c43b916eae5016878e2533c88238480b26128a1f1af95)
+Signed file 'SCRATCH_MNT/file.fsv' (sha256:<digest>)
# Make sure that we can handle multiple mmap writers to the same file.
. ./common/preamble
-_begin_fstest auto quick rw clone
+_begin_fstest auto quick rw clone fiemap
# Override the default cleanup function.
_cleanup()
_supported_fs generic
_require_test_program "mmap-write-concurrent"
_require_command "$FILEFRAG_PROG" filefrag
+_require_xfs_io_command "fiemap"
_require_test_reflink
_require_cp_reflink
echo "Create the original files"
blksz=65536
+_require_congruent_file_oplen $TEST_DIR $blksz
filesz=$((blksz * 4))
_pwrite_byte 0x61 0 $filesz $testdir/file1 >> $seqres.full
_cp_reflink $testdir/file1 $testdir/file2 >> $seqres.full
_require_quota
_require_xfs_io_command "falloc"
_require_scratch
+_require_odirect
cat > $tmp.awk << ENDL
{
_init_flakey
_mount_flakey
+_require_congruent_file_oplen $SCRATCH_MNT 65536
+
# Create our test file with two 256Kb extents, one at file offset 0 and the
# other at file offset 256Kb.
$XFS_IO_PROG -f -c "pwrite -S 0xa3 0 256K" \
{
local target=$1
- $FSSTRESS_PROG -n 50 -p 3 -d $target >/dev/null
+ $FSSTRESS_PROG -n 50 -p 3 -d $target >>$seqres.full
sync
}
_require_odirect
_require_test_program "splice-test"
-$here/src/splice-test -r $TEST_DIR/a
+diosize=`_min_dio_alignment $TEST_DEV`
+
+$here/src/splice-test -s $diosize -r $TEST_DIR/a
$here/src/splice-test -rd $TEST_DIR/a
-$here/src/splice-test $TEST_DIR/a
+$here/src/splice-test -s $diosize $TEST_DIR/a
$here/src/splice-test -d $TEST_DIR/a
# success, all done
QA output created by 591
concurrent reader with O_DIRECT
-concurrent reader with O_DIRECT
-concurrent reader without O_DIRECT
concurrent reader without O_DIRECT
sequential reader with O_DIRECT
sequential reader without O_DIRECT
_require_sysctl_variable fs.protected_symlinks
_require_sysctl_variable fs.protected_hardlinks
_require_user fsgqa2
+_require_group fsgqa2
# Do this SECOND so that qa_user is fsgqa, and _user_do uses that account
_require_user fsgqa
+_require_group fsgqa
_require_symlinks
OWNER=fsgqa2
_require_sysctl_variable fs.protected_regular
_require_sysctl_variable fs.protected_fifos
_require_user fsgqa2
+_require_group fsgqa2
# Do this SECOND so that qa_user is fsgqa, and _user_do uses that account
_require_user fsgqa
+_require_group fsgqa
_require_chmod
USER1=fsgqa2
_scratch_mkfs > /dev/null 2>&1
_scratch_mount
for i in $(seq 0 500); do
- $XFS_IO_PROG -c "pwrite 0 4K" $SCRATCH_MNT/$i >/dev/null 2>&1
+ $XFS_IO_PROG -f -c "pwrite 0 4K" $SCRATCH_MNT/$i >/dev/null
done
-_scratch_unmount &
-_scratch_mount
+# For overlayfs, avoid unmounting the base fs after _scratch_mount tries to
+# mount the base fs. Delay the mount attempt by a small amount in the hope
+# that the mount() call will try to lock s_umount /after/ umount has already
+# taken it.
+$UMOUNT_PROG $SCRATCH_MNT &
+sleep 0.01s ; _scratch_mount
wait
echo "Silence is golden"
# Test per-inode DAX flag by mmap direct/buffered IO.
#
. ./common/preamble
-_begin_fstest auto attr quick dax
+_begin_fstest auto attr quick dax prealloc
# Import common functions.
. ./common/filter
_supported_fs generic
+_require_hugepages
_require_scratch_dax_mountopt "dax=always"
_require_test_program "feature"
_require_test_program "t_mmap_dio"
# and the respective range return zeroes on subsequent reads.
#
. ./common/preamble
-_begin_fstest auto quick prealloc zero
+_begin_fstest auto quick prealloc zero punch
# Import common functions.
. ./common/filter
# real QA test starts here
_supported_fs generic
_require_scratch
+_require_scratch_delalloc
_scratch_mkfs >>$seqres.full 2>&1
_scratch_mount
stat_loop()
{
- trap "wait; exit" SIGTERM
local filepath=$1
local blocks
- while :; do
+ while [ -e "$loop_file" ]; do
blocks=$(stat -c %b $filepath)
if [ $blocks -eq 0 ]; then
echo "error: stat(2) reported zero blocks"
$XFS_IO_PROG -f -s -c "pwrite -b 64K 0 64K" $SCRATCH_MNT/foo > /dev/null
+loop_file=$tmp.loopfile
+touch $loop_file
stat_loop $SCRATCH_MNT/foo &
loop_pid=$!
$XFS_IO_PROG -d -c "pwrite -b 64K 0 64K" $SCRATCH_MNT/foo > /dev/null
done
+rm -f $loop_file
kill $loop_pid &> /dev/null
wait
# fsx ops to limit the testing time to be an auto group test.
#
. ./common/preamble
-_begin_fstest auto rw io_uring stress
+_begin_fstest auto rw io_uring stress soak
# Import common functions.
. ./common/filter
fsx_args+=(-p $((nr_ops / 100)))
fsx_args+=(-o $op_sz)
fsx_args+=(-l $file_sz)
+test -n "$SOAK_DURATION" && fsx_args+=(--duration="$SOAK_DURATION")
run_fsx "${fsx_args[@]}" | sed -e '/^fsx.*/d'
# fsx ops to limit the testing time to be an auto group test.
#
. ./common/preamble
-_begin_fstest auto rw io_uring stress
+_begin_fstest auto rw io_uring stress soak
# Import common functions.
. ./common/filter
fsx_args+=(-t $min_dio_sz)
fsx_args+=(-w $min_dio_sz)
fsx_args+=(-Z)
+test -n "$SOAK_DURATION" && fsx_args+=(--duration="$SOAK_DURATION")
run_fsx "${fsx_args[@]}" | sed -e '/^fsx.*/d'
# retrying")
#
. ./common/preamble
-_begin_fstest auto rw enospc
+_begin_fstest auto rw enospc prealloc
FS_SIZE=$((240*1024*1024)) # 240MB
DEBUG=1 # set to 0 to disable debug statements in shell and c-prog
. ./common/filter
_supported_fs generic
+_fixed_by_kernel_commit e4826691cc7e \
+ "xfs: restore shutdown check in mapped write fault path"
+
_require_scratch_nocheck
_require_scratch_shutdown
. ./common/preamble
_begin_fstest auto quick verity
+# Override the default cleanup function.
+_cleanup()
+{
+ cd /
+ _restore_fsverity_signatures
+ rm -f $tmp.*
+}
+
. ./common/filter
. ./common/verity
_supported_fs generic
_require_scratch_verity
_disable_fsverity_signatures
-# For the output of this test to always be the same, it has to use a specific
-# Merkle tree block size.
-if [ $FSV_BLOCK_SIZE != 4096 ]; then
- _notrun "4096-byte verity block size not supported on this platform"
-fi
+fsv_orig_file=$SCRATCH_MNT/file
+fsv_file=$SCRATCH_MNT/file.fsv
_scratch_mkfs_verity &>> $seqres.full
_scratch_mount
-
-echo -e "\n# Creating a verity file"
-fsv_file=$SCRATCH_MNT/file
-# Always use the same file contents, so that the output of the test is always
-# the same. Also use a file that is large enough to have multiple Merkle tree
-# levels, so that the test verifies that the blocks are returned in the expected
-# order. A 1 MB file with SHA-256 and a Merkle tree block size of 4096 will
-# have 3 Merkle tree blocks (3*4096 bytes): two at level 0 and one at level 1.
-head -c 1000000 /dev/zero > $fsv_file
-merkle_tree_size=$((3 * FSV_BLOCK_SIZE))
-fsverity_descriptor_size=256
-_fsv_enable $fsv_file --salt=abcd
+_fsv_create_enable_file $fsv_file
_require_fsverity_dump_metadata $fsv_file
-_fsv_measure $fsv_file
-echo -e "\n# Dumping Merkle tree"
-_fsv_dump_merkle_tree $fsv_file | sha256sum
+# Test FS_IOC_READ_VERITY_METADATA on a file that uses the given Merkle tree
+# block size.
+test_block_size()
+{
+ local block_size=$1
+ local digest_size=32 # assuming SHA-256
+ local i
+
+ # Create the file. Make the file size big enough to result in multiple
+ # Merkle tree levels being needed. The following expression computes a
+ # file size that needs 2 blocks at level 0, and thus 1 block at level 1.
+ local file_size=$((block_size * (2 * (block_size / digest_size))))
+ head -c $file_size /dev/zero > $fsv_orig_file
+ local tree_params=("--salt=abcd" "--block-size=$block_size")
+ cp $fsv_orig_file $fsv_file
+ _fsv_enable $fsv_file "${tree_params[@]}"
+
+ # Use the 'fsverity digest' command to compute the expected Merkle tree,
+ # descriptor, and file digest.
+ #
+ # Ideally we'd just hard-code expected values into the .out file and
+ # echo the actual values. That doesn't quite work here, since all these
+ # values depend on the Merkle tree block size, and the Merkle tree block
+ # sizes that are supported (and thus get tested here) vary. Therefore,
+ # we calculate the expected values in userspace with the help of
+ # 'fsverity digest', then do explicit comparisons with them. This works
+ # fine as long as fsverity-utils and the kernel don't get broken in the
+ # same way, in which case generic/575 should detect the problem anyway.
+ local expected_file_digest=$(_fsv_digest $fsv_orig_file \
+ "${tree_params[@]}" \
+ --out-merkle-tree=$tmp.merkle_tree.expected \
+ --out-descriptor=$tmp.descriptor.expected)
+ local merkle_tree_size=$(_get_filesize $tmp.merkle_tree.expected)
+ local descriptor_size=$(_get_filesize $tmp.descriptor.expected)
+
+ # 'fsverity measure' should return the expected file digest.
+ local actual_file_digest=$(_fsv_measure $fsv_file)
+ if [ "$actual_file_digest" != "$expected_file_digest" ]; then
+ echo "Measure returned $actual_file_digest but expected $expected_file_digest"
+ fi
+
+ # Test dumping the Merkle tree.
+ _fsv_dump_merkle_tree $fsv_file > $tmp.merkle_tree.actual
+ if ! cmp $tmp.merkle_tree.expected $tmp.merkle_tree.actual; then
+ echo "Dumped Merkle tree didn't match"
+ fi
+
+ # Test dumping the Merkle tree in chunks.
+ for (( i = 0; i < merkle_tree_size; i += 997 )); do
+ _fsv_dump_merkle_tree $fsv_file --offset=$i --length=997
+ done > $tmp.merkle_tree.actual
+ if ! cmp $tmp.merkle_tree.expected $tmp.merkle_tree.actual; then
+ echo "Dumped Merkle tree (in chunks) didn't match"
+ fi
-echo -e "\n# Dumping Merkle tree (in chunks)"
-# The above test may get the whole tree in one read, so also try reading it in
-# chunks.
-for (( i = 0; i < merkle_tree_size; i += 997 )); do
- _fsv_dump_merkle_tree $fsv_file --offset=$i --length=997
-done | sha256sum
+ # Test dumping the descriptor.
+ _fsv_dump_descriptor $fsv_file > $tmp.descriptor.actual
+ if ! cmp $tmp.descriptor.expected $tmp.descriptor.actual; then
+ echo "Dumped descriptor didn't match"
+ fi
-echo -e "\n# Dumping descriptor"
-# Note that the hash that is printed here should be the same hash that was
-# printed by _fsv_measure above.
-_fsv_dump_descriptor $fsv_file | sha256sum
+ # Test dumping the descriptor in chunks.
+ for (( i = 0; i < descriptor_size; i += 13 )); do
+ _fsv_dump_descriptor $fsv_file --offset=$i --length=13
+ done > $tmp.descriptor.actual
+ if ! cmp $tmp.descriptor.expected $tmp.descriptor.actual; then
+ echo "Dumped descriptor (in chunks) didn't match"
+ fi
+}
-echo -e "\n# Dumping descriptor (in chunks)"
-for (( i = 0; i < fsverity_descriptor_size; i += 13 )); do
- _fsv_dump_descriptor $fsv_file --offset=$i --length=13
-done | sha256sum
+# Always test FSV_BLOCK_SIZE. Also test some other block sizes if they happen
+# to be supported.
+_fsv_scratch_begin_subtest "Testing block_size=FSV_BLOCK_SIZE"
+test_block_size $FSV_BLOCK_SIZE
+for block_size in 1024 4096 16384 65536; do
+ _fsv_scratch_begin_subtest "Testing block_size=$block_size if supported"
+ if (( block_size == FSV_BLOCK_SIZE )); then
+ continue # Skip redundant test case.
+ fi
+ if ! _fsv_can_enable $fsv_file --block-size=$block_size; then
+ echo "block_size=$block_size is unsupported" >> $seqres.full
+ continue
+ fi
+ test_block_size $block_size
+done
# success, all done
status=0
QA output created by 624
-# Creating a verity file
-sha256:11e4f886bf2d70a6ef3a8b6ce8e8c62c9e5d3263208b9f120ae46791f124be73
+# Testing block_size=FSV_BLOCK_SIZE
-# Dumping Merkle tree
-db88cdad554734cd648a1bfbb5be7f86646c54397847aab0b3f42a28829fed17 -
+# Testing block_size=1024 if supported
-# Dumping Merkle tree (in chunks)
-db88cdad554734cd648a1bfbb5be7f86646c54397847aab0b3f42a28829fed17 -
+# Testing block_size=4096 if supported
-# Dumping descriptor
-11e4f886bf2d70a6ef3a8b6ce8e8c62c9e5d3263208b9f120ae46791f124be73 -
+# Testing block_size=16384 if supported
-# Dumping descriptor (in chunks)
-11e4f886bf2d70a6ef3a8b6ce8e8c62c9e5d3263208b9f120ae46791f124be73 -
+# Testing block_size=65536 if supported
# size < page size.
#
. ./common/preamble
-_begin_fstest auto aio rw stress
+_begin_fstest auto aio rw stress prealloc
fio_config=$tmp.fio
fio_out=$tmp.fio.out
# 5ffce3cc22a0 ("xfs: force the log after remapping a synchronous-writes file")
. ./common/preamble
-_begin_fstest auto quick rw clone
+_begin_fstest auto quick rw clone eio
# Override the default cleanup function.
_cleanup()
# 5ffce3cc22a0 ("xfs: force the log after remapping a synchronous-writes file")
. ./common/preamble
-_begin_fstest auto quick rw copy_range
+_begin_fstest auto quick rw copy_range eio
# Override the default cleanup function.
_cleanup()
_require_attrs trusted
_supported_fs ^overlay
_require_extra_fs overlay
+_fixed_by_kernel_commit 6da1b4b1ab36 \
+ "xfs: fix an ABBA deadlock in xfs_rename"
_scratch_mkfs >> $seqres.full
_scratch_mount
touch "$SCRATCH_MNT/swap"
$CHATTR_PROG +C "$SCRATCH_MNT/swap" >> $seqres.full 2>&1
chmod 0600 "$SCRATCH_MNT/swap"
-_pwrite_byte 0x61 0 $(get_page_size) "$SCRATCH_MNT/swap" >> $seqres.full
+_pwrite_byte 0x61 0 $(_get_page_size) "$SCRATCH_MNT/swap" >> $seqres.full
"$here/src/mkswap" "$SCRATCH_MNT/swap"
"$here/src/swapon" "$SCRATCH_MNT/swap"
swapoff "$SCRATCH_MNT/swap" >/dev/null 2>&1
# assignment to unsigned sis->pages in iomap_swapfile_activate").
#
. ./common/preamble
-_begin_fstest auto quick swap
+_begin_fstest auto quick swap collapse
# Import common functions
. ./common/filter
_scratch_mkfs >> $seqres.full 2>&1
_scratch_mount
-psize=`get_page_size`
+psize=`_get_page_size`
bsize=`_get_file_block_size $SCRATCH_MNT`
# Due to we need page-unaligned blocks, so blocksize < pagesize is necessary.
# If not, try to make a smaller enough block size
# bugs in the xattr code.
#
. ./common/preamble
-_begin_fstest auto soak attr long_rw stress
+_begin_fstest auto soak attr long_rw stress smoketest
_cleanup()
{
done
args+=('-f' "setfattr=20")
args+=('-f' "attr_set=60") # sets larger xattrs
+test -n "$SOAK_DURATION" && args+=(--duration="$SOAK_DURATION")
$FSSTRESS_PROG "${args[@]}" $FSSTRESS_AVOID -d $SCRATCH_MNT -n $nr_ops -p $nr_cpus >> $seqres.full
_begin_fstest auto quick recoveryloop shutdown
# real QA test starts here
+_supported_fs generic
+_fixed_by_kernel_commit 50d25484bebe \
+ "xfs: sync lazy sb accounting on quiesce of read-only mounts"
_require_scratch
_require_scratch_shutdown
fsstress=($FSSTRESS_PROG $FSSTRESS_AVOID -d "$loopmnt" -n 999999 -p "$((LOAD_FACTOR * 4))")
-for i in $(seq 1 $((25 * TIME_FACTOR)) ); do
+while _soak_loop_running $((25 * TIME_FACTOR)); do
touch $scratch_aliveflag
snap_loop_fs >> $seqres.full 2>&1 &
if ! _mount $loopimg $loopmnt -o loop; then
rm -f $scratch_aliveflag
_metadump_dev $loopimg $seqres.loop.$i.md
- _fail "iteration $i loopimg mount failed"
+ _fail "iteration $SOAK_LOOPIDX loopimg mount failed"
break
fi
done
if [ $is_unmounted -ne 0 ];then
cat $tmp.unmount.err
- _fail "iteration $i scratch unmount failed"
+ _fail "iteration $SOAK_LOOPIDX scratch unmount failed"
fi
_dmerror_load_working_table
if ! _dmerror_mount; then
_metadump_dev $DMERROR_DEV $seqres.scratch.$i.md
- _fail "iteration $i scratch mount failed"
+ _fail "iteration $SOAK_LOOPIDX scratch mount failed"
fi
done
# unshare a hole.
#
. ./common/preamble
-_begin_fstest auto clone unshare
+_begin_fstest auto clone unshare punch
# Override the default cleanup function.
_cleanup()
# Modify as appropriate.
_supported_fs generic
+_fixed_by_kernel_commit 72a048c1056a \
+ "xfs: only set IOMAP_F_SHARED when providing a srcmap to a write"
+
_require_cp_reflink
_require_test_reflink
_require_test_program "punch-alternating"
# hotplugging to shake out bugs in the write path.
#
. ./common/preamble
-_begin_fstest auto rw stress
+_begin_fstest auto rw stress soak
+
+[ "$FSTYP" = "xfs" ] && _fixed_by_kernel_commit ecd49f7a36fb \
+ "xfs: fix per-cpu CIL structure aggregation racing with dying cpus"
# Override the default cleanup function.
_cleanup()
touch $sentinel_file
exercise_cpu_hotplug &
+fsstress_args=(-w -d $stress_dir)
+
# Cap the number of fsstress threads at one per hotpluggable CPU if we exceed
# 1024 IO threads, per maintainer request.
nr_cpus=$((LOAD_FACTOR * nr_hotplug_cpus))
test "$nr_cpus" -gt 1024 && nr_cpus="$nr_hotplug_cpus"
+fsstress_args+=(-p $nr_cpus)
+if [ -n "$SOAK_DURATION" ]; then
+ test "$SOAK_DURATION" -lt 10 && SOAK_DURATION=10
+ fsstress_args+=(--duration="$((SOAK_DURATION / 10))")
+fi
+
+nr_ops=$((2500 * TIME_FACTOR))
+fsstress_args+=(-n $nr_ops)
+for ((i = 0; i < 10; i++)); do
+ $FSSTRESS_PROG $FSSTRESS_AVOID -w "${fsstress_args[@]}" >> $seqres.full
+ _test_cycle_mount
+done
-nr_ops=$((25000 * TIME_FACTOR))
-$FSSTRESS_PROG $FSSTRESS_AVOID -w -d $stress_dir -n $nr_ops -p $nr_cpus >> $seqres.full
rm -f $sentinel_file
# success, all done
# the golden output; we can only compare to a check file.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
# Import common functions.
. ./common/filter
# the golden output; we can only compare to a check file.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
# Import common functions.
. ./common/filter
# the golden output; we can only compare to a check file.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap prealloc
# Import common functions.
. ./common/filter
# the golden output; we can only compare to a check file.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap prealloc
# Import common functions.
. ./common/filter
# - Check that the files are now different where we say they're different.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
# Import common functions.
. ./common/filter
# - Check that the files are now different where we say they're different.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
# Import common functions.
. ./common/filter
# - Check that the files are now different where we say they're different.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
# Import common functions.
. ./common/filter
# - Check that the files are now different where we say they're different.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
# Import common functions.
. ./common/filter
# - Check that the files are now different where we say they're different.
#
. ./common/preamble
-_begin_fstest auto quick clone punch
+_begin_fstest auto quick clone punch prealloc
# Import common functions.
. ./common/filter
# - Check that the files are now different where we say they're different.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
# Import common functions.
. ./common/filter
# - Check that the files are now different where we say they're different.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
# Import common functions.
. ./common/filter
# - Check that the files are now different where we say they're different.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
# Import common functions.
. ./common/filter
# - Check that the files are now different where we say they're different.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
# Import common functions.
. ./common/filter
# - Check that the files are now different where we say they're different.
#
. ./common/preamble
-_begin_fstest auto quick clone punch
+_begin_fstest auto quick clone punch prealloc
# Import common functions.
. ./common/filter
# - Check that the files are now different where we say they're different.
#
. ./common/preamble
-_begin_fstest auto quick clone punch
+_begin_fstest auto quick clone punch prealloc
# Import common functions.
. ./common/filter
# - Check that the files are now different where we say they're different.
#
. ./common/preamble
-_begin_fstest auto quick clone punch
+_begin_fstest auto quick clone punch prealloc
# Import common functions.
. ./common/filter
# Different with generic/164,165, mread copies data from mmapped area
# one-byte-at-a-time, which may cause races during reflink_range().
# So the result of _mread_range() may be a mix of 61 and 62.
- egrep -v '((61|62) ){15}(61|62)'
+ grep -E -v '((61|62) ){15}(61|62)'
}
reader() {
# Functional test for dropping suid and sgid bits as part of a reflink.
#
. ./common/preamble
-_begin_fstest auto clone quick
+_begin_fstest auto clone quick perms
# Import common functions.
. ./common/filter
_scratch_mkfs >> $seqres.full
_scratch_mount
+_require_congruent_file_oplen $SCRATCH_MNT 1048576
chmod a+rw $SCRATCH_MNT/
setup_testfile() {
chmod a+rwxs $SCRATCH_MNT/a
commit_and_check
-#Commit to a non-exec file by an unprivileged user leaves sgid.
-echo "Test 9 - qa_user, non-exec file, only sgid"
-setup_testfile
-chmod a+rw,g+rws $SCRATCH_MNT/a
-commit_and_check "$qa_user"
-
#Commit to a group-exec file by an unprivileged user clears sgid
-echo "Test 10 - qa_user, group-exec file, only sgid"
+echo "Test 9 - qa_user, group-exec file, only sgid"
setup_testfile
chmod a+rw,g+rwxs $SCRATCH_MNT/a
commit_and_check "$qa_user"
-#Commit to a user-exec file by an unprivileged user clears sgid
-echo "Test 11 - qa_user, user-exec file, only sgid"
-setup_testfile
-chmod a+rw,u+x,g+rws $SCRATCH_MNT/a
-commit_and_check "$qa_user"
-
#Commit to a all-exec file by an unprivileged user clears sgid.
-echo "Test 12 - qa_user, all-exec file, only sgid"
+echo "Test 10 - qa_user, all-exec file, only sgid"
setup_testfile
chmod a+rwx,g+rwxs $SCRATCH_MNT/a
commit_and_check "$qa_user"
3784de23efab7a2074c9ec66901e39e5 SCRATCH_MNT/a
6777 -rwsrwsrwx SCRATCH_MNT/a
-Test 9 - qa_user, non-exec file, only sgid
-310f146ce52077fcd3308dcbe7632bb2 SCRATCH_MNT/a
-2666 -rw-rwSrw- SCRATCH_MNT/a
-3784de23efab7a2074c9ec66901e39e5 SCRATCH_MNT/a
-2666 -rw-rwSrw- SCRATCH_MNT/a
-
-Test 10 - qa_user, group-exec file, only sgid
+Test 9 - qa_user, group-exec file, only sgid
310f146ce52077fcd3308dcbe7632bb2 SCRATCH_MNT/a
2676 -rw-rwsrw- SCRATCH_MNT/a
3784de23efab7a2074c9ec66901e39e5 SCRATCH_MNT/a
676 -rw-rwxrw- SCRATCH_MNT/a
-Test 11 - qa_user, user-exec file, only sgid
-310f146ce52077fcd3308dcbe7632bb2 SCRATCH_MNT/a
-2766 -rwxrwSrw- SCRATCH_MNT/a
-3784de23efab7a2074c9ec66901e39e5 SCRATCH_MNT/a
-2766 -rwxrwSrw- SCRATCH_MNT/a
-
-Test 12 - qa_user, all-exec file, only sgid
+Test 10 - qa_user, all-exec file, only sgid
310f146ce52077fcd3308dcbe7632bb2 SCRATCH_MNT/a
2777 -rwxrwsrwx SCRATCH_MNT/a
3784de23efab7a2074c9ec66901e39e5 SCRATCH_MNT/a
# Functional test for dropping suid and sgid bits as part of a deduplication.
#
. ./common/preamble
-_begin_fstest auto clone quick
+_begin_fstest auto clone quick perms dedupe
# Import common functions.
. ./common/filter
_scratch_mkfs >> $seqres.full
_scratch_mount
+_require_congruent_file_oplen $SCRATCH_MNT 1048576
chmod a+rw $SCRATCH_MNT/
setup_testfile() {
# Import common functions.
. ./common/filter
. ./common/reflink
+. ./common/attr
# real QA test starts here
_require_command "$GETCAP_PROG" getcap
_require_command "$SETCAP_PROG" setcap
_require_scratch_reflink
+_require_attrs security
_scratch_mkfs >> $seqres.full
_scratch_mount
+_require_congruent_file_oplen $SCRATCH_MNT 1048576
chmod a+rw $SCRATCH_MNT/
setup_testfile() {
# after we mount the filesystem.
#
. ./common/preamble
-_begin_fstest auto quick log prealloc
+_begin_fstest auto quick log prealloc fiemap
_cleanup()
{
_init_flakey
_mount_flakey
+# The fiemap results in the golden output requires file allocations to align to
+# 1MB boundaries.
+_require_congruent_file_oplen $SCRATCH_MNT 1048576
+
# Create our test file with many extents.
# On btrfs this results in having multiple leaves of metadata full of file
# extent items, a condition necessary to trigger the original bug.
# space to allocate extents for the holes.
#
. ./common/preamble
-_begin_fstest auto quick prealloc
+_begin_fstest auto quick prealloc fiemap
. ./common/filter
. ./common/punch
_qmount_option usrquota
_qmount
-blocksize=$(_get_block_size $SCRATCH_MNT)
+blocksize=$(_get_dir_block_size $SCRATCH_MNT)
scratchdir=$SCRATCH_MNT/dir
scratchfile=$SCRATCH_MNT/file
mkdir $scratchdir
_qmount_option usrquota
_qmount
-blocksize=$(_get_block_size $SCRATCH_MNT)
+blocksize=$(_get_dir_block_size $SCRATCH_MNT)
scratchdir=$SCRATCH_MNT/dir
scratchfile=$SCRATCH_MNT/file
stagedir=$SCRATCH_MNT/staging
echo $(ls $scratchdir | wc -l) files in $scratchdir >> $seqres.full
ls -sld $scratchdir >> $seqres.full
+_filter_mv_output()
+{
+ sed -e "s,cannot move .* to \(.*\):\(.*\),cannot overwrite \1:\2,g" \
+ -e 's/y[0-9]*/yXXX/g'
+}
+
# Fail at renaming into the directory as qa_user to ensure quota enforcement
# works
chmod a+rwx $stagedir
name=$(printf "y%0254d" $i)
ln $scratchfile $stagedir/$name
su - "$qa_user" -c "mv $stagedir/$name $scratchdir/$name" 2>&1 | \
- _filter_scratch | sed -e 's/y[0-9]*/yXXX/g'
+ _filter_scratch | _filter_mv_output
test "${PIPESTATUS[0]}" -ne 0 && break
done
repquota -upn $SCRATCH_MNT >> $seqres.full
QA output created by 682
-mv: cannot move 'SCRATCH_MNT/staging/yXXX' to 'SCRATCH_MNT/dir/yXXX': Disk quota exceeded
+mv: cannot overwrite 'SCRATCH_MNT/dir/yXXX': Disk quota exceeded
Silence is golden
# Functional test for dropping suid and sgid bits as part of a fallocate.
#
. ./common/preamble
-_begin_fstest auto clone quick
+_begin_fstest auto clone quick perms prealloc
# Override the default cleanup function.
_cleanup()
_require_test
verb=falloc
_require_xfs_io_command $verb
+_require_congruent_file_oplen $TEST_DIR 65536
junk_dir=$TEST_DIR/$seq
junk_file=$junk_dir/a
chmod a+rwxs $junk_file
commit_and_check "" "$verb" 64k 64k
-# Commit to a non-exec file by an unprivileged user leaves sgid.
-echo "Test 9 - qa_user, non-exec file $verb, only sgid"
-setup_testfile
-chmod a+rw,g+rws $junk_file
-commit_and_check "$qa_user" "$verb" 64k 64k
-
# Commit to a group-exec file by an unprivileged user clears sgid
-echo "Test 10 - qa_user, group-exec file $verb, only sgid"
+echo "Test 9 - qa_user, group-exec file $verb, only sgid"
setup_testfile
chmod a+rw,g+rwxs $junk_file
commit_and_check "$qa_user" "$verb" 64k 64k
-# Commit to a user-exec file by an unprivileged user clears sgid
-echo "Test 11 - qa_user, user-exec file $verb, only sgid"
-setup_testfile
-chmod a+rw,u+x,g+rws $junk_file
-commit_and_check "$qa_user" "$verb" 64k 64k
-
# Commit to a all-exec file by an unprivileged user clears sgid.
-echo "Test 12 - qa_user, all-exec file $verb, only sgid"
+echo "Test 10 - qa_user, all-exec file $verb, only sgid"
setup_testfile
chmod a+rwx,g+rwxs $junk_file
commit_and_check "$qa_user" "$verb" 64k 64k
6777 -rwsrwsrwx TEST_DIR/683/a
6777 -rwsrwsrwx TEST_DIR/683/a
-Test 9 - qa_user, non-exec file falloc, only sgid
-2666 -rw-rwSrw- TEST_DIR/683/a
-2666 -rw-rwSrw- TEST_DIR/683/a
-
-Test 10 - qa_user, group-exec file falloc, only sgid
+Test 9 - qa_user, group-exec file falloc, only sgid
2676 -rw-rwsrw- TEST_DIR/683/a
676 -rw-rwxrw- TEST_DIR/683/a
-Test 11 - qa_user, user-exec file falloc, only sgid
-2766 -rwxrwSrw- TEST_DIR/683/a
-2766 -rwxrwSrw- TEST_DIR/683/a
-
-Test 12 - qa_user, all-exec file falloc, only sgid
+Test 10 - qa_user, all-exec file falloc, only sgid
2777 -rwxrwsrwx TEST_DIR/683/a
777 -rwxrwxrwx TEST_DIR/683/a
# Functional test for dropping suid and sgid bits as part of a fpunch.
#
. ./common/preamble
-_begin_fstest auto clone quick
+_begin_fstest auto clone quick perms punch
# Override the default cleanup function.
_cleanup()
_require_test
verb=fpunch
_require_xfs_io_command $verb
+_require_congruent_file_oplen $TEST_DIR 65536
junk_dir=$TEST_DIR/$seq
junk_file=$junk_dir/a
chmod a+rwxs $junk_file
commit_and_check "" "$verb" 64k 64k
-# Commit to a non-exec file by an unprivileged user leaves sgid.
-echo "Test 9 - qa_user, non-exec file $verb, only sgid"
-setup_testfile
-chmod a+rw,g+rws $junk_file
-commit_and_check "$qa_user" "$verb" 64k 64k
-
# Commit to a group-exec file by an unprivileged user clears sgid
-echo "Test 10 - qa_user, group-exec file $verb, only sgid"
+echo "Test 9 - qa_user, group-exec file $verb, only sgid"
setup_testfile
chmod a+rw,g+rwxs $junk_file
commit_and_check "$qa_user" "$verb" 64k 64k
-# Commit to a user-exec file by an unprivileged user clears sgid
-echo "Test 11 - qa_user, user-exec file $verb, only sgid"
-setup_testfile
-chmod a+rw,u+x,g+rws $junk_file
-commit_and_check "$qa_user" "$verb" 64k 64k
-
# Commit to a all-exec file by an unprivileged user clears sgid.
-echo "Test 12 - qa_user, all-exec file $verb, only sgid"
+echo "Test 10 - qa_user, all-exec file $verb, only sgid"
setup_testfile
chmod a+rwx,g+rwxs $junk_file
commit_and_check "$qa_user" "$verb" 64k 64k
6777 -rwsrwsrwx TEST_DIR/684/a
6777 -rwsrwsrwx TEST_DIR/684/a
-Test 9 - qa_user, non-exec file fpunch, only sgid
-2666 -rw-rwSrw- TEST_DIR/684/a
-2666 -rw-rwSrw- TEST_DIR/684/a
-
-Test 10 - qa_user, group-exec file fpunch, only sgid
+Test 9 - qa_user, group-exec file fpunch, only sgid
2676 -rw-rwsrw- TEST_DIR/684/a
676 -rw-rwxrw- TEST_DIR/684/a
-Test 11 - qa_user, user-exec file fpunch, only sgid
-2766 -rwxrwSrw- TEST_DIR/684/a
-2766 -rwxrwSrw- TEST_DIR/684/a
-
-Test 12 - qa_user, all-exec file fpunch, only sgid
+Test 10 - qa_user, all-exec file fpunch, only sgid
2777 -rwxrwsrwx TEST_DIR/684/a
777 -rwxrwxrwx TEST_DIR/684/a
# Functional test for dropping suid and sgid bits as part of a fzero.
#
. ./common/preamble
-_begin_fstest auto clone quick
+_begin_fstest auto clone quick perms zero
# Override the default cleanup function.
_cleanup()
_require_test
verb=fzero
_require_xfs_io_command $verb
+_require_congruent_file_oplen $TEST_DIR 65536
junk_dir=$TEST_DIR/$seq
junk_file=$junk_dir/a
chmod a+rwxs $junk_file
commit_and_check "" "$verb" 64k 64k
-# Commit to a non-exec file by an unprivileged user leaves sgid.
-echo "Test 9 - qa_user, non-exec file $verb, only sgid"
-setup_testfile
-chmod a+rw,g+rws $junk_file
-commit_and_check "$qa_user" "$verb" 64k 64k
-
# Commit to a group-exec file by an unprivileged user clears sgid
-echo "Test 10 - qa_user, group-exec file $verb, only sgid"
+echo "Test 9 - qa_user, group-exec file $verb, only sgid"
setup_testfile
chmod a+rw,g+rwxs $junk_file
commit_and_check "$qa_user" "$verb" 64k 64k
-# Commit to a user-exec file by an unprivileged user clears sgid
-echo "Test 11 - qa_user, user-exec file $verb, only sgid"
-setup_testfile
-chmod a+rw,u+x,g+rws $junk_file
-commit_and_check "$qa_user" "$verb" 64k 64k
-
# Commit to a all-exec file by an unprivileged user clears sgid.
-echo "Test 12 - qa_user, all-exec file $verb, only sgid"
+echo "Test 10 - qa_user, all-exec file $verb, only sgid"
setup_testfile
chmod a+rwx,g+rwxs $junk_file
commit_and_check "$qa_user" "$verb" 64k 64k
6777 -rwsrwsrwx TEST_DIR/685/a
6777 -rwsrwsrwx TEST_DIR/685/a
-Test 9 - qa_user, non-exec file fzero, only sgid
-2666 -rw-rwSrw- TEST_DIR/685/a
-2666 -rw-rwSrw- TEST_DIR/685/a
-
-Test 10 - qa_user, group-exec file fzero, only sgid
+Test 9 - qa_user, group-exec file fzero, only sgid
2676 -rw-rwsrw- TEST_DIR/685/a
676 -rw-rwxrw- TEST_DIR/685/a
-Test 11 - qa_user, user-exec file fzero, only sgid
-2766 -rwxrwSrw- TEST_DIR/685/a
-2766 -rwxrwSrw- TEST_DIR/685/a
-
-Test 12 - qa_user, all-exec file fzero, only sgid
+Test 10 - qa_user, all-exec file fzero, only sgid
2777 -rwxrwsrwx TEST_DIR/685/a
777 -rwxrwxrwx TEST_DIR/685/a
# Functional test for dropping suid and sgid bits as part of a finsert.
#
. ./common/preamble
-_begin_fstest auto clone quick
+_begin_fstest auto clone insert quick perms
# Override the default cleanup function.
_cleanup()
_require_test
verb=finsert
_require_xfs_io_command $verb
+_require_congruent_file_oplen $TEST_DIR 65536
junk_dir=$TEST_DIR/$seq
junk_file=$junk_dir/a
chmod a+rwxs $junk_file
commit_and_check "" "$verb" 64k 64k
-# Commit to a non-exec file by an unprivileged user leaves sgid.
-echo "Test 9 - qa_user, non-exec file $verb, only sgid"
-setup_testfile
-chmod a+rw,g+rws $junk_file
-commit_and_check "$qa_user" "$verb" 64k 64k
-
# Commit to a group-exec file by an unprivileged user clears sgid
-echo "Test 10 - qa_user, group-exec file $verb, only sgid"
+echo "Test 9 - qa_user, group-exec file $verb, only sgid"
setup_testfile
chmod a+rw,g+rwxs $junk_file
commit_and_check "$qa_user" "$verb" 64k 64k
-# Commit to a user-exec file by an unprivileged user clears sgid
-echo "Test 11 - qa_user, user-exec file $verb, only sgid"
-setup_testfile
-chmod a+rw,u+x,g+rws $junk_file
-commit_and_check "$qa_user" "$verb" 64k 64k
-
# Commit to a all-exec file by an unprivileged user clears sgid.
-echo "Test 12 - qa_user, all-exec file $verb, only sgid"
+echo "Test 10 - qa_user, all-exec file $verb, only sgid"
setup_testfile
chmod a+rwx,g+rwxs $junk_file
commit_and_check "$qa_user" "$verb" 64k 64k
6777 -rwsrwsrwx TEST_DIR/686/a
6777 -rwsrwsrwx TEST_DIR/686/a
-Test 9 - qa_user, non-exec file finsert, only sgid
-2666 -rw-rwSrw- TEST_DIR/686/a
-2666 -rw-rwSrw- TEST_DIR/686/a
-
-Test 10 - qa_user, group-exec file finsert, only sgid
+Test 9 - qa_user, group-exec file finsert, only sgid
2676 -rw-rwsrw- TEST_DIR/686/a
676 -rw-rwxrw- TEST_DIR/686/a
-Test 11 - qa_user, user-exec file finsert, only sgid
-2766 -rwxrwSrw- TEST_DIR/686/a
-2766 -rwxrwSrw- TEST_DIR/686/a
-
-Test 12 - qa_user, all-exec file finsert, only sgid
+Test 10 - qa_user, all-exec file finsert, only sgid
2777 -rwxrwsrwx TEST_DIR/686/a
777 -rwxrwxrwx TEST_DIR/686/a
# Functional test for dropping suid and sgid bits as part of a fcollapse.
#
. ./common/preamble
-_begin_fstest auto clone quick
+_begin_fstest auto clone quick perms collapse
# Override the default cleanup function.
_cleanup()
_require_test
verb=fcollapse
_require_xfs_io_command $verb
+_require_congruent_file_oplen $TEST_DIR 65536
junk_dir=$TEST_DIR/$seq
junk_file=$junk_dir/a
chmod a+rwxs $junk_file
commit_and_check "" "$verb" 64k 64k
-# Commit to a non-exec file by an unprivileged user leaves sgid.
-echo "Test 9 - qa_user, non-exec file $verb, only sgid"
-setup_testfile
-chmod a+rw,g+rws $junk_file
-commit_and_check "$qa_user" "$verb" 64k 64k
-
# Commit to a group-exec file by an unprivileged user clears sgid
-echo "Test 10 - qa_user, group-exec file $verb, only sgid"
+echo "Test 9 - qa_user, group-exec file $verb, only sgid"
setup_testfile
chmod a+rw,g+rwxs $junk_file
commit_and_check "$qa_user" "$verb" 64k 64k
-# Commit to a user-exec file by an unprivileged user clears sgid
-echo "Test 11 - qa_user, user-exec file $verb, only sgid"
-setup_testfile
-chmod a+rw,u+x,g+rws $junk_file
-commit_and_check "$qa_user" "$verb" 64k 64k
-
# Commit to a all-exec file by an unprivileged user clears sgid.
-echo "Test 12 - qa_user, all-exec file $verb, only sgid"
+echo "Test 10 - qa_user, all-exec file $verb, only sgid"
setup_testfile
chmod a+rwx,g+rwxs $junk_file
commit_and_check "$qa_user" "$verb" 64k 64k
6777 -rwsrwsrwx TEST_DIR/687/a
6777 -rwsrwsrwx TEST_DIR/687/a
-Test 9 - qa_user, non-exec file fcollapse, only sgid
-2666 -rw-rwSrw- TEST_DIR/687/a
-2666 -rw-rwSrw- TEST_DIR/687/a
-
-Test 10 - qa_user, group-exec file fcollapse, only sgid
+Test 9 - qa_user, group-exec file fcollapse, only sgid
2676 -rw-rwsrw- TEST_DIR/687/a
676 -rw-rwxrw- TEST_DIR/687/a
-Test 11 - qa_user, user-exec file fcollapse, only sgid
-2766 -rwxrwSrw- TEST_DIR/687/a
-2766 -rwxrwSrw- TEST_DIR/687/a
-
-Test 12 - qa_user, all-exec file fcollapse, only sgid
+Test 10 - qa_user, all-exec file fcollapse, only sgid
2777 -rwxrwsrwx TEST_DIR/687/a
777 -rwxrwxrwx TEST_DIR/687/a
# Import common functions.
. ./common/filter
+. ./common/attr
# real QA test starts here
_require_command "$SETCAP_PROG" setcap
_require_xfs_io_command falloc
_require_test
+_require_congruent_file_oplen $TEST_DIR 65536
+_require_attrs security
junk_dir=$TEST_DIR/$seq
junk_file=$junk_dir/a
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Meta, Inc. All Rights Reserved.
+#
+# FS QA Test 692
+#
+# fs-verity requires the filesystem to decide how it stores the Merkle tree,
+# which can be quite large.
+# It is convenient to treat the Merkle tree as past EOF, and ext4, f2fs, and
+# btrfs do so in at least some fashion. This leads to an edge case where a
+# large file can be under the file system file size limit, but trigger EFBIG
+# on enabling fs-verity. Test enabling verity on some large files to exercise
+# EFBIG logic for filesystems with fs-verity specific limits.
+#
+. ./common/preamble
+_begin_fstest auto quick verity
+
+# Override the default cleanup function.
+_cleanup()
+{
+ cd /
+ _restore_fsverity_signatures
+ rm -f $tmp.*
+}
+
+# Import common functions.
+. ./common/filter
+. ./common/verity
+
+# real QA test starts here
+_supported_fs generic
+_require_test
+_require_math
+_require_scratch_verity
+_require_fsverity_max_file_size_limit
+_disable_fsverity_signatures
+
+_scratch_mkfs_verity &>> $seqres.full
+_scratch_mount
+
+fsv_file=$SCRATCH_MNT/file.fsv
+
+max_sz=$(_get_max_file_size $SCRATCH_MNT)
+_fsv_scratch_begin_subtest "way too big: fail on first merkle block"
+truncate -s $max_sz $fsv_file
+_fsv_enable $fsv_file |& _filter_scratch
+
+# The goal of this second test is to make a big enough file that we trip the
+# EFBIG codepath, but not so big that we hit it immediately when writing the
+# first Merkle leaf.
+#
+# The Merkle tree is stored with the leaf node level (L0) last, but it is
+# written first. To get an interesting overflow, we need the maximum file size
+# ($max_sz) to be in the middle of L0 -- ideally near the beginning of L0 so
+# that we don't have to write many blocks before getting an error.
+
+bs=$FSV_BLOCK_SIZE
+hash_size=32 # SHA-256
+hashes_per_block=$(echo "scale=30; $bs/$hash_size" | $BC -q)
+
+# Compute the proportion of the original file size that the non-leaf levels of
+# the Merkle tree take up. Ignoring padding, this is 1/($hashes_per_block^2) +
+# 1/($hashes_per_block^3) + 1/($hashes_per_block^4) + ... Compute it using the
+# formula for the sum of a geometric series: \sum_{k=0}^{\infty} ar^k = a/(1-r).
+a=$(echo "scale=30; 1/($hashes_per_block^2)" | $BC -q)
+r=$(echo "scale=30; 1/$hashes_per_block" | $BC -q)
+nonleaves_relative_size=$(echo "scale=30; $a/(1-$r)" | $BC -q)
+
+# Compute the original file size where the leaf level L0 starts at $max_sz.
+# Padding is still ignored, so the real value is slightly smaller than this.
+sz=$(echo "$max_sz/(1+$nonleaves_relative_size)" | $BC -q)
+
+# Finally, to get a file size where $max_sz occurs just after the start of L0
+# rather than *at* the start of L0, subtract an overestimate of the padding: 64K
+# after the file contents, then $bs per level for 11 levels. 11 levels is the
+# most levels that can ever be needed, assuming the block size is at least 1K.
+sz=$(echo "$sz - 65536 - $bs*11" | $BC -q)
+
+_fsv_scratch_begin_subtest "still too big: fail on first invalid merkle block"
+truncate -s $sz $fsv_file
+_fsv_enable $fsv_file |& _filter_scratch
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 692
+
+# way too big: fail on first merkle block
+ERROR: FS_IOC_ENABLE_VERITY failed on 'SCRATCH_MNT/file.fsv': File too large
+
+# still too big: fail on first invalid merkle block
+ERROR: FS_IOC_ENABLE_VERITY failed on 'SCRATCH_MNT/file.fsv': File too large
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright 2022 Google LLC
+#
+# FS QA Test No. 693
+#
+# Verify ciphertext for v2 encryption policies that use AES-256-XTS to encrypt
+# file contents and AES-256-HCTR2 to encrypt file names.
+#
+# HCTR2 was introduced in kernel commit 6b2a51ff03bf ("fscrypt: Add HCTR2
+# support for filename encryption")
+#
+. ./common/preamble
+_begin_fstest auto quick encrypt
+
+# Import common functions.
+. ./common/filter
+. ./common/encrypt
+
+# real QA test starts here
+_supported_fs generic
+
+_verify_ciphertext_for_encryption_policy AES-256-XTS AES-256-HCTR2 v2
+_verify_ciphertext_for_encryption_policy AES-256-XTS AES-256-HCTR2 \
+ v2 iv_ino_lblk_32
+_verify_ciphertext_for_encryption_policy AES-256-XTS AES-256-HCTR2 \
+ v2 iv_ino_lblk_64
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 693
+
+Verifying ciphertext with parameters:
+ contents_encryption_mode: AES-256-XTS
+ filenames_encryption_mode: AES-256-HCTR2
+ options: v2
+
+Verifying ciphertext with parameters:
+ contents_encryption_mode: AES-256-XTS
+ filenames_encryption_mode: AES-256-HCTR2
+ options: v2 iv_ino_lblk_32
+
+Verifying ciphertext with parameters:
+ contents_encryption_mode: AES-256-XTS
+ filenames_encryption_mode: AES-256-HCTR2
+ options: v2 iv_ino_lblk_64
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Red Hat Inc. All Rights Reserved.
+#
+# FS QA Test 694
+#
+# Verify that i_blocks for files larger than 4 GiB have correct
+# values.
+#
+# This test verifies the problem fixed in kernel with commit
+# 0c336d6e33f4 exfat: fix incorrect loading of i_blocks for large files
+#
+. ./common/preamble
+_begin_fstest auto
+
+# Override the default cleanup function.
+_cleanup()
+{
+ cd /
+ rm -r -f $tmp.* $junk_dir
+}
+
+_supported_fs generic
+_fixed_by_kernel_commit 0c336d6e33f4 \
+ "exfat: fix incorrect loading of i_blocks for large file"
+
+_require_test
+_require_fs_space $TEST_DIR $((4 * 1024 * 1024)) #kB
+
+echo "Silence is golden"
+
+junk_dir=$TEST_DIR/$seq
+junk_file=$junk_dir/junk
+mkdir -p $junk_dir
+
+_create_file_sized 4G $junk_file
+if [ $? -ne 0 ]; then
+ echo "Could not create 4G test file"
+fi
+
+iblocks=`stat -c '%b' $junk_file`
+
+_test_cycle_mount
+
+iblocks_after_remount=`stat -c '%b' $junk_file`
+
+if [ "$iblocks" != "$iblocks_after_remount" ]; then
+ echo "Number of blocks needs to be same: $iblocks, $iblocks_after_remount"
+fi
+
+status=0
+
+exit
--- /dev/null
+QA output created by 694
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2022 SUSE Linux Products GmbH. All Rights Reserved.
+#
+# FS QA Test 695
+#
+# Test that if we punch a hole adjacent to an existing hole, fsync the file and
+# then power fail, the new hole exists after mounting again the filesystem.
+#
+# This is motivated by a regression on btrfs, fixed by the commit mentioned
+# below, when not using the no-holes feature (which is enabled by default since
+# btrfs-progs 5.15).
+#
+. ./common/preamble
+_begin_fstest auto quick log punch fiemap
+
+_cleanup()
+{
+ _cleanup_flakey
+ cd /
+ rm -r -f $tmp.*
+}
+
+. ./common/filter
+. ./common/dmflakey
+. ./common/punch
+
+_supported_fs generic
+_fixed_by_kernel_commit e6e3dec6c3c288 \
+ "btrfs: update generation of hole file extent item when merging holes"
+_require_scratch
+_require_dm_target flakey
+_require_xfs_io_command "fpunch"
+_require_xfs_io_command "fiemap"
+
+_scratch_mkfs >>$seqres.full 2>&1
+_require_metadata_journaling $SCRATCH_DEV
+_init_flakey
+_mount_flakey
+
+# We punch 2M holes and require extent allocations to align to 2M in fiemap
+# results.
+_require_congruent_file_oplen $SCRATCH_MNT $((2 * 1024 * 1024))
+
+# Create our test file with the following layout:
+#
+# [0, 2M) - hole
+# [2M, 10M) - extent
+# [10M, 12M) - hole
+$XFS_IO_PROG -f -c "truncate 12M" \
+ -c "pwrite -S 0xab 2M 8M" \
+ $SCRATCH_MNT/foobar | _filter_xfs_io
+
+# Persist everything, commit the filesystem's transaction.
+sync
+
+# Now punch two holes in the file:
+#
+# 1) For the range [2M, 4M), which is adjacent to the existing hole in the range
+# [0, 2M);
+# 2) For the range [8M, 10M), which is adjacent to the existing hole in the
+# range [10M, 12M).
+#
+# These operations start a new filesystem transaction.
+# Then finally fsync the file.
+$XFS_IO_PROG -c "fpunch 2M 2M" \
+ -c "fpunch 8M 2M" \
+ -c "fsync" $SCRATCH_MNT/foobar
+
+# Simulate a power failure and mount the filesystem to check that everything
+# is in the same state as before the power failure.
+_flakey_drop_and_remount
+
+# We expect the following file layout:
+#
+# [0, 4M) - hole
+# [4M, 8M) - extent
+# [8M, 12M) - hole
+echo "File layout after power failure:"
+$XFS_IO_PROG -c "fiemap -v" $SCRATCH_MNT/foobar | _filter_fiemap
+
+# When reading the file we expect to get the range [4M, 8M) filled with bytes
+# that have a value of 0xab and 0x00 for anything outside that range.
+echo "File content after power failure:"
+_hexdump $SCRATCH_MNT/foobar
+
+_unmount_flakey
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 695
+wrote 8388608/8388608 bytes at offset 2097152
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+File layout after power failure:
+0: [0..8191]: hole
+1: [8192..16383]: data
+2: [16384..24575]: hole
+File content after power failure:
+000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >................<
+*
+400000 ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab >................<
+*
+800000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >................<
+*
+c00000
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Fujitsu Limited. All Rights Reserved.
+#
+# FS QA Test No. 696
+#
+# Test S_ISGID stripping whether works correctly when call process
+# uses umask(S_IXGRP).
+#
+# It is also a regression test for
+# commit ac6800e279a2 ("fs: Add missing umask strip in vfs_tmpfile")
+# commit 1639a49ccdce ("fs: move S_ISGID stripping into the vfs_*() helpers")
+
+. ./common/preamble
+_begin_fstest auto quick cap idmapped mount perms rw unlink
+
+# Import common functions.
+. ./common/filter
+
+# real QA test starts here
+
+_supported_fs generic
+_require_test
+_require_scratch
+_fixed_by_kernel_commit ac6800e279a2 \
+ "fs: Add missing umask strip in vfs_tmpfile" \
+1639a49ccdce "fs: move S_ISGID stripping into the vfs_*() helpers"
+
+_scratch_mkfs >$seqres.full 2>&1
+
+$here/src/vfs/vfstest --test-setgid-create-umask \
+ --device "$TEST_DEV" --mount "$TEST_DIR" --fstype "$FSTYP"
+
+if [ "$FSTYP" != afs ]
+then
+ export MOUNT_OPTIONS="-o noacl $MOUNT_OPTIONS"
+fi
+
+# Also test S_ISGID stripping whether works correctly on underflying filesystem
+# that supports noacl feature.
+# noacl will earse acl flag in superblock, so kernel will use current_umask in
+# vfs directly instead of calling posix_acl_create on underflying filesystem.
+_try_scratch_mount >>$seqres.full 2>&1 && \
+ $here/src/vfs/vfstest --test-setgid-create-umask \
+ --device "$SCRATCH_DEV" --mount "$SCRATCH_MNT" --fstype "$FSTYP"
+
+echo "Silence is golden"
+status=0
+exit
--- /dev/null
+QA output created by 696
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Fujitsu Limited. All Rights Reserved.
+#
+# FS QA Test No. 697
+#
+# Test S_ISGID stripping whether works correctly when call process
+# uses posix acl.
+#
+# It is also a regression test for
+# commit 1639a49ccdce ("fs: move S_ISGID stripping into the vfs_*() helpers")
+
+. ./common/preamble
+_begin_fstest auto quick cap acl idmapped mount perms rw unlink
+
+# Import common functions.
+. ./common/filter
+. ./common/attr
+
+# real QA test starts here
+
+_supported_fs generic
+_require_test
+_require_acls
+_fixed_by_kernel_commit 1639a49ccdce \
+ "fs: move S_ISGID stripping into the vfs_*() helpers"
+
+$here/src/vfs/vfstest --test-setgid-create-acl \
+ --device "$TEST_DEV" --mount "$TEST_DIR" --fstype "$FSTYP"
+
+echo "Silence is golden"
+status=0
+exit
--- /dev/null
+QA output created by 697
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Christian Brauner (Microsoft). All Rights Reserved.
+#
+# FS QA Test 698
+#
+# Test that users can changed group ownership of a file they own to a group
+# they are a member of.
+#
+# Regression test for commit:
+# 168f91289340 ("fs: account for group membership")
+#
+. ./common/preamble
+_begin_fstest auto quick perms attr idmapped mount
+
+# Override the default cleanup function.
+_cleanup()
+{
+ cd /
+ $UMOUNT_PROG $SCRATCH_MNT/target-mnt 2>/dev/null
+ $UMOUNT_PROG $SCRATCH_MNT 2>/dev/null
+ rm -r -f $tmp.*
+}
+
+# real QA test starts here
+_supported_fs generic
+_fixed_by_kernel_commit 168f91289340 \
+ "fs: account for group membership"
+_require_scratch
+_require_chown
+_require_idmapped_mounts
+_require_test_program "vfs/mount-idmapped"
+_require_user fsgqa2
+_require_group fsgqa2
+# Do this SECOND so that qa_user is fsgqa, and _user_do uses that account
+_require_user fsgqa
+_require_group fsgqa
+
+user_foo=`id -u fsgqa`
+group_foo=`id -g fsgqa`
+user_bar=`id -u fsgqa2`
+group_bar=`id -g fsgqa2`
+
+setup_tree()
+{
+ mkdir -p $SCRATCH_MNT/source-mnt
+ chmod 0777 $SCRATCH_MNT/source-mnt
+ touch $SCRATCH_MNT/source-mnt/file1
+ chown 65534:65534 $SCRATCH_MNT
+ chown 65534:65534 $SCRATCH_MNT/source-mnt
+ chown 65534:65535 $SCRATCH_MNT/source-mnt/file1
+
+ mkdir -p $SCRATCH_MNT/target-mnt
+ chmod 0777 $SCRATCH_MNT/target-mnt
+}
+
+# Setup an idmapped mount where uid and gid 65534 are mapped to fsgqa and uid
+# and gid 65535 are mapped to fsgqa2.
+setup_idmapped_mnt()
+{
+ $here/src/vfs/mount-idmapped \
+ --map-mount=u:65534:$user_foo:1 \
+ --map-mount=g:65534:$group_foo:1 \
+ --map-mount=u:65535:$user_bar:1 \
+ --map-mount=g:65535:$group_bar:1 \
+ $SCRATCH_MNT/source-mnt $SCRATCH_MNT/target-mnt
+}
+
+# We've created a layout where fsgqa owns the target file but the group of the
+# target file is owned by another group. We now test that user fsgqa can change
+# the group ownership of the file to a group they control. In this case to the
+# fsgqa group.
+change_group_ownership()
+{
+ local path="$1"
+
+ stat -c '%U:%G' $path
+ _user_do "id -u --name; id -g --name; chgrp $group_foo $path"
+ stat -c '%U:%G' $path
+ _user_do "id -u --name; id -g --name; chgrp $group_bar $path > /dev/null 2>&1"
+ stat -c '%U:%G' $path
+}
+
+run_base_test()
+{
+ mkdir -p $SCRATCH_MNT/source-mnt
+ chmod 0777 $SCRATCH_MNT/source-mnt
+ touch $SCRATCH_MNT/source-mnt/file1
+ chown $user_foo:$group_foo $SCRATCH_MNT
+ chown $user_foo:$group_foo $SCRATCH_MNT/source-mnt
+ chown $user_foo:$group_bar $SCRATCH_MNT/source-mnt/file1
+
+ echo ""
+ echo "base test"
+ change_group_ownership "$SCRATCH_MNT/source-mnt/file1"
+ rm -rf "$SCRATCH_MNT/source-mnt"
+}
+
+# Basic test as explained in the comment for change_group_ownership().
+run_idmapped_test()
+{
+ echo ""
+ echo "base idmapped test"
+ change_group_ownership "$SCRATCH_MNT/target-mnt/file1"
+}
+
+_scratch_mkfs >> $seqres.full
+_scratch_mount
+
+run_base_test
+setup_tree
+setup_idmapped_mnt
+run_idmapped_test
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 698
+
+base test
+fsgqa:fsgqa2
+fsgqa
+fsgqa
+fsgqa:fsgqa
+fsgqa
+fsgqa
+fsgqa:fsgqa
+
+base idmapped test
+fsgqa:fsgqa2
+fsgqa
+fsgqa
+fsgqa:fsgqa
+fsgqa
+fsgqa
+fsgqa:fsgqa
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Christian Brauner (Microsoft). All Rights Reserved.
+#
+# FS QA Test 698
+#
+# This's copied from generic/698, extend it to test overlayfs on top of idmapped
+# mounts specifically.
+#
+. ./common/preamble
+. ./common/overlay
+_begin_fstest auto quick perms attr idmapped mount
+
+# Override the default cleanup function.
+_cleanup()
+{
+ cd /
+ $UMOUNT_PROG $SCRATCH_MNT/target-mnt
+ $UMOUNT_PROG $SCRATCH_MNT/ovl-merge 2>/dev/null
+ $UMOUNT_PROG $SCRATCH_MNT 2>/dev/null
+ rm -r -f $tmp.*
+}
+
+# real QA test starts here
+_supported_fs ^overlay
+_require_extra_fs overlay
+_require_scratch
+_require_chown
+_require_idmapped_mounts
+_require_test_program "vfs/mount-idmapped"
+_require_user fsgqa2
+_require_group fsgqa2
+# Do this SECOND so that qa_user is fsgqa, and _user_do uses that account
+_require_user fsgqa
+_require_group fsgqa
+
+user_foo=`id -u fsgqa`
+group_foo=`id -g fsgqa`
+user_bar=`id -u fsgqa2`
+group_bar=`id -g fsgqa2`
+
+setup_tree()
+{
+ mkdir -p $SCRATCH_MNT/source-mnt
+ chmod 0777 $SCRATCH_MNT/source-mnt
+ touch $SCRATCH_MNT/source-mnt/file1
+ chown 65534:65534 $SCRATCH_MNT
+ chown 65534:65534 $SCRATCH_MNT/source-mnt
+ chown 65534:65535 $SCRATCH_MNT/source-mnt/file1
+
+ mkdir -p $SCRATCH_MNT/target-mnt
+ chmod 0777 $SCRATCH_MNT/target-mnt
+}
+
+# Setup an idmapped mount where uid and gid 65534 are mapped to fsgqa and uid
+# and gid 65535 are mapped to fsgqa2.
+setup_idmapped_mnt()
+{
+ $here/src/vfs/mount-idmapped \
+ --map-mount=u:65534:$user_foo:1 \
+ --map-mount=g:65534:$group_foo:1 \
+ --map-mount=u:65535:$user_bar:1 \
+ --map-mount=g:65535:$group_bar:1 \
+ $SCRATCH_MNT/source-mnt $SCRATCH_MNT/target-mnt
+}
+
+# We've created a layout where fsgqa owns the target file but the group of the
+# target file is owned by another group. We now test that user fsgqa can change
+# the group ownership of the file to a group they control. In this case to the
+# fsgqa group.
+change_group_ownership()
+{
+ local path="$1"
+
+ stat -c '%U:%G' $path
+ _user_do "id -u --name; id -g --name; chgrp $group_foo $path"
+ stat -c '%U:%G' $path
+ _user_do "id -u --name; id -g --name; chgrp $group_bar $path > /dev/null 2>&1"
+ stat -c '%U:%G' $path
+}
+
+lower="$SCRATCH_MNT/target-mnt"
+upper="$SCRATCH_MNT/ovl-upper"
+work="$SCRATCH_MNT/ovl-work"
+merge="$SCRATCH_MNT/ovl-merge"
+
+reset_ownership()
+{
+ local path="$SCRATCH_MNT/source-mnt/file1"
+
+ echo ""
+ echo "reset ownership"
+ chown 65534:65534 $path
+ stat -c '%u:%g' $path
+ chown 65534:65535 $path
+ stat -c '%u:%g' $path
+}
+
+# Prepare overlayfs with metacopy turned off.
+setup_overlayfs_idmapped_lower_metacopy_off()
+{
+ mkdir -p $upper $work $merge
+ _overlay_mount_dirs $lower $upper $work \
+ overlay $merge -ometacopy=off || \
+ _notrun "overlayfs doesn't support idmappped layers"
+}
+
+# Prepare overlayfs with metacopy turned on.
+setup_overlayfs_idmapped_lower_metacopy_on()
+{
+ mkdir -p $upper $work $merge
+ _overlay_mount_dirs $lower $upper $work overlay $merge -ometacopy=on
+}
+
+reset_overlayfs()
+{
+ $UMOUNT_PROG $SCRATCH_MNT/ovl-merge 2>/dev/null
+ rm -rf $upper $work $merge
+}
+
+# Overlayfs can be mounted on top of idmapped layers. Make sure that the basic
+# test explained in the comment for change_group_ownership() passes with
+# overlayfs mounted on top of it.
+# This tests overlayfs with metacopy turned off, i.e., changing a file copies
+# up data and metadata.
+run_overlayfs_idmapped_lower_metacopy_off()
+{
+ echo ""
+ echo "overlayfs idmapped lower metacopy off"
+ change_group_ownership "$SCRATCH_MNT/ovl-merge/file1"
+ reset_overlayfs
+ reset_ownership
+}
+
+# Overlayfs can be mounted on top of idmapped layers. Make sure that the basic
+# test explained in the comment for change_group_ownership() passes with
+# overlayfs mounted on top of it.
+# This tests overlayfs with metacopy turned on, i.e., changing a file tries to
+# only copy up metadata.
+run_overlayfs_idmapped_lower_metacopy_on()
+{
+ echo ""
+ echo "overlayfs idmapped lower metacopy on"
+ change_group_ownership "$SCRATCH_MNT/ovl-merge/file1"
+ reset_overlayfs
+ reset_ownership
+}
+
+_scratch_mkfs >> $seqres.full
+_scratch_mount
+_supports_filetype $SCRATCH_MNT || _notrun "overlayfs test requires d_type"
+
+setup_tree
+setup_idmapped_mnt
+setup_overlayfs_idmapped_lower_metacopy_off
+run_overlayfs_idmapped_lower_metacopy_off
+
+setup_overlayfs_idmapped_lower_metacopy_on
+run_overlayfs_idmapped_lower_metacopy_on
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 699
+
+overlayfs idmapped lower metacopy off
+fsgqa:fsgqa2
+fsgqa
+fsgqa
+fsgqa:fsgqa
+fsgqa
+fsgqa
+fsgqa:fsgqa
+
+reset ownership
+65534:65534
+65534:65535
+
+overlayfs idmapped lower metacopy on
+fsgqa:fsgqa2
+fsgqa
+fsgqa
+fsgqa:fsgqa
+fsgqa
+fsgqa
+fsgqa:fsgqa
+
+reset ownership
+65534:65534
+65534:65535
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Red Hat, Copyright. All Rights Reserved.
+#
+# FS QA Test No. 700
+#
+# Verify selinux label can be kept after RENAME_WHITEOUT. This is
+# a regression test for:
+# 70b589a37e1a ("xfs: add selinux labels to whiteout inodes")
+#
+. ./common/preamble
+_begin_fstest auto quick rename attr whiteout
+
+# Import common functions.
+. ./common/attr
+. ./common/renameat2
+
+# real QA test starts here
+_supported_fs generic
+_require_scratch
+_require_attrs
+_require_renameat2 whiteout
+
+_fixed_by_kernel_commit 70b589a37e1a \
+ xfs: add selinux labels to whiteout inodes
+
+get_selinux_label()
+{
+ local label
+
+ label=$(_getfattr --absolute-names -n security.selinux $@ | sed -n 's/security.selinux=\"\(.*\)\"/\1/p')
+ if [ ${PIPESTATUS[0]} -ne 0 -o -z "$label" ];then
+ _fail "Fail to get selinux label: $label"
+ fi
+ echo $label
+}
+
+_scratch_mkfs >> $seqres.full 2>&1
+# SELINUX_MOUNT_OPTIONS will be set in common/config if selinux is enabled
+if [ -z "$SELINUX_MOUNT_OPTIONS" ]; then
+ _notrun "Require selinux to be enabled"
+fi
+# This test need to verify selinux labels in objects, so unset this selinux
+# mount option
+export SELINUX_MOUNT_OPTIONS=""
+_scratch_mount
+
+touch $SCRATCH_MNT/f1
+echo "Before RENAME_WHITEOUT" >> $seqres.full
+ls -lZ $SCRATCH_MNT >> $seqres.full 2>&1
+# Expect f1 and f2 have same label after RENAME_WHITEOUT
+$here/src/renameat2 -w $SCRATCH_MNT/f1 $SCRATCH_MNT/f2
+echo "After RENAME_WHITEOUT" >> $seqres.full
+ls -lZ $SCRATCH_MNT >> $seqres.full 2>&1
+label1=$(get_selinux_label $SCRATCH_MNT/f1)
+label2=$(get_selinux_label $SCRATCH_MNT/f2)
+if [ "$label1" != "$label2" ];then
+ echo "$label1 != $label2"
+fi
+
+echo "Silence is golden"
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 700
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Red Hat Inc. All Rights Reserved.
+#
+# FS QA Test No. 701
+#
+# Verify that i_blocks for truncated files larger than 4 GiB have correct
+# values.
+#
+# This test verifies the problem fixed in kernel with commit
+# 92fba084b79e exfat: fix i_blocks for files truncated over 4 GiB
+#
+. ./common/preamble
+. ./common/filter
+
+_begin_fstest auto
+
+# Override the default cleanup function.
+_cleanup()
+{
+ cd /
+ rm -r -f $tmp.* $junk_dir
+}
+
+_supported_fs generic
+_fixed_by_kernel_commit 92fba084b79e \
+ "exfat: fix i_blocks for files truncated over 4 GiB"
+
+_require_test
+_require_fs_space $TEST_DIR $((5 * 1024 * 1024)) #kB
+
+junk_dir=$TEST_DIR/$seq
+junk_file=$junk_dir/junk
+mkdir -p $junk_dir
+
+_create_file_sized 5G $junk_file
+if [ $? -ne 0 ]; then
+ echo "Could not create 5G test file"
+fi
+
+truncate -s 4G $junk_file
+
+block_size=`stat -c '%B' $junk_file`
+iblocks_after_truncate=`stat -c '%b' $junk_file`
+iblocks_expected=$((4 * 1024 * 1024 * 1024 / $block_size))
+
+_within_tolerance "Number of allocated blocks after truncate" $iblocks_after_truncate $iblocks_expected 1% -v
+
+status=0
+
+exit
--- /dev/null
+QA output created by 701
+Number of allocated blocks after truncate is in range
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2022 SUSE Linux Products GmbH. All Rights Reserved.
+#
+# FS QA Test 702
+#
+# Test that if we have two consecutive extents and only one of them is cloned,
+# then fiemap correctly reports which one is shared and reports the other as not
+# shared.
+#
+. ./common/preamble
+_begin_fstest auto quick clone fiemap
+
+. ./common/filter
+. ./common/reflink
+
+_fixed_by_kernel_commit ac3c0d36a2a2f7 \
+ "btrfs: make fiemap more efficient and accurate reporting extent sharedness"
+
+_supported_fs generic
+_require_scratch_reflink
+_require_xfs_io_command "fiemap"
+
+fiemap_test_file()
+{
+ local filepath=$1
+
+ # Skip the first two lines of xfs_io's fiemap output (file path and
+ # header describing the output columns).
+ #
+ # Print the first column (extent number), second column (file range),
+ # fourth column (extent size) and fifth column (flags) of the fiemap
+ # output.
+ #
+ # We filter the flags column to only tell us if an extent is shared or
+ # not (flag 0x2000, which matches FIEMAP_EXTENT_SHARED) because on some
+ # filesystem configs we may have other flags printed - for example
+ # running btrfs with "-o compress" we get the flag 0x8 as well (which
+ # is FIEMAP_EXTENT_ENCODED).
+ #
+ # The third column is the physical location of the extents, so it's
+ # omitted because the location varies between different filesystems.
+ #
+ $XFS_IO_PROG -c "fiemap -v" $filepath | tail -n +3 | \
+ $AWK_PROG '{ print $1, $2, $4, \
+ and(strtonum($5), 0x2000) ? "shared" : "not_shared" }'
+}
+
+_scratch_mkfs >> $seqres.full
+_scratch_mount
+
+# We create 128K extents in the test files below.
+_require_congruent_file_oplen $SCRATCH_MNT $((128 * 1024))
+
+# Create file foo with 2 consecutive extents, each one with a size of 128K.
+echo "Creating file foo"
+$XFS_IO_PROG -f -c "pwrite -b 128K 0 128K" -c "fsync" \
+ -c "pwrite -b 128K 128K 128K" -c "fsync" \
+ $SCRATCH_MNT/foo | _filter_xfs_io
+
+# Clone only the first extent into another file.
+echo "Cloning first extent of file foo to file bar"
+$XFS_IO_PROG -f -c "reflink $SCRATCH_MNT/foo 0 0 128K" $SCRATCH_MNT/bar | \
+ _filter_xfs_io
+
+# Now fiemap file foo, it should report the first 128K extent as shared and the
+# second 128K extent as not shared.
+echo "fiemap of file foo:"
+fiemap_test_file $SCRATCH_MNT/foo
+
+# Now do a similar test as above, except that this time only the second 128K
+# extent is cloned, the first extent is not cloned.
+
+# Create file foo2 with 2 consecutive extents, each one with a size of 128K.
+echo "Creating file foo2"
+$XFS_IO_PROG -f -c "pwrite -b 128K 0 128K" -c "fsync" \
+ -c "pwrite -b 128K 128K 128K" -c "fsync" \
+ $SCRATCH_MNT/foo2 | _filter_xfs_io
+
+# Clone only the second extent of foo2 into another file.
+echo "Cloning second extent of file foo2 to file bar2"
+$XFS_IO_PROG -f -c "reflink $SCRATCH_MNT/foo2 128K 0 128K" $SCRATCH_MNT/bar2 | \
+ _filter_xfs_io
+
+# Now fiemap file foo2, it should report the first 128K extent as not shared and
+# the second 128K extent as shared
+echo "fiemap of file foo2:"
+fiemap_test_file $SCRATCH_MNT/foo2
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 702
+Creating file foo
+wrote 131072/131072 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 131072/131072 bytes at offset 131072
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Cloning first extent of file foo to file bar
+linked 131072/131072 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+fiemap of file foo:
+0: [0..255]: 256 shared
+1: [256..511]: 256 not_shared
+Creating file foo2
+wrote 131072/131072 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 131072/131072 bytes at offset 131072
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Cloning second extent of file foo2 to file bar2
+linked 131072/131072 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+fiemap of file foo2:
+0: [0..255]: 256 not_shared
+1: [256..511]: 256 shared
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2022 SUSE Linux Products GmbH. All Rights Reserved.
+#
+# FS QA Test 703
+#
+# Test that direct IO writes with io_uring and O_DSYNC are durable if a power
+# failure happens after they complete.
+#
+. ./common/preamble
+_begin_fstest auto quick log prealloc io_uring
+
+_cleanup()
+{
+ _cleanup_flakey
+ cd /
+ rm -r -f $tmp.*
+}
+
+. ./common/dmflakey
+
+fio_config=$tmp.fio
+fio_out=$tmp.fio.out
+test_file="${SCRATCH_MNT}/foo"
+
+[ $FSTYP == "btrfs" ] &&
+ _fixed_by_kernel_commit 8184620ae212 \
+ "btrfs: fix lost file sync on direct IO write with nowait and dsync iocb"
+
+_supported_fs generic
+# We allocate 256M of data for the test file, so require a higher size of 512M
+# which gives a margin of safety for a COW filesystem like btrfs (where metadata
+# is always COWed).
+_require_scratch_size $((512 * 1024))
+_require_odirect
+_require_io_uring
+_require_dm_target flakey
+_require_xfs_io_command "falloc"
+
+cat >$fio_config <<EOF
+[test_io_uring_dio_dsync]
+ioengine=io_uring
+direct=1
+bs=64K
+sync=1
+filename=$test_file
+rw=randwrite
+time_based
+runtime=10
+EOF
+
+_require_fio $fio_config
+
+_scratch_mkfs >>$seqres.full 2>&1
+_require_metadata_journaling $SCRATCH_DEV
+_init_flakey
+_mount_flakey
+
+# We do 64K writes in the fio job.
+_require_congruent_file_oplen $SCRATCH_MNT $((64 * 1024))
+
+touch $test_file
+
+# On btrfs IOCB_NOWAIT writes can only be done on NOCOW files, so enable
+# nodatacow on the file if we are running on btrfs.
+if [ $FSTYP == "btrfs" ]; then
+ _require_chattr C
+ $CHATTR_PROG +C $test_file
+fi
+
+$XFS_IO_PROG -c "falloc 0 256M" $test_file
+
+# Persist everything, make sure the file exists after power failure.
+sync
+
+echo -e "Running fio with config:\n" >> $seqres.full
+cat $fio_config >> $seqres.full
+
+$FIO_PROG $fio_config --output=$fio_out
+
+echo -e "\nOutput from fio:\n" >> $seqres.full
+cat $fio_out >> $seqres.full
+
+digest_before=$(_md5_checksum $test_file)
+
+# Simulate a power failure and mount the filesystem to check that all the data
+# previously written are available.
+_flakey_drop_and_remount
+
+digest_after=$(_md5_checksum $test_file)
+
+if [ "$digest_after" != "$digest_before" ]; then
+ echo "Error: not all file data got persisted."
+ echo "Digest before power failure: $digest_before"
+ echo "Digest after power failure: $digest_after"
+fi
+
+_unmount_flakey
+
+# success, all done
+echo "Silence is golden"
+status=0
+exit
--- /dev/null
+QA output created by 703
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Red Hat, Inc. All Rights Reserved.
+#
+# FS QA Test No. 704
+#
+# Make sure logical-sector sized O_DIRECT write is allowed
+#
+. ./common/preamble
+_begin_fstest auto quick
+
+# Override the default cleanup function.
+_cleanup()
+{
+ cd /
+ rm -r -f $tmp.*
+ [ -d "$SCSI_DEBUG_MNT" ] && $UMOUNT_PROG $SCSI_DEBUG_MNT 2>/dev/null
+ _put_scsi_debug_dev
+}
+
+# Import common functions.
+. ./common/scsi_debug
+
+# real QA test starts here
+_supported_fs generic
+_fixed_by_kernel_commit 7c71ee78031c "xfs: allow logical-sector sized O_DIRECT"
+_require_scsi_debug
+# If TEST_DEV is block device, make sure current fs is a localfs which can be
+# written on scsi_debug device
+_require_test
+_require_block_device $TEST_DEV
+
+size=$(_small_fs_size_mb 256)
+echo "Get a device with 4096 physical sector size and 512 logical sector size"
+SCSI_DEBUG_DEV=`_get_scsi_debug_dev 4096 512 0 $size`
+blockdev --getpbsz --getss $SCSI_DEBUG_DEV
+
+echo "mkfs and mount"
+_mkfs_dev $SCSI_DEBUG_DEV || _fail "Can't make $FSTYP on scsi_debug device"
+SCSI_DEBUG_MNT="$TEST_DIR/scsi_debug_$seq"
+rm -rf $SCSI_DEBUG_MNT
+mkdir $SCSI_DEBUG_MNT
+run_check _mount $SCSI_DEBUG_DEV $SCSI_DEBUG_MNT
+
+echo "DIO read/write 512 bytes"
+# This dio write should succeed, even the physical sector size is 4096, but
+# the logical sector size is 512
+$XFS_IO_PROG -d -f -c "pwrite 0 512" $SCSI_DEBUG_MNT/testfile >> $seqres.full
+$XFS_IO_PROG -d -c "pread 0 512" $SCSI_DEBUG_MNT/testfile >> $seqres.full
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 704
+Get a device with 4096 physical sector size and 512 logical sector size
+4096
+512
+mkfs and mount
+DIO read/write 512 bytes
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Red Hat, Inc. All Rights Reserved.
+#
+# FS QA Test No. 705
+#
+# Test an issue in the truncate codepath where on-disk inode sizes are logged
+# prematurely via the free eofblocks path on file close.
+#
+. ./common/preamble
+_begin_fstest auto shutdown
+
+# real QA test starts here
+_supported_fs generic
+_require_scratch
+_require_scratch_shutdown
+_require_command "$FILEFRAG_PROG" filefrag
+_scratch_mkfs > $seqres.full 2>&1
+_scratch_mount
+
+echo "Create many small files with one extent at least"
+for ((i=0; i<10000; i++));do
+ $XFS_IO_PROG -f -c "pwrite 0 4k" $SCRATCH_MNT/file.$i >/dev/null 2>&1
+done
+
+echo "Shutdown the fs suddently"
+_scratch_shutdown
+
+echo "Cycle mount"
+_scratch_cycle_mount
+
+echo "Check file's (di_size > 0) extents"
+for f in $(find $SCRATCH_MNT -type f -size +0);do
+ # Check if the file has any extent
+ $FILEFRAG_PROG -v $f > $tmp.filefrag
+ if grep -Eq ': 0 extents found' $tmp.filefrag;then
+ echo " - $f get no extents, but its di_size > 0"
+ cat $tmp.filefrag
+ break
+ fi
+done
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 705
+Create many small files with one extent at least
+Shutdown the fs suddently
+Cycle mount
+Check file's (di_size > 0) extents
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2023 SUSE Linux Products GmbH. All Rights Reserved.
+#
+# FS QA Test 706
+#
+# Test that seeking for data on a 1 byte file works correctly, the returned
+# offset should be 0 if the start offset is 0.
+#
+. ./common/preamble
+_begin_fstest auto quick seek
+
+[ $FSTYP == "btrfs" ] &&
+ _fixed_by_kernel_commit 2f2e84ca6066 \
+ "btrfs: fix off-by-one in delalloc search during lseek"
+
+_supported_fs generic
+_require_test
+_require_seek_data_hole
+_require_test_program "seek_sanity_test"
+
+test_file=$TEST_DIR/seek_sanity_testfile.$seq
+
+_cleanup()
+{
+ cd /
+ rm -f $tmp.*
+ rm -f $test_file
+}
+
+_run_seek_sanity_test -s 22 -e 22 $test_file > $seqres.full 2>&1 ||
+ _fail "seek sanity check failed!"
+
+echo "Silence is golden"
+status=0
+exit
--- /dev/null
+QA output created by 706
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023 Jan Kara, SUSE Linux. All Rights Reserved.
+#
+# FS QA Test 707
+#
+# This is a test verifying whether the filesystem can gracefully handle
+# modifying of a directory while it is being moved, in particular the cases
+# where directory format changes
+#
+. ./common/preamble
+_begin_fstest auto
+
+_supported_fs generic
+_require_scratch
+
+_fixed_by_kernel_commit f950fd052913 \
+ "udf: Protect rename against modification of moved directory"
+_fixed_by_kernel_commit 0813299c586b \
+ "ext4: Fix possible corruption when moving a directory"
+
+_scratch_mkfs >>$seqres.full 2>&1
+_scratch_mount
+
+_cleanup()
+{
+ cd /
+ rm -f $tmp.*
+ if [ -n "$BGPID" ]; then
+ # Stop background process
+ kill -9 $BGPID &>/dev/null
+ wait
+ fi
+}
+
+# Loop multiple times trying to hit the race
+loops=$((100*TIME_FACTOR))
+files=500
+moves=500
+
+create_files()
+{
+ # We use slightly longer file name to make directory grow faster and
+ # hopefully convert between various types
+ for (( i = 0; i < $files; i++ )); do
+ touch somewhatlongerfilename$i
+ done
+}
+
+for (( i = 0; i <= $moves; i++ )); do
+ mkdir $SCRATCH_MNT/dir$i
+done
+
+for (( l = 0; l < $loops; l++ )); do
+ mkdir $SCRATCH_MNT/dir0/dir
+ pushd $SCRATCH_MNT/dir0/dir &>/dev/null
+ create_files &
+ BGPID=$!
+ popd &>/dev/null
+ for (( i = 0; i < $moves; i++ )); do
+ mv $SCRATCH_MNT/dir$i/dir $SCRATCH_MNT/dir$((i+1))/dir
+ done
+ wait
+ unset BGPID
+ rm -r $SCRATCH_MNT/dir$moves/dir
+done
+
+echo "Silence is golden"
+status=0
+exit
--- /dev/null
+QA output created by 707
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023 Meta Platforms, Inc. All Rights Reserved.
+#
+# FS QA Test 708
+#
+# Test iomap direct_io partial writes.
+#
+# Create a reasonably large file, then run a program which mmaps it,
+# touches the first page, then dio writes it to a second file. This
+# can result in a page fault reading from the mmapped dio write buffer and
+# thus the iomap direct_io partial write codepath.
+#
+. ./common/preamble
+_begin_fstest quick auto
+[ $FSTYP == "btrfs" ] && \
+ _fixed_by_kernel_commit b73a6fd1b1ef \
+ "btrfs: split partial dio bios before submit"
+
+# real QA test starts here
+_supported_fs generic
+_require_test
+_require_odirect
+_require_test_program dio-buf-fault
+src=$TEST_DIR/dio-buf-fault-$seq.src
+dst=$TEST_DIR/dio-buf-fault-$seq.dst
+
+rm -rf "$src" "$dst"
+
+echo "Silence is golden"
+
+$XFS_IO_PROG -fc "pwrite -q -S 0xcd 0 $((2 * 1024 * 1024))" $src
+$here/src/dio-buf-fault $src $dst > /dev/null || _fail "failed doing the dio copy"
+diff $src $dst
+
+# success, all done
+status=$?
+exit
--- /dev/null
+QA output created by 708
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 709
+#
+# Can we use swapext to make the quota accounting incorrect?
+
+. ./common/preamble
+_begin_fstest auto quick fiexchange swapext quota
+
+# Import common functions.
+. ./common/filter
+. ./common/quota
+
+# real QA test starts here
+_require_xfs_io_command swapext '-v exchrange'
+_require_user
+_require_nobody
+_require_quota
+_require_xfs_quota
+_require_scratch
+
+# Format filesystem and set up quota limits
+_scratch_mkfs > $seqres.full
+_qmount_option "usrquota,grpquota"
+_qmount >> $seqres.full
+
+# Set up initial files
+$XFS_IO_PROG -f -c 'pwrite -S 0x58 0 256k -b 1m' $SCRATCH_MNT/a >> $seqres.full
+chown $qa_user $SCRATCH_MNT/a
+$XFS_IO_PROG -f -c 'pwrite -S 0x59 0 64k -b 64k' -c 'truncate 256k' $SCRATCH_MNT/b >> $seqres.full
+chown nobody $SCRATCH_MNT/b
+
+echo before swapext >> $seqres.full
+$XFS_QUOTA_PROG -x -c 'report -a' $SCRATCH_MNT >> $seqres.full
+stat $SCRATCH_MNT/* >> $seqres.full
+
+# Now try to swap the extents of the two files. The command is allowed to
+# fail with -EINVAL (since that's what the first kernel fix does) or succeed
+# (because subsequent rewrites can handle quota). Whatever the outcome, the
+# quota usage check at the end should never show a discrepancy.
+$XFS_IO_PROG -c "swapext $SCRATCH_MNT/b" $SCRATCH_MNT/a &> $tmp.swap
+cat $tmp.swap >> $seqres.full
+grep -v 'Invalid argument' $tmp.swap
+
+echo after swapext >> $seqres.full
+$XFS_QUOTA_PROG -x -c 'report -a' $SCRATCH_MNT >> $seqres.full
+stat $SCRATCH_MNT/* >> $seqres.full
+
+_check_quota_usage
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 709
+Comparing user usage
+Comparing group usage
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 710
+#
+# Can we use swapext to exceed the quota enforcement?
+
+. ./common/preamble
+_begin_fstest auto quick fiexchange swapext quota
+
+# Import common functions.
+. ./common/filter
+. ./common/quota
+
+# real QA test starts here
+_require_xfs_io_command swapext '-v exchrange'
+_require_user
+_require_nobody
+_require_quota
+_require_xfs_quota
+_require_scratch
+
+# Format filesystem and set up quota limits
+_scratch_mkfs > $seqres.full
+_qmount_option "usrquota,grpquota"
+_qmount >> $seqres.full
+
+# Set up initial files
+$XFS_IO_PROG -f -c 'pwrite -S 0x58 0 256k -b 1m' $SCRATCH_MNT/a >> $seqres.full
+chown $qa_user $SCRATCH_MNT/a
+$XFS_IO_PROG -f -c 'pwrite -S 0x59 0 64k -b 64k' -c 'truncate 256k' $SCRATCH_MNT/b >> $seqres.full
+chown nobody $SCRATCH_MNT/b
+
+# Set up a quota limit
+$XFS_QUOTA_PROG -x -c "limit -u bhard=70k nobody" $SCRATCH_MNT
+
+echo before swapext >> $seqres.full
+$XFS_QUOTA_PROG -x -c 'report -a' $SCRATCH_MNT >> $seqres.full
+stat $SCRATCH_MNT/* >> $seqres.full
+
+# Now try to swapext
+$XFS_IO_PROG -c "swapext $SCRATCH_MNT/b" $SCRATCH_MNT/a
+
+echo after swapext >> $seqres.full
+$XFS_QUOTA_PROG -x -c 'report -a' $SCRATCH_MNT >> $seqres.full
+stat $SCRATCH_MNT/* >> $seqres.full
+
+_check_quota_usage
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 710
+swapext: Disk quota exceeded
+Comparing user usage
+Comparing group usage
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 711
+#
+# Make sure that swapext won't touch a swap file.
+
+. ./common/preamble
+_begin_fstest auto quick fiexchange swapext
+
+# Override the default cleanup function.
+_cleanup()
+{
+ cd /
+ test -e "$dir/a" && swapoff $dir/a
+ rm -r -f $tmp.* $dir
+}
+
+# Import common functions.
+. ./common/filter
+
+# real QA test starts here
+_require_xfs_io_command swapext '-v exchrange'
+_require_test
+
+dir=$TEST_DIR/test-$seq
+mkdir -p $dir
+
+# Set up a fragmented swapfile and a dummy donor file.
+$XFS_IO_PROG -f -c 'pwrite -S 0x58 0 32m -b 1m' -c fsync $dir/a >> $seqres.full
+$here/src/punch-alternating $dir/a
+$XFS_IO_PROG -f -c 'pwrite -S 0x58 0 32m -b 1m' -c fsync $dir/a >> $seqres.full
+$MKSWAP_PROG $dir/a >> $seqres.full
+
+$XFS_IO_PROG -f -c 'pwrite -S 0x59 0 32m -b 1m' $dir/b >> $seqres.full
+
+swapon $dir/a || _notrun 'failed to swapon'
+
+# Now try to swapext. The old code would return EINVAL for swapfiles
+# even though everything else in the VFS returns ETXTBSY.
+$XFS_IO_PROG -c "swapext $dir/b" $dir/a 2>&1 | \
+ sed -e 's/swapext: Invalid argument/swapext: Text file busy/g'
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 711
+swapext: Text file busy
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 712
+#
+# Make sure that swapext modifies ctime and not mtime of the file.
+
+. ./common/preamble
+_begin_fstest auto quick fiexchange swapext
+
+# Override the default cleanup function.
+_cleanup()
+{
+ cd /
+ rm -r -f $tmp.* $dir
+}
+
+# Import common functions.
+. ./common/filter
+
+# real QA test starts here
+_require_test_program punch-alternating
+_require_xfs_io_command swapext '-v exchrange'
+_require_test
+
+dir=$TEST_DIR/test-$seq
+mkdir -p $dir
+
+# Set up initial files
+$XFS_IO_PROG -f -c 'pwrite -S 0x58 0 256k -b 1m' $dir/a >> $seqres.full
+$here/src/punch-alternating $dir/a
+$XFS_IO_PROG -f -c 'pwrite -S 0x59 0 256k -b 1m' $dir/b >> $seqres.full
+
+# Snapshot the 'a' file before we swap
+echo before >> $seqres.full
+md5sum $dir/a $dir/b >> $seqres.full
+old_mtime="$(echo $(stat -c '%y' $dir/a $dir/b))"
+old_ctime="$(echo $(stat -c '%z' $dir/a $dir/b))"
+stat -c '%y %Y %z %Z' $dir/a $dir/b >> $seqres.full
+
+# Now try to swapext
+$XFS_IO_PROG -c "swapext $dir/b" $dir/a
+
+# Snapshot the 'a' file after we swap
+echo after >> $seqres.full
+md5sum $dir/a $dir/b >> $seqres.full
+new_mtime="$(echo $(stat -c '%y' $dir/a $dir/b))"
+new_ctime="$(echo $(stat -c '%z' $dir/a $dir/b))"
+stat -c '%y %Y %z %Z' $dir/a $dir/b >> $seqres.full
+
+test "$new_mtime" = "$old_mtime" && echo "mtime: $new_mtime == $old_mtime"
+test "$new_ctime" = "$old_ctime" && echo "ctime: $new_ctime == $old_ctime"
+
+# success, all done
+echo Silence is golden.
+status=0
+exit
--- /dev/null
+QA output created by 712
+Silence is golden.
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 713
+#
+# Test swapext between ranges of two different files.
+
+. ./common/preamble
+_begin_fstest auto quick fiexchange swapext
+
+# Override the default cleanup function.
+_cleanup()
+{
+ cd /
+ rm -r -f $tmp.* $dir
+}
+
+# Import common functions.
+. ./common/filter
+. ./common/reflink
+
+# real QA test starts here
+_require_xfs_io_command swapext '-v exchrange -s 64k -l 64k'
+_require_xfs_io_command "falloc"
+_require_test
+
+filesnap() {
+ echo "$1"
+ if [ "$2" != "$3" ]; then
+ md5sum $2 $3 | _filter_test_dir
+ else
+ md5sum $2 | _filter_test_dir
+ fi
+}
+
+test_swapext_once() {
+ filesnap "$1: before swapext" $dir/$3 $dir/$4
+ $XFS_IO_PROG -c "swapext -v exchrange $2 $dir/$3" $dir/$4
+ filesnap "$1: after swapext" $dir/$3 $dir/$4
+ _test_cycle_mount
+ filesnap "$1: after cycling mount" $dir/$3 $dir/$4
+ echo
+}
+
+test_swapext_two() {
+ # swapext the same range of two files
+ test_swapext_once "$*: samerange" \
+ "-s $((blksz * 3)) -d $((blksz * 3)) -l $((blksz * 5))" b a
+
+ # swapext different ranges of two files
+ test_swapext_once "$*: diffrange" \
+ "-s $((blksz * 37)) -d $((blksz * 47)) -l $((blksz * 7))" b a
+
+ # swapext overlapping ranges of two files
+ test_swapext_once "$*: overlap" \
+ "-s $((blksz * 17)) -d $((blksz * 23)) -l $((blksz * 7))" b a
+}
+
+dir=$TEST_DIR/test-$seq
+mkdir -p $dir
+blksz=65536
+nrblks=57
+_require_congruent_file_oplen $TEST_DIR $blksz
+
+# Set up some simple files for a first test.
+rm -rf $dir/a $dir/b
+_pwrite_byte 0x58 0 $((blksz * nrblks)) $dir/a >> $seqres.full
+_pwrite_byte 0x59 0 $((blksz * nrblks)) $dir/b >> $seqres.full
+test_swapext_two "simple"
+
+# Make some files that don't end an aligned offset.
+rm -rf $dir/a $dir/b
+_pwrite_byte 0x58 0 $(( (blksz * nrblks) + 37)) $dir/a >> $seqres.full
+_pwrite_byte 0x59 0 $(( (blksz * nrblks) + 37)) $dir/b >> $seqres.full
+test_swapext_once "unalignedeof" "" a b
+
+# Set up some crazy rainbow files
+rm -rf $dir/a $dir/b
+_weave_file_rainbow $blksz $nrblks $dir/a >> $seqres.full
+_weave_file_rainbow $blksz $nrblks $dir/b >> $seqres.full
+test_swapext_two "rainbow"
+
+# Now set up a simple file for testing within the same file
+rm -rf $dir/c
+$XFS_IO_PROG -f -c "pwrite -S 0x58 0 $((blksz * nrblks))" \
+ -c "pwrite -S 0x59 $((blksz * nrblks)) $((blksz * nrblks))" \
+ $dir/c >> $seqres.full
+
+# swapext the same offset into the 'X' and 'Y' regions of the file
+test_swapext_once "single: sameXandY" \
+ "-s $((blksz * 3)) -d $((blksz * (nrblks + 3))) -l $((blksz * 5))" c c
+
+# swapext the same offset into the 'X' and 'Y' regions of the file
+test_swapext_once "single: overlap" \
+ "-s $((blksz * 13)) -d $((blksz * 17)) -l $((blksz * 5))" c c
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 713
+simple: samerange: before swapext
+db85d578204631f2b4eb1e73974253c2 TEST_DIR/test-713/b
+d0425612f15c6071022cf7127620f63d TEST_DIR/test-713/a
+simple: samerange: after swapext
+20beef1c9ed7de02e4229c69bd43bd8f TEST_DIR/test-713/b
+e7697fa99d08f7eb76fa3fb963fe916a TEST_DIR/test-713/a
+simple: samerange: after cycling mount
+20beef1c9ed7de02e4229c69bd43bd8f TEST_DIR/test-713/b
+e7697fa99d08f7eb76fa3fb963fe916a TEST_DIR/test-713/a
+
+simple: diffrange: before swapext
+20beef1c9ed7de02e4229c69bd43bd8f TEST_DIR/test-713/b
+e7697fa99d08f7eb76fa3fb963fe916a TEST_DIR/test-713/a
+simple: diffrange: after swapext
+cd32ce54c295fcdf571ce7f8220fac56 TEST_DIR/test-713/b
+d9771c5bb6d9db00b9abe65a4410e1a6 TEST_DIR/test-713/a
+simple: diffrange: after cycling mount
+cd32ce54c295fcdf571ce7f8220fac56 TEST_DIR/test-713/b
+d9771c5bb6d9db00b9abe65a4410e1a6 TEST_DIR/test-713/a
+
+simple: overlap: before swapext
+cd32ce54c295fcdf571ce7f8220fac56 TEST_DIR/test-713/b
+d9771c5bb6d9db00b9abe65a4410e1a6 TEST_DIR/test-713/a
+simple: overlap: after swapext
+e0fff655f6a08fc2f03ee01e4767060c TEST_DIR/test-713/b
+ec7d764c85e583e305028c9cba5b25b6 TEST_DIR/test-713/a
+simple: overlap: after cycling mount
+e0fff655f6a08fc2f03ee01e4767060c TEST_DIR/test-713/b
+ec7d764c85e583e305028c9cba5b25b6 TEST_DIR/test-713/a
+
+unalignedeof: before swapext
+9f8c731a4f1946ffdda8c33e82417f2d TEST_DIR/test-713/a
+7a5d2ba7508226751c835292e28cd227 TEST_DIR/test-713/b
+unalignedeof: after swapext
+7a5d2ba7508226751c835292e28cd227 TEST_DIR/test-713/a
+9f8c731a4f1946ffdda8c33e82417f2d TEST_DIR/test-713/b
+unalignedeof: after cycling mount
+7a5d2ba7508226751c835292e28cd227 TEST_DIR/test-713/a
+9f8c731a4f1946ffdda8c33e82417f2d TEST_DIR/test-713/b
+
+rainbow: samerange: before swapext
+48b41ee1970eb71064b77181f42634cf TEST_DIR/test-713/b
+48b41ee1970eb71064b77181f42634cf TEST_DIR/test-713/a
+rainbow: samerange: after swapext
+48b41ee1970eb71064b77181f42634cf TEST_DIR/test-713/b
+48b41ee1970eb71064b77181f42634cf TEST_DIR/test-713/a
+rainbow: samerange: after cycling mount
+48b41ee1970eb71064b77181f42634cf TEST_DIR/test-713/b
+48b41ee1970eb71064b77181f42634cf TEST_DIR/test-713/a
+
+rainbow: diffrange: before swapext
+48b41ee1970eb71064b77181f42634cf TEST_DIR/test-713/b
+48b41ee1970eb71064b77181f42634cf TEST_DIR/test-713/a
+rainbow: diffrange: after swapext
+48b41ee1970eb71064b77181f42634cf TEST_DIR/test-713/b
+48b41ee1970eb71064b77181f42634cf TEST_DIR/test-713/a
+rainbow: diffrange: after cycling mount
+48b41ee1970eb71064b77181f42634cf TEST_DIR/test-713/b
+48b41ee1970eb71064b77181f42634cf TEST_DIR/test-713/a
+
+rainbow: overlap: before swapext
+48b41ee1970eb71064b77181f42634cf TEST_DIR/test-713/b
+48b41ee1970eb71064b77181f42634cf TEST_DIR/test-713/a
+rainbow: overlap: after swapext
+6753bc585e3c71d53bfaae11d2ffee99 TEST_DIR/test-713/b
+39597abd4d9d0c9ceac22b77eb00c373 TEST_DIR/test-713/a
+rainbow: overlap: after cycling mount
+6753bc585e3c71d53bfaae11d2ffee99 TEST_DIR/test-713/b
+39597abd4d9d0c9ceac22b77eb00c373 TEST_DIR/test-713/a
+
+single: sameXandY: before swapext
+39e17753fa9e923a3b5928e13775e358 TEST_DIR/test-713/c
+single: sameXandY: after swapext
+8262c617070703fb0e2a28d8f05e3112 TEST_DIR/test-713/c
+single: sameXandY: after cycling mount
+8262c617070703fb0e2a28d8f05e3112 TEST_DIR/test-713/c
+
+single: overlap: before swapext
+8262c617070703fb0e2a28d8f05e3112 TEST_DIR/test-713/c
+swapext: Invalid argument
+single: overlap: after swapext
+8262c617070703fb0e2a28d8f05e3112 TEST_DIR/test-713/c
+single: overlap: after cycling mount
+8262c617070703fb0e2a28d8f05e3112 TEST_DIR/test-713/c
+
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 714
+#
+# Test swapext between ranges of two different files, when one of the files
+# is shared.
+
+. ./common/preamble
+_begin_fstest auto quick fiexchange swapext
+
+# Override the default cleanup function.
+_cleanup()
+{
+ cd /
+ rm -r -f $tmp.* $dir
+}
+
+# Import common functions.
+. ./common/filter
+. ./common/reflink
+
+# real QA test starts here
+_require_xfs_io_command swapext '-v exchrange'
+_require_xfs_io_command "falloc"
+_require_test_reflink
+
+filesnap() {
+ echo "$1"
+ if [ "$2" != "$3" ]; then
+ md5sum $2 $3 | _filter_test_dir
+ else
+ md5sum $2 | _filter_test_dir
+ fi
+}
+
+test_swapext_once() {
+ filesnap "$1: before swapext" $dir/$3 $dir/$4
+ $XFS_IO_PROG -c "swapext -v exchrange $2 $dir/$3" $dir/$4
+ filesnap "$1: after swapext" $dir/$3 $dir/$4
+ _test_cycle_mount
+ filesnap "$1: after cycling mount" $dir/$3 $dir/$4
+ echo
+}
+
+test_swapext_two() {
+ # swapext the same range of two files
+ test_swapext_once "$*: samerange" \
+ "-s $((blksz * 3)) -d $((blksz * 3)) -l $((blksz * 5))" b a
+
+ # swapext different ranges of two files
+ test_swapext_once "$*: diffrange" \
+ "-s $((blksz * 37)) -d $((blksz * 47)) -l $((blksz * 7))" b a
+
+ # swapext overlapping ranges of two files
+ test_swapext_once "$*: overlap" \
+ "-s $((blksz * 17)) -d $((blksz * 23)) -l $((blksz * 7))" b a
+
+ # Now let's overwrite a entirely to make sure COW works
+ echo "overwrite A and B entirely"
+ md5sum $dir/sharea | _filter_test_dir
+ $XFS_IO_PROG -c "pwrite -S 0x60 0 $((blksz * nrblks))" $dir/a >> $seqres.full
+ $XFS_IO_PROG -c "pwrite -S 0x60 0 $((blksz * nrblks))" $dir/b >> $seqres.full
+ md5sum $dir/sharea | _filter_test_dir
+ _test_cycle_mount
+ md5sum $dir/sharea | _filter_test_dir
+ echo
+}
+
+dir=$TEST_DIR/test-$seq
+mkdir -p $dir
+blksz=65536
+_require_congruent_file_oplen $TEST_DIR $blksz
+nrblks=57
+
+# Set up some simple files for a first test.
+rm -f $dir/a $dir/b $dir/sharea
+_pwrite_byte 0x58 0 $((blksz * nrblks)) $dir/a >> $seqres.full
+_pwrite_byte 0x59 0 $((blksz * nrblks)) $dir/b >> $seqres.full
+_cp_reflink $dir/a $dir/sharea
+test_swapext_two "simple"
+
+# Set up some crazy rainbow files
+rm -f $dir/a $dir/b $dir/sharea
+_weave_file_rainbow $blksz $nrblks $dir/a >> $seqres.full
+_weave_file_rainbow $blksz $nrblks $dir/b >> $seqres.full
+_cp_reflink $dir/a $dir/sharea
+test_swapext_two "rainbow"
+
+# Now set up a simple file for testing within the same file
+rm -f $dir/c $dir/sharec
+$XFS_IO_PROG -f -c "pwrite -S 0x58 0 $((blksz * nrblks))" \
+ -c "pwrite -S 0x59 $((blksz * nrblks)) $((blksz * nrblks))" \
+ $dir/c >> $seqres.full
+_cp_reflink $dir/c $dir/sharec
+
+# swapext the same offset into the 'X' and 'Y' regions of the file
+test_swapext_once "single: sameXandY" \
+ "-s $((blksz * 3)) -d $((blksz * (nrblks + 3))) -l $((blksz * 5))" c c
+
+# swapext the same offset into the 'X' and 'Y' regions of the file
+test_swapext_once "single: overlap" \
+ "-s $((blksz * 13)) -d $((blksz * 17)) -l $((blksz * 5))" c c
+
+# Now let's overwrite a entirely to make sure COW works
+echo "overwrite C entirely"
+md5sum $dir/sharec | _filter_test_dir
+$XFS_IO_PROG -c "pwrite -S 0x60 0 $((blksz * nrblks))" $dir/c >> $seqres.full
+md5sum $dir/sharec | _filter_test_dir
+_test_cycle_mount
+md5sum $dir/sharec | _filter_test_dir
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 714
+simple: samerange: before swapext
+db85d578204631f2b4eb1e73974253c2 TEST_DIR/test-714/b
+d0425612f15c6071022cf7127620f63d TEST_DIR/test-714/a
+simple: samerange: after swapext
+20beef1c9ed7de02e4229c69bd43bd8f TEST_DIR/test-714/b
+e7697fa99d08f7eb76fa3fb963fe916a TEST_DIR/test-714/a
+simple: samerange: after cycling mount
+20beef1c9ed7de02e4229c69bd43bd8f TEST_DIR/test-714/b
+e7697fa99d08f7eb76fa3fb963fe916a TEST_DIR/test-714/a
+
+simple: diffrange: before swapext
+20beef1c9ed7de02e4229c69bd43bd8f TEST_DIR/test-714/b
+e7697fa99d08f7eb76fa3fb963fe916a TEST_DIR/test-714/a
+simple: diffrange: after swapext
+cd32ce54c295fcdf571ce7f8220fac56 TEST_DIR/test-714/b
+d9771c5bb6d9db00b9abe65a4410e1a6 TEST_DIR/test-714/a
+simple: diffrange: after cycling mount
+cd32ce54c295fcdf571ce7f8220fac56 TEST_DIR/test-714/b
+d9771c5bb6d9db00b9abe65a4410e1a6 TEST_DIR/test-714/a
+
+simple: overlap: before swapext
+cd32ce54c295fcdf571ce7f8220fac56 TEST_DIR/test-714/b
+d9771c5bb6d9db00b9abe65a4410e1a6 TEST_DIR/test-714/a
+simple: overlap: after swapext
+e0fff655f6a08fc2f03ee01e4767060c TEST_DIR/test-714/b
+ec7d764c85e583e305028c9cba5b25b6 TEST_DIR/test-714/a
+simple: overlap: after cycling mount
+e0fff655f6a08fc2f03ee01e4767060c TEST_DIR/test-714/b
+ec7d764c85e583e305028c9cba5b25b6 TEST_DIR/test-714/a
+
+overwrite A and B entirely
+d0425612f15c6071022cf7127620f63d TEST_DIR/test-714/sharea
+d0425612f15c6071022cf7127620f63d TEST_DIR/test-714/sharea
+d0425612f15c6071022cf7127620f63d TEST_DIR/test-714/sharea
+
+rainbow: samerange: before swapext
+48b41ee1970eb71064b77181f42634cf TEST_DIR/test-714/b
+48b41ee1970eb71064b77181f42634cf TEST_DIR/test-714/a
+rainbow: samerange: after swapext
+48b41ee1970eb71064b77181f42634cf TEST_DIR/test-714/b
+48b41ee1970eb71064b77181f42634cf TEST_DIR/test-714/a
+rainbow: samerange: after cycling mount
+48b41ee1970eb71064b77181f42634cf TEST_DIR/test-714/b
+48b41ee1970eb71064b77181f42634cf TEST_DIR/test-714/a
+
+rainbow: diffrange: before swapext
+48b41ee1970eb71064b77181f42634cf TEST_DIR/test-714/b
+48b41ee1970eb71064b77181f42634cf TEST_DIR/test-714/a
+rainbow: diffrange: after swapext
+48b41ee1970eb71064b77181f42634cf TEST_DIR/test-714/b
+48b41ee1970eb71064b77181f42634cf TEST_DIR/test-714/a
+rainbow: diffrange: after cycling mount
+48b41ee1970eb71064b77181f42634cf TEST_DIR/test-714/b
+48b41ee1970eb71064b77181f42634cf TEST_DIR/test-714/a
+
+rainbow: overlap: before swapext
+48b41ee1970eb71064b77181f42634cf TEST_DIR/test-714/b
+48b41ee1970eb71064b77181f42634cf TEST_DIR/test-714/a
+rainbow: overlap: after swapext
+6753bc585e3c71d53bfaae11d2ffee99 TEST_DIR/test-714/b
+39597abd4d9d0c9ceac22b77eb00c373 TEST_DIR/test-714/a
+rainbow: overlap: after cycling mount
+6753bc585e3c71d53bfaae11d2ffee99 TEST_DIR/test-714/b
+39597abd4d9d0c9ceac22b77eb00c373 TEST_DIR/test-714/a
+
+overwrite A and B entirely
+48b41ee1970eb71064b77181f42634cf TEST_DIR/test-714/sharea
+48b41ee1970eb71064b77181f42634cf TEST_DIR/test-714/sharea
+48b41ee1970eb71064b77181f42634cf TEST_DIR/test-714/sharea
+
+single: sameXandY: before swapext
+39e17753fa9e923a3b5928e13775e358 TEST_DIR/test-714/c
+single: sameXandY: after swapext
+8262c617070703fb0e2a28d8f05e3112 TEST_DIR/test-714/c
+single: sameXandY: after cycling mount
+8262c617070703fb0e2a28d8f05e3112 TEST_DIR/test-714/c
+
+single: overlap: before swapext
+8262c617070703fb0e2a28d8f05e3112 TEST_DIR/test-714/c
+swapext: Invalid argument
+single: overlap: after swapext
+8262c617070703fb0e2a28d8f05e3112 TEST_DIR/test-714/c
+single: overlap: after cycling mount
+8262c617070703fb0e2a28d8f05e3112 TEST_DIR/test-714/c
+
+overwrite C entirely
+39e17753fa9e923a3b5928e13775e358 TEST_DIR/test-714/sharec
+39e17753fa9e923a3b5928e13775e358 TEST_DIR/test-714/sharec
+39e17753fa9e923a3b5928e13775e358 TEST_DIR/test-714/sharec
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 715
+#
+# Test swapext between two files of unlike size.
+
+. ./common/preamble
+_begin_fstest auto quick fiexchange swapext
+
+# Override the default cleanup function.
+_cleanup()
+{
+ cd /
+ rm -r -f $tmp.* $dir
+}
+
+# Import common functions.
+. ./common/filter
+. ./common/reflink
+
+# real QA test starts here
+_require_xfs_io_command swapext '-v exchrange -s 64k -l 64k'
+_require_test
+
+filesnap() {
+ echo "$1"
+ if [ "$2" != "$3" ]; then
+ md5sum $2 $3 | _filter_test_dir
+ else
+ md5sum $2 | _filter_test_dir
+ fi
+}
+
+test_swapext_once() {
+ local tag=$1
+ local a_len=$2
+ local b_len=$3
+ local a_off=$4
+ local b_off=$5
+ local len=$6
+
+ # len is either a block count or -e to swap to EOF
+ if [ "$len" != "-e" ]; then
+ len="-l $((blksz * len))"
+ fi
+
+ rm -f $dir/a $dir/b
+ _pwrite_byte 0x58 0 $((blksz * a_len)) $dir/a >> $seqres.full
+ _pwrite_byte 0x59 0 $((blksz * b_len)) $dir/b >> $seqres.full
+ filesnap "$tag: before swapext" $dir/a $dir/b
+
+ cmd="swapext -v exchrange -s $((blksz * a_off)) -d $((blksz * b_off)) $len $dir/a"
+ echo "$cmd" >> $seqres.full
+ $XFS_IO_PROG -c "$cmd" $dir/b
+ filesnap "$tag: after swapext" $dir/a $dir/b
+
+ _test_cycle_mount
+ filesnap "$tag: after cycling mount" $dir/a $dir/b
+ echo
+}
+
+dir=$TEST_DIR/test-$seq
+mkdir -p $dir
+blksz=65536
+
+test_swapext_once "last 5 blocks" 27 37 22 32 5
+
+test_swapext_once "whole file to eof" 27 37 0 0 -e
+
+test_swapext_once "blocks 30-40" 27 37 30 30 10
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 715
+last 5 blocks: before swapext
+207ea56e0ccbf50d38fd3a2d842aa170 TEST_DIR/test-715/a
+eb58941d31f5be1e4e22df8c536dd490 TEST_DIR/test-715/b
+last 5 blocks: after swapext
+3f34470fe9feb8513d5f3a8538f2c5f3 TEST_DIR/test-715/a
+c3daca7dd9218371cd0dc64f11e4b0bf TEST_DIR/test-715/b
+last 5 blocks: after cycling mount
+3f34470fe9feb8513d5f3a8538f2c5f3 TEST_DIR/test-715/a
+c3daca7dd9218371cd0dc64f11e4b0bf TEST_DIR/test-715/b
+
+whole file to eof: before swapext
+207ea56e0ccbf50d38fd3a2d842aa170 TEST_DIR/test-715/a
+eb58941d31f5be1e4e22df8c536dd490 TEST_DIR/test-715/b
+whole file to eof: after swapext
+eb58941d31f5be1e4e22df8c536dd490 TEST_DIR/test-715/a
+207ea56e0ccbf50d38fd3a2d842aa170 TEST_DIR/test-715/b
+whole file to eof: after cycling mount
+eb58941d31f5be1e4e22df8c536dd490 TEST_DIR/test-715/a
+207ea56e0ccbf50d38fd3a2d842aa170 TEST_DIR/test-715/b
+
+blocks 30-40: before swapext
+207ea56e0ccbf50d38fd3a2d842aa170 TEST_DIR/test-715/a
+eb58941d31f5be1e4e22df8c536dd490 TEST_DIR/test-715/b
+swapext: Invalid argument
+blocks 30-40: after swapext
+207ea56e0ccbf50d38fd3a2d842aa170 TEST_DIR/test-715/a
+eb58941d31f5be1e4e22df8c536dd490 TEST_DIR/test-715/b
+blocks 30-40: after cycling mount
+207ea56e0ccbf50d38fd3a2d842aa170 TEST_DIR/test-715/a
+eb58941d31f5be1e4e22df8c536dd490 TEST_DIR/test-715/b
+
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 716
+#
+# Test atomic file updates when (a) the length is the same; (b) the length
+# is different; and (c) someone modifies the original file and we need to
+# cancel the update. The file contents are cloned into the staging file,
+# and some of the contents are updated.
+
+. ./common/preamble
+_begin_fstest auto quick fiexchange swapext
+
+# Override the default cleanup function.
+_cleanup()
+{
+ cd /
+ rm -r -f $tmp.* $dir
+}
+
+# Import common functions.
+. ./common/filter
+. ./common/reflink
+
+# real QA test starts here
+_require_xfs_io_command swapext '-v exchrange'
+_require_xfs_io_command startupdate
+_require_test_reflink
+_require_test
+
+filesnap() {
+ echo "$1"
+ md5sum $2 | _filter_test_dir
+}
+
+mkfile() {
+ rm -f $dir/a
+ _pwrite_byte 0x58 0 $((blksz * nrblks)) $dir/a >> $seqres.full
+ sync
+}
+
+dir=$TEST_DIR/test-$seq
+mkdir -p $dir
+blksz=65536
+nrblks=64
+
+# Use the atomic file update staging prototype in xfs_io to update a file.
+mkfile
+filesnap "before commit" $dir/a
+
+$XFS_IO_PROG \
+ -c 'startupdate' \
+ -c 'pwrite -S 0x60 44k 55k -b 1m' \
+ -c 'commitupdate -q' \
+ "$dir/a" 2>&1 | _filter_xfs_io | _filter_test_dir
+
+filesnap "after commit" $dir/a
+echo
+
+# Use the atomic file updates to replace a file with a shorter file.
+mkfile
+filesnap "before shorten commit" $dir/a
+
+$XFS_IO_PROG \
+ -c 'startupdate' \
+ -c 'truncate 55k' \
+ -c 'pwrite -S 0x60 0 55k' \
+ -c 'commitupdate -q' \
+ "$dir/a" 2>&1 | _filter_xfs_io | _filter_test_dir
+
+filesnap "after shorten commit" $dir/a
+echo
+
+# Use the atomic file updates to replace a file with a longer file.
+mkfile
+filesnap "before lengthen commit" $dir/a
+
+$XFS_IO_PROG \
+ -c 'startupdate' \
+ -c "pwrite -S 0x60 0 $(( (blksz * nrblks) + 37373 ))" \
+ -c 'commitupdate -q' \
+ "$dir/a" 2>&1 | _filter_xfs_io | _filter_test_dir
+
+filesnap "after lengthen commit" $dir/a
+echo
+
+# Use the atomic file update staging prototype in xfs_io to cancel updating a
+# file.
+mkfile
+filesnap "before cancel" $dir/a
+
+$XFS_IO_PROG \
+ -c 'startupdate' \
+ -c 'pwrite -S 0x60 44k 55k -b 1m' \
+ -c 'cancelupdate' \
+ "$dir/a" 2>&1 | _filter_xfs_io | _filter_test_dir
+
+filesnap "after cancel" $dir/a
+echo
+
+# Now try the update but with the A file open separately so that we clobber
+# mtime and fail the update.
+mkfile
+filesnap "before fail commit" $dir/a
+
+$XFS_IO_PROG \
+ -c "open $dir/a" \
+ -c 'startupdate' \
+ -c 'pwrite -S 0x58 44k 55k -b 1m' \
+ -c 'file 0' \
+ -c 'close' \
+ -c 'pwrite -S 0x61 22k 11k -b 1m' \
+ -c 'commitupdate -q' \
+ "$dir/a" 2>&1 | _filter_xfs_io | _filter_test_dir
+
+filesnap "after fail commit" $dir/a
+echo
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 716
+before commit
+d712f003e9d467e063cda1baf319b928 TEST_DIR/test-716/a
+wrote 56320/56320 bytes at offset 45056
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Committed updates to 'TEST_DIR/test-716/a'.
+after commit
+bedbd22b58a680219a1225353f6195fa TEST_DIR/test-716/a
+
+before shorten commit
+d712f003e9d467e063cda1baf319b928 TEST_DIR/test-716/a
+wrote 56320/56320 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Committed updates to 'TEST_DIR/test-716/a'.
+after shorten commit
+52353039d89c5f2b76b9003464e5276a TEST_DIR/test-716/a
+
+before lengthen commit
+d712f003e9d467e063cda1baf319b928 TEST_DIR/test-716/a
+wrote 4231677/4231677 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Committed updates to 'TEST_DIR/test-716/a'.
+after lengthen commit
+1839e7c6bf616160dc51b12179db2642 TEST_DIR/test-716/a
+
+before cancel
+d712f003e9d467e063cda1baf319b928 TEST_DIR/test-716/a
+wrote 56320/56320 bytes at offset 45056
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Cancelled updates to 'TEST_DIR/test-716/a'.
+after cancel
+d712f003e9d467e063cda1baf319b928 TEST_DIR/test-716/a
+
+before fail commit
+d712f003e9d467e063cda1baf319b928 TEST_DIR/test-716/a
+committing update: Device or resource busy
+wrote 56320/56320 bytes at offset 45056
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 56320/56320 bytes at offset 45056
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+[000] TEST_DIR/test-716/a (xfs,non-sync,non-direct,read-write)
+ 001 TEST_DIR/test-716/a (fileupdate) (xfs,non-sync,non-direct,read-write)
+[000] TEST_DIR/test-716/a (fileupdate) (xfs,non-sync,non-direct,read-write)
+wrote 11264/11264 bytes at offset 22528
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+after fail commit
+d712f003e9d467e063cda1baf319b928 TEST_DIR/test-716/a
+
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 717
+#
+# Try invalid parameters to see if they fail.
+
+. ./common/preamble
+_begin_fstest auto quick fiexchange swapext
+
+# Override the default cleanup function.
+_cleanup()
+{
+ cd /
+ rm -r -f $tmp.* $dir
+}
+
+# Import common functions.
+. ./common/filter
+. ./common/reflink
+
+# real QA test starts here
+_require_xfs_io_command swapext '-v exchrange'
+_require_xfs_io_command startupdate
+_require_test
+_require_scratch
+_require_chattr i
+
+dir=$TEST_DIR/test-$seq
+mkdir -p $dir
+blksz=65536
+nrblks=64
+
+_scratch_mkfs >> $seqres.full
+_scratch_mount
+
+_pwrite_byte 0x58 0 $((blksz * nrblks)) $dir/a >> $seqres.full
+_pwrite_byte 0x58 0 $((blksz * nrblks)) $dir/b >> $seqres.full
+
+echo Immutable files
+$XFS_IO_PROG -c 'chattr +i' -c "swapext $dir/b" $dir/a
+$CHATTR_PROG -i $dir/a
+
+echo Readonly files
+$XFS_IO_PROG -r -c "swapext $dir/b" $dir/a
+
+echo Directories
+$XFS_IO_PROG -c "swapext $dir/b" $dir
+
+echo Unaligned ranges
+$XFS_IO_PROG -c "swapext -s 37 -d 61 -l 17 $dir/b" $dir/a
+
+echo file1 range entirely beyond EOF
+$XFS_IO_PROG -c "swapext -s $(( blksz * (nrblks + 500) )) -d 0 -l $blksz $dir/b" $dir/a
+
+echo file2 range entirely beyond EOF
+$XFS_IO_PROG -c "swapext -d $(( blksz * (nrblks + 500) )) -s 0 -l $blksz $dir/b" $dir/a
+
+echo Both ranges entirely beyond EOF
+$XFS_IO_PROG -c "swapext -d $(( blksz * (nrblks + 500) )) -s $(( blksz * (nrblks + 500) )) -l $blksz $dir/b" $dir/a
+
+echo file1 range crossing EOF
+$XFS_IO_PROG -c "swapext -s $(( blksz * (nrblks - 1) )) -d 0 -l $((2 * blksz)) $dir/b" $dir/a
+
+echo file2 range crossing EOF
+$XFS_IO_PROG -c "swapext -d $(( blksz * (nrblks - 1) )) -s 0 -l $((2 * blksz)) $dir/b" $dir/a
+
+echo Both ranges crossing EOF
+$XFS_IO_PROG -c "swapext -d $(( blksz * (nrblks - 1) )) -s $(( blksz * (nrblks - 1) )) -l $((blksz * 2)) $dir/b" $dir/a
+
+echo file1 unaligned EOF to file2 nowhere near EOF
+_pwrite_byte 0x58 $((blksz * nrblks)) 37 $dir/a >> $seqres.full
+_pwrite_byte 0x59 $((blksz * nrblks)) 37 $dir/b >> $seqres.full
+$XFS_IO_PROG -c "swapext -d 0 -s $(( blksz * nrblks )) -l 37 $dir/b" $dir/a
+
+echo file2 unaligned EOF to file1 nowhere near EOF
+$XFS_IO_PROG -c "swapext -s 0 -d $(( blksz * nrblks )) -l 37 $dir/b" $dir/a
+
+echo Files of unequal length
+_pwrite_byte 0x58 $((blksz * nrblks)) $((blksz * 2)) $dir/a >> $seqres.full
+_pwrite_byte 0x59 $((blksz * nrblks)) $blksz $dir/b >> $seqres.full
+$XFS_IO_PROG -c "swapext $dir/b" $dir/a
+
+echo Files on different filesystems
+_pwrite_byte 0x58 0 $((blksz * nrblks)) $SCRATCH_MNT/c >> $seqres.full
+$XFS_IO_PROG -c "swapext $SCRATCH_MNT/c" $dir/a
+
+echo Files on different mounts
+mkdir -p $SCRATCH_MNT/xyz
+mount --bind $dir $SCRATCH_MNT/xyz --bind
+_pwrite_byte 0x60 0 $((blksz * (nrblks + 2))) $dir/c >> $seqres.full
+$XFS_IO_PROG -c "swapext $SCRATCH_MNT/xyz/c" $dir/a
+umount $SCRATCH_MNT/xyz
+
+echo Swapping a file with itself
+$XFS_IO_PROG -c "swapext $dir/a" $dir/a
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 717
+Immutable files
+swapext: Operation not permitted
+Readonly files
+swapext: Bad file descriptor
+Directories
+swapext: Is a directory
+Unaligned ranges
+swapext: Invalid argument
+file1 range entirely beyond EOF
+swapext: Invalid argument
+file2 range entirely beyond EOF
+swapext: Invalid argument
+Both ranges entirely beyond EOF
+swapext: Invalid argument
+file1 range crossing EOF
+swapext: Invalid argument
+file2 range crossing EOF
+swapext: Invalid argument
+Both ranges crossing EOF
+swapext: Invalid argument
+file1 unaligned EOF to file2 nowhere near EOF
+swapext: Invalid argument
+file2 unaligned EOF to file1 nowhere near EOF
+swapext: Invalid argument
+Files of unequal length
+swapext: Bad address
+Files on different filesystems
+swapext: Invalid cross-device link
+Files on different mounts
+swapext: Invalid cross-device link
+Swapping a file with itself
+swapext: Invalid argument
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 718
+#
+# Make sure swapext honors RLIMIT_FSIZE.
+
+. ./common/preamble
+_begin_fstest auto quick fiexchange swapext
+
+# Override the default cleanup function.
+_cleanup()
+{
+ cd /
+ rm -r -f $tmp.* $dir
+}
+
+# Import common functions.
+. ./common/filter
+. ./common/reflink
+
+# real QA test starts here
+_require_xfs_io_command swapext '-v exchrange'
+_require_test
+
+dir=$TEST_DIR/test-$seq
+mkdir -p $dir
+blksz=65536
+nrblks=64
+
+# Create some 4M files to test swapext
+_pwrite_byte 0x58 0 $((blksz * nrblks)) $dir/a >> $seqres.full
+_pwrite_byte 0x59 0 $((blksz * nrblks)) $dir/b >> $seqres.full
+sync
+md5sum $dir/a $dir/b | _filter_test_dir
+
+# Set FSIZE to twice the blocksize (IOWs, 128k)
+ulimit -f $(( (blksz * 2) / 512))
+ulimit -a >> $seqres.full
+
+# Now try to swapext
+$XFS_IO_PROG -c "swapext $dir/b" $dir/a
+md5sum $dir/a $dir/b | _filter_test_dir
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 718
+d712f003e9d467e063cda1baf319b928 TEST_DIR/test-718/a
+901e136269b8d283d311697b7c6dc1f2 TEST_DIR/test-718/b
+swapext: Invalid argument
+d712f003e9d467e063cda1baf319b928 TEST_DIR/test-718/a
+901e136269b8d283d311697b7c6dc1f2 TEST_DIR/test-718/b
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 719
+#
+# Test atomic file replacement when (a) the length is the same; (b) the length
+# is different; and (c) someone modifies the original file and we need to
+# cancel the update. The staging file is created empty, which implies that the
+# caller wants a full file replacement.
+
+. ./common/preamble
+_begin_fstest auto quick fiexchange swapext
+
+# Override the default cleanup function.
+_cleanup()
+{
+ cd /
+ rm -r -f $tmp.* $dir
+}
+
+# Import common functions.
+. ./common/filter
+
+# real QA test starts here
+_require_xfs_io_command swapext '-v exchrange'
+_require_xfs_io_command startupdate '-e'
+_require_test
+
+filesnap() {
+ echo "$1"
+ md5sum $2 | _filter_test_dir
+}
+
+mkfile() {
+ rm -f $dir/a
+ _pwrite_byte 0x58 0 $((blksz * nrblks)) $dir/a >> $seqres.full
+ sync
+}
+
+dir=$TEST_DIR/test-$seq
+mkdir -p $dir
+blksz=65536
+nrblks=64
+
+# Use the atomic file update staging prototype in xfs_io to update a file.
+mkfile
+filesnap "before commit" $dir/a
+
+$XFS_IO_PROG \
+ -c 'startupdate -e' \
+ -c "pwrite -S 0x60 0 $((blksz * nrblks))" \
+ -c 'commitupdate -q' \
+ "$dir/a" 2>&1 | _filter_xfs_io | _filter_test_dir
+
+filesnap "after commit" $dir/a
+echo
+
+# Use the atomic file updates to replace a file with a shorter file.
+mkfile
+filesnap "before shorten commit" $dir/a
+
+$XFS_IO_PROG \
+ -c 'startupdate -e' \
+ -c 'pwrite -S 0x60 0 55k' \
+ -c 'commitupdate -q' \
+ "$dir/a" 2>&1 | _filter_xfs_io | _filter_test_dir
+
+filesnap "after shorten commit" $dir/a
+echo
+
+# Use the atomic file updates to replace a file with a longer file.
+mkfile
+filesnap "before lengthen commit" $dir/a
+
+$XFS_IO_PROG \
+ -c 'startupdate -e' \
+ -c "pwrite -S 0x60 0 $(( (blksz * nrblks) + 37373 ))" \
+ -c 'commitupdate -q' \
+ "$dir/a" 2>&1 | _filter_xfs_io | _filter_test_dir
+
+filesnap "after lengthen commit" $dir/a
+echo
+
+# Now try the update but with the A file open separately so that we clobber
+# mtime and fail the update.
+mkfile
+filesnap "before fail commit" $dir/a
+
+$XFS_IO_PROG \
+ -c "open $dir/a" \
+ -c 'startupdate -e ' \
+ -c 'pwrite -S 0x58 44k 55k -b 1m' \
+ -c 'file 0' \
+ -c 'close' \
+ -c 'pwrite -S 0x61 22k 11k -b 1m' \
+ -c 'commitupdate -q' \
+ "$dir/a" 2>&1 | _filter_xfs_io | _filter_test_dir
+
+filesnap "after fail commit" $dir/a
+echo
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 719
+before commit
+d712f003e9d467e063cda1baf319b928 TEST_DIR/test-719/a
+wrote 4194304/4194304 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Committed updates to 'TEST_DIR/test-719/a'.
+after commit
+0558063c531ca7c7864fc5a4923f7144 TEST_DIR/test-719/a
+
+before shorten commit
+d712f003e9d467e063cda1baf319b928 TEST_DIR/test-719/a
+wrote 56320/56320 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Committed updates to 'TEST_DIR/test-719/a'.
+after shorten commit
+52353039d89c5f2b76b9003464e5276a TEST_DIR/test-719/a
+
+before lengthen commit
+d712f003e9d467e063cda1baf319b928 TEST_DIR/test-719/a
+wrote 4231677/4231677 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Committed updates to 'TEST_DIR/test-719/a'.
+after lengthen commit
+1839e7c6bf616160dc51b12179db2642 TEST_DIR/test-719/a
+
+before fail commit
+d712f003e9d467e063cda1baf319b928 TEST_DIR/test-719/a
+committing update: Device or resource busy
+wrote 56320/56320 bytes at offset 45056
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 56320/56320 bytes at offset 45056
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+[000] TEST_DIR/test-719/a (xfs,non-sync,non-direct,read-write)
+ 001 TEST_DIR/test-719/a (fileupdate) (xfs,non-sync,non-direct,read-write)
+[000] TEST_DIR/test-719/a (fileupdate) (xfs,non-sync,non-direct,read-write)
+wrote 11264/11264 bytes at offset 22528
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+after fail commit
+d712f003e9d467e063cda1baf319b928 TEST_DIR/test-719/a
+
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 720
+#
+# Stress testing with a lot of extents.
+
+. ./common/preamble
+_begin_fstest auto quick fiexchange swapext
+
+# Override the default cleanup function.
+_cleanup()
+{
+ cd /
+ rm -r -f $tmp.* $dir
+}
+
+# Import common functions.
+. ./common/filter
+
+# real QA test starts here
+_require_xfs_io_command swapext '-v exchrange'
+_require_test_program punch-alternating
+_require_test
+
+dir=$TEST_DIR/test-$seq
+mkdir -p $dir
+blksz=$(_get_file_block_size $TEST_DIR)
+nrblks=$((LOAD_FACTOR * 100000))
+
+_require_fs_space $TEST_DIR $(( (2 * blksz * nrblks) / 1024 ))
+
+# Create some big swiss cheese files to test swapext with a lot of extents
+_pwrite_byte 0x58 0 $((blksz * nrblks)) $dir/a >> $seqres.full
+$here/src/punch-alternating $dir/a
+_pwrite_byte 0x59 0 $((blksz * nrblks)) $dir/b >> $seqres.full
+$here/src/punch-alternating -o 1 $dir/b
+filefrag -v $dir/a $dir/b >> $seqres.full
+
+# Now try to swapext
+md5_a="$(md5sum < $dir/a)"
+md5_b="$(md5sum < $dir/b)"
+date >> $seqres.full
+$XFS_IO_PROG -c "swapext $dir/b" $dir/a
+date >> $seqres.full
+
+echo "md5_a=$md5_a" >> $seqres.full
+echo "md5_b=$md5_b" >> $seqres.full
+md5sum $dir/a $dir/b >> $seqres.full
+
+test "$(md5sum < $dir/b)" = "$md5_a" || echo "file b does not match former a"
+test "$(md5sum < $dir/a)" = "$md5_b" || echo "file a does not match former b"
+
+echo "Silence is golden!"
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 720
+Silence is golden!
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 721
+#
+# Test non-root atomic file updates when (a) the file contents are cloned into
+# the staging file; and (b) when the staging file is created empty.
+
+. ./common/preamble
+_begin_fstest auto quick fiexchange swapext
+
+# Override the default cleanup function.
+_cleanup()
+{
+ cd /
+ rm -r -f $tmp.* $dir
+}
+
+# Import common functions.
+. ./common/filter
+. ./common/reflink
+
+# real QA test starts here
+_require_xfs_io_command startupdate
+_require_test_reflink
+_require_test
+_require_user
+
+filesnap() {
+ echo "$1"
+ md5sum $2 | _filter_test_dir
+}
+
+mkfile() {
+ rm -f $dir/a
+ _pwrite_byte 0x58 0 $((blksz * nrblks)) $dir/a >> $seqres.full
+ chown $qa_user $dir/a $dir/
+ sync
+}
+
+dir=$TEST_DIR/test-$seq
+mkdir -p $dir
+blksz=65536
+nrblks=64
+
+# Use the atomic file update staging prototype in xfs_io to update a file.
+mkfile
+filesnap "before commit" $dir/a
+
+cmd="$XFS_IO_PROG \
+ -c 'startupdate' \
+ -c 'pwrite -S 0x60 44k 55k -b 1m' \
+ -c 'commitupdate -q' \
+ \"$dir/a\""
+su -s /bin/bash -c "$cmd" $qa_user 2>&1 | _filter_xfs_io | _filter_test_dir
+
+filesnap "after commit" $dir/a
+echo
+
+# Use the atomic file updates to replace a file with a shorter file.
+mkfile
+filesnap "before shorten commit" $dir/a
+
+cmd="$XFS_IO_PROG \
+ -c 'startupdate' \
+ -c 'truncate 55k' \
+ -c 'pwrite -S 0x60 0 55k' \
+ -c 'commitupdate -q' \
+ \"$dir/a\""
+su -s /bin/bash -c "$cmd" $qa_user 2>&1 | _filter_xfs_io | _filter_test_dir
+
+filesnap "after shorten commit" $dir/a
+echo
+
+# Use the atomic file updates to replace a file with a longer file.
+mkfile
+filesnap "before lengthen commit" $dir/a
+
+cmd="$XFS_IO_PROG \
+ -c 'startupdate' \
+ -c \"pwrite -S 0x60 0 $(( (blksz * nrblks) + 37373 ))\" \
+ -c 'commitupdate -q' \
+ \"$dir/a\""
+su -s /bin/bash -c "$cmd" $qa_user 2>&1 | _filter_xfs_io | _filter_test_dir
+
+filesnap "after lengthen commit" $dir/a
+echo
+
+# Use the atomic file update staging prototype in xfs_io to cancel updating a
+# file.
+mkfile
+filesnap "before cancel" $dir/a
+
+cmd="$XFS_IO_PROG \
+ -c 'startupdate' \
+ -c 'pwrite -S 0x60 44k 55k -b 1m' \
+ -c 'cancelupdate' \
+ \"$dir/a\""
+su -s /bin/bash -c "$cmd" $qa_user 2>&1 | _filter_xfs_io | _filter_test_dir
+
+filesnap "after cancel" $dir/a
+echo
+
+# Now try the update but with the A file open separately so that we clobber
+# mtime and fail the update.
+mkfile
+filesnap "before fail commit" $dir/a
+
+cmd="$XFS_IO_PROG \
+ -c \"open $dir/a\" \
+ -c 'startupdate' \
+ -c 'pwrite -S 0x58 44k 55k -b 1m' \
+ -c 'file 0' \
+ -c 'close' \
+ -c 'pwrite -S 0x61 22k 11k -b 1m' \
+ -c 'commitupdate -q' \
+ \"$dir/a\""
+su -s /bin/bash -c "$cmd" $qa_user 2>&1 | _filter_xfs_io | _filter_test_dir
+
+filesnap "after fail commit" $dir/a
+echo
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 721
+before commit
+d712f003e9d467e063cda1baf319b928 TEST_DIR/test-721/a
+wrote 56320/56320 bytes at offset 45056
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Committed updates to 'TEST_DIR/test-721/a'.
+after commit
+bedbd22b58a680219a1225353f6195fa TEST_DIR/test-721/a
+
+before shorten commit
+d712f003e9d467e063cda1baf319b928 TEST_DIR/test-721/a
+wrote 56320/56320 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Committed updates to 'TEST_DIR/test-721/a'.
+after shorten commit
+52353039d89c5f2b76b9003464e5276a TEST_DIR/test-721/a
+
+before lengthen commit
+d712f003e9d467e063cda1baf319b928 TEST_DIR/test-721/a
+wrote 4231677/4231677 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Committed updates to 'TEST_DIR/test-721/a'.
+after lengthen commit
+1839e7c6bf616160dc51b12179db2642 TEST_DIR/test-721/a
+
+before cancel
+d712f003e9d467e063cda1baf319b928 TEST_DIR/test-721/a
+wrote 56320/56320 bytes at offset 45056
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Cancelled updates to 'TEST_DIR/test-721/a'.
+after cancel
+d712f003e9d467e063cda1baf319b928 TEST_DIR/test-721/a
+
+before fail commit
+d712f003e9d467e063cda1baf319b928 TEST_DIR/test-721/a
+committing update: Device or resource busy
+wrote 56320/56320 bytes at offset 45056
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 56320/56320 bytes at offset 45056
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+[000] TEST_DIR/test-721/a (xfs,non-sync,non-direct,read-write)
+ 001 TEST_DIR/test-721/a (fileupdate) (xfs,non-sync,non-direct,read-write)
+[000] TEST_DIR/test-721/a (fileupdate) (xfs,non-sync,non-direct,read-write)
+wrote 11264/11264 bytes at offset 22528
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+after fail commit
+d712f003e9d467e063cda1baf319b928 TEST_DIR/test-721/a
+
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 722
+#
+# Test swapext with the fsync flag flushes everything to disk before the call
+# returns.
+
+. ./common/preamble
+_begin_fstest auto quick fiexchange swapext
+
+# Override the default cleanup function.
+_cleanup()
+{
+ cd /
+ rm -r -f $tmp.* $dir
+}
+
+# Import common functions.
+. ./common/filter
+. ./common/reflink
+
+# real QA test starts here
+_require_test_program "punch-alternating"
+_require_xfs_io_command swapext '-v exchrange -a'
+_require_scratch
+_require_scratch_shutdown
+
+_scratch_mkfs >> $seqres.full
+_scratch_mount
+
+_pwrite_byte 0x58 0 2m $SCRATCH_MNT/a >> $seqres.full
+_pwrite_byte 0x59 0 2m $SCRATCH_MNT/b >> $seqres.full
+$here/src/punch-alternating $SCRATCH_MNT/a
+$here/src/punch-alternating $SCRATCH_MNT/b
+
+old_a=$(md5sum $SCRATCH_MNT/a | awk '{print $1}')
+old_b=$(md5sum $SCRATCH_MNT/b | awk '{print $1}')
+echo "md5 a: $old_a md5 b: $old_b" >> $seqres.full
+
+od -tx1 -Ad -c $SCRATCH_MNT/a > /tmp/a0
+od -tx1 -Ad -c $SCRATCH_MNT/b > /tmp/b0
+
+echo swap >> $seqres.full
+$XFS_IO_PROG -c "swapext -v exchrange -a -e -f -u $SCRATCH_MNT/a" $SCRATCH_MNT/b
+_scratch_shutdown
+_scratch_cycle_mount
+
+new_a=$(md5sum $SCRATCH_MNT/a | awk '{print $1}')
+new_b=$(md5sum $SCRATCH_MNT/b | awk '{print $1}')
+echo "md5 a: $new_a md5 b: $new_b" >> $seqres.full
+
+test $old_a = $new_b || echo "scratch file B doesn't match old file A"
+test $old_b = $new_a || echo "scratch file A doesn't match old file B"
+
+od -tx1 -Ad -c $SCRATCH_MNT/a > /tmp/a1
+od -tx1 -Ad -c $SCRATCH_MNT/b > /tmp/b1
+
+# success, all done
+echo Silence is golden
+status=0
+exit
--- /dev/null
+QA output created by 722
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 723
+#
+# Test swapext with the dry run flag doesn't change anything.
+
+. ./common/preamble
+_begin_fstest auto quick fiexchange swapext
+
+# Override the default cleanup function.
+_cleanup()
+{
+ cd /
+ rm -r -f $tmp.* $dir
+}
+
+# Import common functions.
+. ./common/filter
+. ./common/reflink
+
+# real QA test starts here
+_require_test_program "punch-alternating"
+_require_xfs_io_command swapext '-v exchrange'
+_require_scratch
+
+_scratch_mkfs >> $seqres.full
+_scratch_mount
+
+_pwrite_byte 0x58 0 1m $SCRATCH_MNT/a >> $seqres.full
+_pwrite_byte 0x59 0 2m $SCRATCH_MNT/b >> $seqres.full
+$XFS_IO_PROG -c 'truncate 2m' $SCRATCH_MNT/a
+$here/src/punch-alternating $SCRATCH_MNT/a
+$here/src/punch-alternating $SCRATCH_MNT/b
+
+old_a=$(md5sum $SCRATCH_MNT/a | awk '{print $1}')
+old_b=$(md5sum $SCRATCH_MNT/b | awk '{print $1}')
+echo "md5 a: $old_a md5 b: $old_b" >> $seqres.full
+
+# Test swapext with the -n option, which will do all the input parameter
+# checking and return 0 without changing anything.
+echo dry run swap >> $seqres.full
+$XFS_IO_PROG -c "swapext -v exchrange -n -f -u $SCRATCH_MNT/a" $SCRATCH_MNT/b
+_scratch_cycle_mount
+
+new_a=$(md5sum $SCRATCH_MNT/a | awk '{print $1}')
+new_b=$(md5sum $SCRATCH_MNT/b | awk '{print $1}')
+echo "md5 a: $new_a md5 b: $new_b" >> $seqres.full
+
+test $old_a = $new_a || echo "scratch file A should not have swapped"
+test $old_b = $new_b || echo "scratch file B should not have swapped"
+
+# Do it again, but without the -n option, to prove that we can actually
+# swap the file contents.
+echo actual swap >> $seqres.full
+$XFS_IO_PROG -c "swapext -v exchrange -f -u $SCRATCH_MNT/a" $SCRATCH_MNT/b
+_scratch_cycle_mount
+
+new_a=$(md5sum $SCRATCH_MNT/a | awk '{print $1}')
+new_b=$(md5sum $SCRATCH_MNT/b | awk '{print $1}')
+echo "md5 a: $new_a md5 b: $new_b" >> $seqres.full
+
+test $old_a = $new_b || echo "scratch file A should have swapped"
+test $old_b = $new_a || echo "scratch file B should have swapped"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
--- /dev/null
+QA output created by 723
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 724
+#
+# Test scatter-gather atomic file writes. We create a temporary file, write
+# sparsely to it, then use XFS_EXCH_RANGE_FILE1_WRITTEN flag to swap
+# atomicallly only the ranges that we wrote.
+
+. ./common/preamble
+_begin_fstest auto quick fiexchange swapext
+
+# Override the default cleanup function.
+_cleanup()
+{
+ cd /
+ rm -r -f $tmp.* $dir
+}
+
+# Import common functions.
+. ./common/filter
+
+# real QA test starts here
+_require_xfs_io_command swapext '-v exchrange -a'
+_require_scratch
+
+_scratch_mkfs >> $seqres.full
+_scratch_mount
+_require_congruent_file_oplen $SCRATCH_MNT 65536
+
+# Create original file
+_pwrite_byte 0x58 0 1m $SCRATCH_MNT/a >> $seqres.full
+
+# Create the donor file
+$XFS_IO_PROG -f -c 'truncate 1m' $SCRATCH_MNT/b
+_pwrite_byte 0x59 64k 64k $SCRATCH_MNT/b >> $seqres.full
+_pwrite_byte 0x57 768k 64k $SCRATCH_MNT/b >> $seqres.full
+
+md5sum $SCRATCH_MNT/a | _filter_scratch
+md5sum $SCRATCH_MNT/b | _filter_scratch
+
+# Test swapext. -h means skip holes in /b, and -e means operate to EOF
+echo swap | tee -a $seqres.full
+$XFS_IO_PROG -c "swapext -v exchrange -f -u -h -e -a $SCRATCH_MNT/b" $SCRATCH_MNT/a
+_scratch_cycle_mount
+
+md5sum $SCRATCH_MNT/a | _filter_scratch
+md5sum $SCRATCH_MNT/b | _filter_scratch
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 724
+310f146ce52077fcd3308dcbe7632bb2 SCRATCH_MNT/a
+c9fb827e2e3e579dc2a733ddad486d1d SCRATCH_MNT/b
+swap
+e9cbfe8489a68efaa5fcf40cf3106118 SCRATCH_MNT/a
+faf8ed02a5b0638096a817abcc6c2127 SCRATCH_MNT/b
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 725
+#
+# Test scatter-gather atomic file commits. Use the startupdate command to
+# create a temporary file, write sparsely to it, then commitupdate -h to
+# perform the scattered update.
+
+. ./common/preamble
+_begin_fstest auto quick fiexchange swapext
+
+# Override the default cleanup function.
+_cleanup()
+{
+ cd /
+ rm -r -f $tmp.* $dir
+}
+
+# Import common functions.
+. ./common/filter
+
+# real QA test starts here
+_require_xfs_io_command swapext '-v exchrange -a'
+_require_xfs_io_command startupdate '-e'
+_require_scratch
+
+_scratch_mkfs >> $seqres.full
+_scratch_mount
+_require_congruent_file_oplen $SCRATCH_MNT 65536
+
+# Create original file
+_pwrite_byte 0x58 0 1m $SCRATCH_MNT/a >> $seqres.full
+sync
+md5sum $SCRATCH_MNT/a | _filter_scratch
+
+# Test atomic scatter-gather file commits.
+echo commit | tee -a $seqres.full
+$XFS_IO_PROG \
+ -c 'bmap -elpv' \
+ -c 'startupdate -e' \
+ -c 'truncate 1m' \
+ -c 'pwrite -S 0x59 64k 64k' \
+ -c 'pwrite -S 0x57 768k 64k' \
+ -c 'bmap -elpv' \
+ -c 'commitupdate -h -k' \
+ -c 'bmap -elpv' \
+ $SCRATCH_MNT/a >> $seqres.full
+_scratch_cycle_mount
+
+md5sum $SCRATCH_MNT/a | _filter_scratch
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 725
+310f146ce52077fcd3308dcbe7632bb2 SCRATCH_MNT/a
+commit
+e9cbfe8489a68efaa5fcf40cf3106118 SCRATCH_MNT/a
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 726
+#
+# Functional test for dropping suid and sgid bits as part of an atomic file
+# commit.
+#
+. ./common/preamble
+_begin_fstest auto fiexchange swapext quick
+
+# Override the default cleanup function.
+# _cleanup()
+# {
+# cd /
+# rm -r -f $tmp.*
+# }
+
+# Import common functions.
+. ./common/filter
+
+# real QA test starts here
+
+# Modify as appropriate.
+_supported_fs generic
+_require_user
+_require_xfs_io_command swapext '-v exchrange -a'
+_require_xfs_io_command startupdate
+_require_scratch
+
+_scratch_mkfs >> $seqres.full
+_scratch_mount
+_require_congruent_file_oplen $SCRATCH_MNT 1048576
+chmod a+rw $SCRATCH_MNT/
+
+setup_testfile() {
+ rm -f $SCRATCH_MNT/a
+ _pwrite_byte 0x58 0 1m $SCRATCH_MNT/a >> $seqres.full
+ sync
+}
+
+commit_and_check() {
+ local user="$1"
+
+ md5sum $SCRATCH_MNT/a | _filter_scratch
+ stat -c '%a %A %n' $SCRATCH_MNT/a | _filter_scratch
+
+ local cmd="$XFS_IO_PROG -c 'startupdate' -c 'pwrite -S 0x57 0 1m' -c 'commitupdate' $SCRATCH_MNT/a"
+ if [ -n "$user" ]; then
+ su - "$user" -c "$cmd" >> $seqres.full
+ else
+ $SHELL -c "$cmd" >> $seqres.full
+ fi
+
+ _scratch_cycle_mount
+ md5sum $SCRATCH_MNT/a | _filter_scratch
+ stat -c '%a %A %n' $SCRATCH_MNT/a | _filter_scratch
+
+ # Blank line in output
+ echo
+}
+
+# Commit to a non-exec file by an unprivileged user clears suid but leaves
+# sgid.
+echo "Test 1 - qa_user, non-exec file"
+setup_testfile
+chmod a+rws $SCRATCH_MNT/a
+commit_and_check "$qa_user"
+
+# Commit to a group-exec file by an unprivileged user clears suid and sgid.
+echo "Test 2 - qa_user, group-exec file"
+setup_testfile
+chmod g+x,a+rws $SCRATCH_MNT/a
+commit_and_check "$qa_user"
+
+# Commit to a user-exec file by an unprivileged user clears suid but not sgid.
+echo "Test 3 - qa_user, user-exec file"
+setup_testfile
+chmod u+x,a+rws,g-x $SCRATCH_MNT/a
+commit_and_check "$qa_user"
+
+# Commit to a all-exec file by an unprivileged user clears suid and sgid.
+echo "Test 4 - qa_user, all-exec file"
+setup_testfile
+chmod a+rwxs $SCRATCH_MNT/a
+commit_and_check "$qa_user"
+
+# Commit to a non-exec file by root leaves suid and sgid.
+echo "Test 5 - root, non-exec file"
+setup_testfile
+chmod a+rws $SCRATCH_MNT/a
+commit_and_check
+
+# Commit to a group-exec file by root leaves suid and sgid.
+echo "Test 6 - root, group-exec file"
+setup_testfile
+chmod g+x,a+rws $SCRATCH_MNT/a
+commit_and_check
+
+# Commit to a user-exec file by root leaves suid and sgid.
+echo "Test 7 - root, user-exec file"
+setup_testfile
+chmod u+x,a+rws,g-x $SCRATCH_MNT/a
+commit_and_check
+
+# Commit to a all-exec file by root leaves suid and sgid.
+echo "Test 8 - root, all-exec file"
+setup_testfile
+chmod a+rwxs $SCRATCH_MNT/a
+commit_and_check
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 726
+Test 1 - qa_user, non-exec file
+310f146ce52077fcd3308dcbe7632bb2 SCRATCH_MNT/a
+6666 -rwSrwSrw- SCRATCH_MNT/a
+3784de23efab7a2074c9ec66901e39e5 SCRATCH_MNT/a
+666 -rw-rw-rw- SCRATCH_MNT/a
+
+Test 2 - qa_user, group-exec file
+310f146ce52077fcd3308dcbe7632bb2 SCRATCH_MNT/a
+6676 -rwSrwsrw- SCRATCH_MNT/a
+3784de23efab7a2074c9ec66901e39e5 SCRATCH_MNT/a
+676 -rw-rwxrw- SCRATCH_MNT/a
+
+Test 3 - qa_user, user-exec file
+310f146ce52077fcd3308dcbe7632bb2 SCRATCH_MNT/a
+6766 -rwsrwSrw- SCRATCH_MNT/a
+3784de23efab7a2074c9ec66901e39e5 SCRATCH_MNT/a
+766 -rwxrw-rw- SCRATCH_MNT/a
+
+Test 4 - qa_user, all-exec file
+310f146ce52077fcd3308dcbe7632bb2 SCRATCH_MNT/a
+6777 -rwsrwsrwx SCRATCH_MNT/a
+3784de23efab7a2074c9ec66901e39e5 SCRATCH_MNT/a
+777 -rwxrwxrwx SCRATCH_MNT/a
+
+Test 5 - root, non-exec file
+310f146ce52077fcd3308dcbe7632bb2 SCRATCH_MNT/a
+6666 -rwSrwSrw- SCRATCH_MNT/a
+3784de23efab7a2074c9ec66901e39e5 SCRATCH_MNT/a
+6666 -rwSrwSrw- SCRATCH_MNT/a
+
+Test 6 - root, group-exec file
+310f146ce52077fcd3308dcbe7632bb2 SCRATCH_MNT/a
+6676 -rwSrwsrw- SCRATCH_MNT/a
+3784de23efab7a2074c9ec66901e39e5 SCRATCH_MNT/a
+6676 -rwSrwsrw- SCRATCH_MNT/a
+
+Test 7 - root, user-exec file
+310f146ce52077fcd3308dcbe7632bb2 SCRATCH_MNT/a
+6766 -rwsrwSrw- SCRATCH_MNT/a
+3784de23efab7a2074c9ec66901e39e5 SCRATCH_MNT/a
+6766 -rwsrwSrw- SCRATCH_MNT/a
+
+Test 8 - root, all-exec file
+310f146ce52077fcd3308dcbe7632bb2 SCRATCH_MNT/a
+6777 -rwsrwsrwx SCRATCH_MNT/a
+3784de23efab7a2074c9ec66901e39e5 SCRATCH_MNT/a
+6777 -rwsrwsrwx SCRATCH_MNT/a
+
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 727
+#
+# Functional test for dropping capability bits as part of an atomic file
+# commit.
+#
+. ./common/preamble
+_begin_fstest auto fiexchange swapext quick
+
+# Override the default cleanup function.
+# _cleanup()
+# {
+# cd /
+# rm -r -f $tmp.*
+# }
+
+# Import common functions.
+. ./common/filter
+. ./common/attr
+
+# real QA test starts here
+
+# Modify as appropriate.
+_supported_fs generic
+_require_user
+_require_command "$GETCAP_PROG" getcap
+_require_command "$SETCAP_PROG" setcap
+_require_xfs_io_command swapext '-v exchrange -a'
+_require_xfs_io_command startupdate
+_require_scratch
+_require_attrs security
+
+_scratch_mkfs >> $seqres.full
+_scratch_mount
+_require_congruent_file_oplen $SCRATCH_MNT 1048576
+chmod a+rw $SCRATCH_MNT/
+
+setup_testfile() {
+ rm -f $SCRATCH_MNT/a $SCRATCH_MNT/b
+ _pwrite_byte 0x58 0 1m $SCRATCH_MNT/a >> $seqres.full
+ _pwrite_byte 0x57 0 1m $SCRATCH_MNT/b >> $seqres.full
+ chmod a+rw $SCRATCH_MNT/a $SCRATCH_MNT/b
+ $SETCAP_PROG cap_setgid,cap_setuid+ep $SCRATCH_MNT/a
+ sync
+}
+
+commit_and_check() {
+ local user="$1"
+
+ md5sum $SCRATCH_MNT/a | _filter_scratch
+ stat -c '%a %A %n' $SCRATCH_MNT/a | _filter_scratch
+ _getcap -v $SCRATCH_MNT/a | _filter_scratch
+
+ local cmd="$XFS_IO_PROG -c 'startupdate' -c 'pwrite -S 0x57 0 1m' -c 'commitupdate' $SCRATCH_MNT/a"
+ if [ -n "$user" ]; then
+ su - "$user" -c "$cmd" >> $seqres.full
+ else
+ $SHELL -c "$cmd" >> $seqres.full
+ fi
+
+ _scratch_cycle_mount
+ md5sum $SCRATCH_MNT/a | _filter_scratch
+ stat -c '%a %A %n' $SCRATCH_MNT/a | _filter_scratch
+ _getcap -v $SCRATCH_MNT/a | _filter_scratch
+
+ # Blank line in output
+ echo
+}
+
+# Commit by an unprivileged user clears capability bits.
+echo "Test 1 - qa_user"
+setup_testfile
+commit_and_check "$qa_user"
+
+# Commit by root leaves capability bits.
+echo "Test 2 - root"
+setup_testfile
+commit_and_check
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 727
+Test 1 - qa_user
+310f146ce52077fcd3308dcbe7632bb2 SCRATCH_MNT/a
+666 -rw-rw-rw- SCRATCH_MNT/a
+SCRATCH_MNT/a cap_setgid,cap_setuid=ep
+3784de23efab7a2074c9ec66901e39e5 SCRATCH_MNT/a
+666 -rw-rw-rw- SCRATCH_MNT/a
+SCRATCH_MNT/a
+
+Test 2 - root
+310f146ce52077fcd3308dcbe7632bb2 SCRATCH_MNT/a
+666 -rw-rw-rw- SCRATCH_MNT/a
+SCRATCH_MNT/a cap_setgid,cap_setuid=ep
+3784de23efab7a2074c9ec66901e39e5 SCRATCH_MNT/a
+666 -rw-rw-rw- SCRATCH_MNT/a
+SCRATCH_MNT/a
+
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023 Netapp Inc., All Rights Reserved.
+#
+# FS QA Test 728
+#
+# Test a bug where the NFS client wasn't sending a post-op GETATTR to the
+# server after setting an xattr, resulting in `stat` reporting a stale ctime.
+#
+. ./common/preamble
+_begin_fstest auto quick attr
+
+# Import common functions
+. ./common/attr
+
+# real QA test starts here
+_supported_fs generic
+_require_test
+_require_attrs
+
+rm -rf $TEST_DIR/testfile
+touch $TEST_DIR/testfile
+
+check_xattr_op()
+{
+ what=$1
+ shift 1
+
+ before_ctime=$(stat -c %z $TEST_DIR/testfile)
+ # maximum known ctime granularity is 2s (e.g. FAT)
+ sleep 2
+ $SETFATTR_PROG $* $TEST_DIR/testfile
+ after_ctime=$(stat -c %z $TEST_DIR/testfile)
+
+ test "$before_ctime" != "$after_ctime" || echo "Expected ctime to change after $what."
+}
+
+check_xattr_op setxattr -n user.foobar -v 123
+check_xattr_op removexattr -x user.foobar
+
+echo "Silence is golden"
+status=0
+exit
--- /dev/null
+QA output created by 728
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023 Red Hat, Inc. All Rights Reserved.
+#
+# FS QA Test No. 729
+#
+# Trigger page faults in the same file during read and write
+#
+# This is generic/647 with an additional test that writes a memory-mapped page
+# onto itself using direct I/O.
+#
+# The kernel will invalidate the page cache before carrying out the write, so
+# filesystems that fault in the page and then carry out the direct I/O write
+# with page faults disabled will never make any progress.
+#
+. ./common/preamble
+_begin_fstest auto quick
+
+# Override the default cleanup function.
+_cleanup()
+{
+ cd /
+ rm -f $tmp.*
+ rm -f $TEST_DIR/mmap-rw-fault.tmp
+}
+
+# Import common functions.
+. ./common/filter
+
+# real QA test starts here
+
+_supported_fs generic
+_require_test
+_require_odirect
+_require_test_program mmap-rw-fault
+
+echo "Silence is golden"
+
+$here/src/mmap-rw-fault -2 $TEST_DIR/mmap-rw-fault.tmp
+
+status=$?
+exit
--- /dev/null
+QA output created by 729
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2015 Red Hat Inc. All Rights Reserved.
+# Copyright (c) 2023 Christoph Hellwig
+#
+# Test proper file system shut down when the block device is removed underneath
+# and there is dirty data.
+#
+. ./common/preamble
+_begin_fstest auto quick
+
+_cleanup()
+{
+ cd /
+ $UMOUNT_PROG $SCSI_DEBUG_MNT >>$seqres.full 2>&1
+ _put_scsi_debug_dev
+ rm -f $tmp.*
+}
+
+. ./common/filter
+. ./common/scsi_debug
+
+_supported_fs generic
+
+# We don't actually use the test device, but we need a block based fs
+_require_test
+_require_block_device $TEST_DEV
+_require_scsi_debug
+
+size=$(_small_fs_size_mb 256)
+SCSI_DEBUG_DEV=`_get_scsi_debug_dev 512 512 0 $size`
+test -b "$SCSI_DEBUG_DEV" || _notrun "Failed to initialize scsi debug device"
+echo "SCSI debug device $SCSI_DEBUG_DEV" >>$seqres.full
+
+run_check _mkfs_dev $SCSI_DEBUG_DEV
+
+SCSI_DEBUG_MNT="$TEST_DIR/scsi_debug_$seq"
+rm -rf $SCSI_DEBUG_MNT
+mkdir $SCSI_DEBUG_MNT
+run_check _mount $SCSI_DEBUG_DEV $SCSI_DEBUG_MNT
+
+# create a test file
+$XFS_IO_PROG -f -c "pwrite 0 1M" $SCSI_DEBUG_MNT/testfile >>$seqres.full
+
+# open a file descriptor for reading the file
+exec 3< $SCSI_DEBUG_MNT/testfile
+
+# delete the scsi debug device while it still has dirty data
+echo 1 > /sys/block/$(_short_dev $SCSI_DEBUG_DEV)/device/delete
+
+# try to read from the file, which should give us -EIO
+cat <&3 > /dev/null
+
+# close the file descriptor to not block unmount
+exec 3<&-
+
+status=0
+exit
--- /dev/null
+QA output created by 730
+cat: -: Input/output error
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2015 Red Hat Inc. All Rights Reserved.
+# Copyright (c) 2023 Christoph Hellwig
+#
+# Test proper file system shut down when the block device is removed underneath
+# and it has no dirty data.
+#
+#
+. ./common/preamble
+_begin_fstest auto quick
+
+_cleanup()
+{
+ cd /
+ $UMOUNT_PROG $SCSI_DEBUG_MNT >>$seqres.full 2>&1
+ _put_scsi_debug_dev
+ rm -f $tmp.*
+}
+
+. ./common/filter
+. ./common/scsi_debug
+
+# We don't actually use the test device, but we need a block based fs
+_require_test
+_require_block_device $TEST_DEV
+_supported_fs generic
+_require_scsi_debug
+
+size=$(_small_fs_size_mb 256)
+SCSI_DEBUG_DEV=`_get_scsi_debug_dev 512 512 0 $size`
+test -b "$SCSI_DEBUG_DEV" || _notrun "Failed to initialize scsi debug device"
+echo "SCSI debug device $SCSI_DEBUG_DEV" >>$seqres.full
+
+run_check _mkfs_dev $SCSI_DEBUG_DEV
+
+SCSI_DEBUG_MNT="$TEST_DIR/scsi_debug_$seq"
+rm -rf $SCSI_DEBUG_MNT
+mkdir $SCSI_DEBUG_MNT
+run_check _mount $SCSI_DEBUG_DEV $SCSI_DEBUG_MNT
+
+# create a test file
+$XFS_IO_PROG -f -c "pwrite 0 1M" -c "fsync" $SCSI_DEBUG_MNT/testfile >>$seqres.full
+
+# open a file descriptor for reading the file
+exec 3< $SCSI_DEBUG_MNT/testfile
+
+# drop all caches and delete the scsi debug device
+echo 3 > /proc/sys/vm/drop_caches
+echo 1 > /sys/block/`_short_dev $SCSI_DEBUG_DEV`/device/delete
+
+# try to read from the file, which should give us -EIO
+cat <&3 > /dev/null
+
+# close the file descriptor to not block unmount
+exec 3<&-
+
+status=0
+exit
--- /dev/null
+QA output created by 731
+cat: -: Input/output error
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023 Red Hat, Inc. All Rights Reserved.
+#
+# FS QA Test No. 732
+#
+# Mount the same export to different mount points and move (rename)
+# files among those mount points.
+# This simple test recently unveils an ancient nfsd bug that is fixed
+# by fdd2630a739819 ("nfsd: fix change_info in NFSv4 RENAME replies").
+#
+. ./common/preamble
+_begin_fstest auto quick rename
+
+# Override the default cleanup function.
+_cleanup()
+{
+ $UMOUNT_PROG $testdir1 2>/dev/null
+ $UMOUNT_PROG $testdir2 2>/dev/null
+ cd /
+ rm -r -f $tmp.*
+}
+
+# real QA test starts here
+_supported_fs ^nfs
+
+_require_test
+_require_scratch
+
+echo "Silence is golden"
+
+_scratch_mkfs >> $seqres.full
+testdir1=$TEST_DIR/mountpoint1-$seq
+testdir2=$TEST_DIR/mountpoint2-$seq
+rm -rf $testdir1 $testdir2
+mkdir -p $testdir1 $testdir2
+
+# Don't share the data and attribute caches among mount points for NFS.
+# This caching behavior is necessary to reproduce this issue as we're
+# checking the alignment of each mount point's own unique cache.
+[ "$FSTYP" = "nfs" ] && MOUNT_OPTIONS="-o nosharecache"
+
+SCRATCH_MNT=$testdir1 _scratch_mount
+SCRATCH_MNT=$testdir2 _scratch_mount
+rm -rf $testdir1/{A,B}
+mkdir $testdir1/{A,B}
+touch $testdir1/A/f
+mv $testdir1/A/f $testdir1/B/
+cat $testdir2/B/f
+mv $testdir2/B/f $testdir2/A/
+cat $testdir1/A/f
+mv $testdir1/A/f $testdir1/B/
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 732
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023, Oracle and/or its affiliates. All Rights Reserved.
+#
+# FS QA Test No. 733
+#
+# Race file reads with a very slow reflink operation to see if the reads
+# actually complete while the reflink is ongoing. This is a functionality
+# test for XFS commit 14a537983b22 "xfs: allow read IO and FICLONE to run
+# concurrently".
+#
+. ./common/preamble
+_begin_fstest auto clone punch
+
+# Import common functions.
+. ./common/filter
+. ./common/attr
+. ./common/reflink
+
+# real QA test starts here
+_supported_fs generic
+_require_scratch_reflink
+_require_cp_reflink
+_require_xfs_io_command "fpunch"
+_require_test_program "punch-alternating"
+_require_test_program "t_reflink_read_race"
+_require_command "$TIMEOUT_PROG" timeout
+
+[ "$FSTYP" = "xfs" ] && _fixed_by_kernel_commit 14a537983b22 \
+ "xfs: allow read IO and FICLONE to run concurrently"
+
+rm -f "$seqres.full"
+
+echo "Format and mount"
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount >> "$seqres.full" 2>&1
+
+testdir="$SCRATCH_MNT/test-$seq"
+mkdir "$testdir"
+
+calc_space() {
+ blocks_needed=$(( 2 ** (fnr + 1) ))
+ space_needed=$((blocks_needed * blksz * 5 / 4))
+}
+
+# Figure out the number of blocks that we need to get the reflink runtime above
+# 1 seconds
+echo "Create a many-block file"
+for ((fnr = 1; fnr < 40; fnr++)); do
+ free_blocks=$(stat -f -c '%a' "$testdir")
+ blksz=$(_get_file_block_size "$testdir")
+ space_avail=$((free_blocks * blksz))
+ calc_space
+ test $space_needed -gt $space_avail && \
+ _notrun "Insufficient space for stress test; would only create $blocks_needed extents."
+
+ off=$(( (2 ** fnr) * blksz))
+ $XFS_IO_PROG -f -c "pwrite -S 0x61 -b 4194304 $off $off" "$testdir/file1" >> "$seqres.full"
+ "$here/src/punch-alternating" "$testdir/file1" >> "$seqres.full"
+
+ $TIMEOUT_PROG 1s cp --reflink=always "$testdir/file1" "$testdir/garbage" || break
+done
+echo "fnr=$fnr" >> $seqres.full
+
+echo "Reflink the big file"
+$here/src/t_reflink_read_race "$testdir/file1" "$testdir/file2" \
+ "$testdir/outcome" &>> $seqres.full
+
+if [ ! -e "$testdir/outcome" ]; then
+ echo "Could not set up program"
+elif grep -q "finished read early" "$testdir/outcome"; then
+ echo "test completed successfully"
+else
+ cat "$testdir/outcome"
+fi
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 733
+Format and mount
+Create a many-block file
+Reflink the big file
+Terminated
+test completed successfully
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 734
+#
+# This is a regression test for the kernel commit noted below. The stale
+# memory exposure can be exploited by creating a file with shared blocks,
+# evicting the page cache for that file, and then funshareing at least one
+# memory page's worth of data. iomap will mark the page uptodate and dirty
+# without ever reading the ondisk contents.
+#
+. ./common/preamble
+_begin_fstest auto quick unshare clone
+
+_cleanup()
+{
+ cd /
+ rm -r -f $tmp.* $testdir
+}
+
+# real QA test starts here
+
+# Import common functions.
+. ./common/filter
+. ./common/reflink
+
+_fixed_by_git_commit kernel 35d30c9cf127 \
+ "iomap: don't skip reading in !uptodate folios when unsharing a range"
+
+# real QA test starts here
+_supported_fs generic
+_require_test_reflink
+_require_cp_reflink
+_require_xfs_io_command "funshare"
+
+testdir=$TEST_DIR/test-$seq
+rm -rf $testdir
+mkdir $testdir
+
+# Create a file that is at least four pages in size and aligned to the
+# file allocation unit size so that we don't trigger any unnecessary zeroing.
+pagesz=$(_get_page_size)
+alloc_unit=$(_get_file_block_size $TEST_DIR)
+filesz=$(( ( (4 * pagesz) + alloc_unit - 1) / alloc_unit * alloc_unit))
+
+echo "Create the original file and a clone"
+_pwrite_byte 0x61 0 $filesz $testdir/file2.chk >> $seqres.full
+_pwrite_byte 0x61 0 $filesz $testdir/file1 >> $seqres.full
+_cp_reflink $testdir/file1 $testdir/file2
+_cp_reflink $testdir/file1 $testdir/file3
+
+_test_cycle_mount
+
+cat $testdir/file3 > /dev/null
+
+echo "Funshare at least one pagecache page"
+$XFS_IO_PROG -c "funshare 0 $filesz" $testdir/file2
+$XFS_IO_PROG -c "funshare 0 $filesz" $testdir/file3
+_pwrite_byte 0x61 0 $filesz $testdir/file2.chk >> $seqres.full
+
+echo "Check contents"
+
+# file2 wasn't cached when it was unshared, but it should match
+if ! cmp -s $testdir/file2.chk $testdir/file2; then
+ echo "file2.chk does not match file2"
+
+ echo "file2.chk contents" >> $seqres.full
+ od -tx1 -Ad -c $testdir/file2.chk >> $seqres.full
+ echo "file2 contents" >> $seqres.full
+ od -tx1 -Ad -c $testdir/file2 >> $seqres.full
+ echo "end bad contents" >> $seqres.full
+fi
+
+# file3 was cached when it was unshared, and it should match
+if ! cmp -s $testdir/file2.chk $testdir/file3; then
+ echo "file2.chk does not match file3"
+
+ echo "file2.chk contents" >> $seqres.full
+ od -tx1 -Ad -c $testdir/file2.chk >> $seqres.full
+ echo "file3 contents" >> $seqres.full
+ od -tx1 -Ad -c $testdir/file3 >> $seqres.full
+ echo "end bad contents" >> $seqres.full
+fi
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 734
+Create the original file and a clone
+Funshare at least one pagecache page
+Check contents
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023 HUAWEI. All Rights Reserved.
+#
+# FS QA Test No. 735
+#
+# Append writes to a file with logical block numbers close to 0xffffffff
+# and observe if a kernel crash is caused by ext4_lblk_t overflow triggering
+# BUG_ON at ext4_mb_new_inode_pa(). This is a regression test for
+# commit bc056e7163ac ("ext4: fix BUG in ext4_mb_new_inode_pa() due to overflow")
+# commit 2dcf5fde6dff ("ext4: prevent the normalized size from exceeding EXT_MAX_BLOCKS")
+
+. ./common/preamble
+. ./common/populate
+_begin_fstest auto quick insert prealloc
+
+# real QA test starts here
+if [[ "$FSTYP" =~ ext[0-9]+ ]]; then
+ _fixed_by_kernel_commit bc056e7163ac "ext4: fix BUG in ext4_mb_new_inode_pa() due to overflow"
+ _fixed_by_kernel_commit 2dcf5fde6dff "ext4: prevent the normalized size from exceeding EXT_MAX_BLOCKS"
+fi
+
+_require_odirect
+_require_xfs_io_command "falloc"
+_require_xfs_io_command "finsert"
+
+dev_size=$((80 * 1024 * 1024))
+_scratch_mkfs_sized $dev_size >>$seqres.full 2>&1 || _fail "mkfs failed"
+
+_scratch_mount
+_require_congruent_file_oplen $SCRATCH_MNT 1048576 # finsert at 1M
+file_blksz="$(_get_file_block_size ${SCRATCH_MNT})"
+
+# Reserve 1M space
+$XFS_IO_PROG -f -c "falloc 0 1M" "${SCRATCH_MNT}/tmp" >> $seqres.full
+
+# Create a file with logical block numbers close to 0xffffffff
+$XFS_IO_PROG -f -c "falloc 0 10M" "${SCRATCH_MNT}/file" >> $seqres.full
+max_pos=$(( 0xffffffff * file_blksz ))
+finsert_len=$(( max_pos - ((10 + 2) << 20) ))
+$XFS_IO_PROG -f -c "finsert 1M ${finsert_len}" "${SCRATCH_MNT}/file" >> $seqres.full
+
+# Filling up the free space ensures that the pre-allocated space is the reserved space.
+nr_free=$(stat -f -c '%f' ${SCRATCH_MNT})
+_fill_fs $((nr_free * file_blksz)) ${SCRATCH_MNT}/fill $file_blksz 0 >> $seqres.full 2>&1
+sync
+
+# Remove reserved space to gain free space for allocation
+rm -f ${SCRATCH_MNT}/tmp
+
+# Trying to allocate two blocks triggers BUG_ON.
+$XFS_IO_PROG -c "open -ad ${SCRATCH_MNT}/file" -c "pwrite -S 0xff 0 $((2 * file_blksz))" >> $seqres.full
+
+echo "Silence is golden"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 735
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2023 SUSE Linux Products GmbH. All Rights Reserved.
+#
+# FS QA Test No. 736
+#
+# Test that on a fairly large directory if we keep renaming files while holding
+# the directory open and doing readdir(3) calls, we don't end up in an infinite
+# loop.
+#
+. ./common/preamble
+_begin_fstest auto quick dir rename
+
+_cleanup()
+{
+ cd /
+ rm -fr $tmp.*
+ rm -fr $target_dir
+}
+
+_supported_fs generic
+_require_test
+_require_test_program readdir-while-renames
+
+[ $FSTYP = "btrfs" ] && _fixed_by_kernel_commit 9b378f6ad48c \
+ "btrfs: fix infinite directory reads"
+
+target_dir="$TEST_DIR/test-$seq"
+rm -fr $target_dir
+mkdir $target_dir
+
+$here/src/readdir-while-renames $target_dir
+
+# success, all done
+echo "Silence is golden"
+status=0
+exit
--- /dev/null
+QA output created by 736
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023 IBM Corporation. All Rights Reserved.
+#
+# FS QA Test No. 737
+#
+# Integrity test for O_SYNC with buff-io, dio, aio-dio with sudden shutdown.
+# Based on a testcase reported by Gao Xiang <hsiangkao@linux.alibaba.com>
+#
+
+. ./common/preamble
+_begin_fstest auto quick shutdown aio
+
+# real QA test starts here
+_supported_fs generic
+_require_scratch
+_require_scratch_shutdown
+_require_aiodio aio-dio-write-verify
+
+[[ "$FSTYP" =~ ext[0-9]+ ]] && _fixed_by_kernel_commit 91562895f803 \
+ "ext4: properly sync file size update after O_SYNC direct IO"
+
+_scratch_mkfs > $seqres.full 2>&1
+_scratch_mount
+
+echo "T-1: Create a 1M file using buff-io & O_SYNC"
+$XFS_IO_PROG -fs -c "pwrite -S 0x5a 0 1M" $SCRATCH_MNT/testfile.t1 > /dev/null 2>&1
+echo "T-1: Shutdown the fs suddenly"
+_scratch_shutdown
+echo "T-1: Cycle mount"
+_scratch_cycle_mount
+echo "T-1: File contents after cycle mount"
+_hexdump $SCRATCH_MNT/testfile.t1
+
+echo "T-2: Create a 1M file using O_DIRECT & O_SYNC"
+$XFS_IO_PROG -fsd -c "pwrite -S 0x5a 0 1M" $SCRATCH_MNT/testfile.t2 > /dev/null 2>&1
+echo "T-2: Shutdown the fs suddenly"
+_scratch_shutdown
+echo "T-2: Cycle mount"
+_scratch_cycle_mount
+echo "T-2: File contents after cycle mount"
+_hexdump $SCRATCH_MNT/testfile.t2
+
+echo "T-3: Create a 1M file using AIO-DIO & O_SYNC"
+$AIO_TEST -a size=1048576 -S -N $SCRATCH_MNT/testfile.t3 > /dev/null 2>&1
+echo "T-3: Shutdown the fs suddenly"
+_scratch_shutdown
+echo "T-3: Cycle mount"
+_scratch_cycle_mount
+echo "T-3: File contents after cycle mount"
+_hexdump $SCRATCH_MNT/testfile.t3
+
+status=0
+exit
--- /dev/null
+QA output created by 737
+T-1: Create a 1M file using buff-io & O_SYNC
+T-1: Shutdown the fs suddenly
+T-1: Cycle mount
+T-1: File contents after cycle mount
+000000 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a >ZZZZZZZZZZZZZZZZ<
+*
+100000
+T-2: Create a 1M file using O_DIRECT & O_SYNC
+T-2: Shutdown the fs suddenly
+T-2: Cycle mount
+T-2: File contents after cycle mount
+000000 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a >ZZZZZZZZZZZZZZZZ<
+*
+100000
+T-3: Create a 1M file using AIO-DIO & O_SYNC
+T-3: Shutdown the fs suddenly
+T-3: Cycle mount
+T-3: File contents after cycle mount
+000000 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a >ZZZZZZZZZZZZZZZZ<
+*
+100000
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# FS QA Test 738
+#
+# Test possible deadlock of umount and reclaim memory
+# when there are EOF blocks in files.
+#
+. ./common/preamble
+_begin_fstest auto quick freeze
+
+_cleanup()
+{
+ xfs_freeze -u $SCRATCH_MNT 2>/dev/null
+ wait
+ cd /
+ rm -r -f $tmp.*
+}
+
+_supported_fs generic
+_require_scratch
+_require_freeze
+
+_scratch_mkfs >> $seqres.full
+_scratch_mount
+
+create_eof_block_file()
+{
+ # Create several EOF blocks in the new file
+ for j in $(seq 1 5); do
+ cat $SCRATCH_MNT/testfile >> $1
+ done
+}
+
+$XFS_IO_PROG -fc "pwrite 0 64k" $SCRATCH_MNT/testfile >> $seqres.full
+# Create enough files to make sure there is enough cache
+for i in $(seq 0 1024); do
+ create_eof_block_file $SCRATCH_MNT/$i
+done
+sync
+xfs_freeze -f $SCRATCH_MNT
+
+# This will hang if bug reproduces
+echo 3 > /proc/sys/vm/drop_caches &
+
+# Wait a while before exiting and unfreezing.
+sleep 3
+
+# success, all done
+echo "Silence is golden"
+status=0
+exit
--- /dev/null
+QA output created by 738
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright 2023 Google LLC
+#
+# FS QA Test No. 739
+#
+# Verify the on-disk format of encrypted files that use a crypto data unit size
+# that differs from the filesystem block size. This tests the functionality
+# that was introduced in Linux 6.7 by kernel commit 5b1188847180
+# ("fscrypt: support crypto data unit size less than filesystem block size").
+#
+. ./common/preamble
+_begin_fstest auto quick encrypt
+
+. ./common/filter
+. ./common/encrypt
+
+_supported_fs generic
+_wants_kernel_commit 5b1188847180 \
+ "fscrypt: support crypto data unit size less than filesystem block size"
+
+# For now, just test 512-byte and 1024-byte data units. Filesystems accept
+# power-of-2 sizes between 512 and the filesystem block size, inclusively.
+# Testing 512 and 1024 ensures this test will run for any FS block size >= 1024
+# (provided that the filesystem supports sub-block data units at all).
+_verify_ciphertext_for_encryption_policy AES-256-XTS AES-256-CTS-CBC v2 log2_dusize=9
+_verify_ciphertext_for_encryption_policy AES-256-XTS AES-256-CTS-CBC v2 log2_dusize=10
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 739
+
+Verifying ciphertext with parameters:
+ contents_encryption_mode: AES-256-XTS
+ filenames_encryption_mode: AES-256-CTS-CBC
+ options: v2 log2_dusize=9
+
+Verifying ciphertext with parameters:
+ contents_encryption_mode: AES-256-XTS
+ filenames_encryption_mode: AES-256-CTS-CBC
+ options: v2 log2_dusize=10
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2024 Oracle. All Rights Reserved.
+#
+# FS QA Test 741
+#
+# Attempt to mount both the DM physical device and the DM flakey device.
+# Verify the returned error message.
+#
+. ./common/preamble
+_begin_fstest auto quick volume tempfsid
+
+# Override the default cleanup function.
+_cleanup()
+{
+ umount $extra_mnt &> /dev/null
+ rm -rf $extra_mnt
+ _unmount_flakey
+ _cleanup_flakey
+ cd /
+ rm -r -f $tmp.*
+}
+
+# Import common functions.
+. ./common/filter
+. ./common/dmflakey
+
+# real QA test starts here
+_supported_fs generic
+_require_test
+_require_scratch
+_require_dm_target flakey
+
+[ "$FSTYP" = "btrfs" ] && _fixed_by_kernel_commit XXXXXXXXXXXX \
+ "btrfs: return accurate error code on open failure"
+
+_scratch_mkfs >> $seqres.full
+_init_flakey
+_mount_flakey
+
+extra_mnt=$TEST_DIR/extra_mnt
+rm -rf $extra_mnt
+mkdir -p $extra_mnt
+
+# Mount must fail because the physical device has a dm created on it.
+# Filters alter the return code of the mount.
+_mount $SCRATCH_DEV $extra_mnt 2>&1 | \
+ _filter_testdir_and_scratch | _filter_error_mount
+
+# Try again with flakey unmounted, must fail.
+_unmount_flakey
+_mount $SCRATCH_DEV $extra_mnt 2>&1 | \
+ _filter_testdir_and_scratch | _filter_error_mount
+
+# Removing dm should make mount successful.
+_cleanup_flakey
+_scratch_mount
+
+status=0
+exit
--- /dev/null
+QA output created by 741
+mount: TEST_DIR/extra_mnt: SCRATCH_DEV already mounted or mount point busy
+mount: TEST_DIR/extra_mnt: SCRATCH_DEV already mounted or mount point busy
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2024 Meta Platforms, Inc. All Rights Reserved.
+#
+# FS QA Test No. 742
+#
+# Test fiemap into an mmaped buffer of the same file
+#
+# Create a reasonably large file, then run a program which mmaps it and uses
+# that as a buffer for an fiemap call. This is a regression test for btrfs
+# where we used to hold a lock for the duration of the fiemap call which would
+# result in a deadlock if we page faulted.
+#
+. ./common/preamble
+_begin_fstest quick auto fiemap
+[ $FSTYP == "btrfs" ] && \
+ _fixed_by_kernel_commit b0ad381fa769 \
+ "btrfs: fix deadlock with fiemap and extent locking"
+
+_cleanup()
+{
+ rm -f $dst
+ cd /
+ rm -r -f $tmp.*
+}
+
+# real QA test starts here
+_supported_fs generic
+_require_test
+_require_test_program "fiemap-fault"
+_require_test_program "punch-alternating"
+_require_xfs_io_command "fpunch"
+
+dst=$TEST_DIR/$seq/fiemap-fault
+
+mkdir -p $TEST_DIR/$seq
+
+echo "Silence is golden"
+
+# Generate a file with lots of extents
+blksz=$(_get_file_block_size $TEST_DIR)
+$XFS_IO_PROG -f -c "pwrite -q 0 $((blksz * 10000))" $dst
+$here/src/punch-alternating $dst
+
+# Now run the reproducer
+$here/src/fiemap-fault $dst
+
+# success, all done
+status=$?
+exit
--- /dev/null
+QA output created by 742
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2024 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 743
+#
+# This is a regression test for a kernel hang that I saw when creating a memory
+# mapping, injecting EIO errors on the block device, and invoking
+# MADV_POPULATE_READ on the mapping to fault in the pages.
+#
+. ./common/preamble
+_begin_fstest auto rw eio
+
+# Override the default cleanup function.
+_cleanup()
+{
+ cd /
+ rm -f $tmp.*
+ _dmerror_unmount
+ _dmerror_cleanup
+}
+
+# Import common functions.
+. ./common/dmerror
+
+_fixed_by_kernel_commit XXXXXXXXXXXX \
+ "mm/madvise: make MADV_POPULATE_(READ|WRITE) handle VM_FAULT_RETRY properly"
+
+# real QA test starts here
+
+# Modify as appropriate.
+_supported_fs generic
+_require_xfs_io_command madvise -R
+_require_scratch
+_require_dm_target error
+_require_command "$TIMEOUT_PROG" "timeout"
+
+_scratch_mkfs >> $seqres.full 2>&1
+_dmerror_init
+
+filesz=2m
+
+# Create a file that we'll read, then cycle mount to zap pagecache
+_dmerror_mount
+$XFS_IO_PROG -f -c "pwrite -S 0x58 0 $filesz" "$SCRATCH_MNT/a" >> $seqres.full
+_dmerror_unmount
+_dmerror_mount
+
+# Try to read the file data in a regular fashion just to prove that it works.
+echo read with no errors
+$TIMEOUT_PROG -s KILL 10s $XFS_IO_PROG -c "mmap -r 0 $filesz" -c "madvise -R 0 $filesz" "$SCRATCH_MNT/a"
+_dmerror_unmount
+_dmerror_mount
+
+# Load file metadata and induce EIO errors on read. Try to provoke the kernel;
+# kill the process after 10s so we can clean up.
+stat "$SCRATCH_MNT/a" >> $seqres.full
+echo read with IO errors
+_dmerror_load_error_table
+$TIMEOUT_PROG -s KILL 10s $XFS_IO_PROG -c "mmap -r 0 $filesz" -c "madvise -R 0 $filesz" "$SCRATCH_MNT/a"
+_dmerror_load_working_table
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 743
+read with no errors
+read with IO errors
+madvise: Bad address
include $(BUILDRULES)
-install:
+install: default
$(INSTALL) -m 755 -d $(TARGET_DIR)
$(INSTALL) -m 755 $(TESTS) $(TARGET_DIR)
$(INSTALL) -m 644 group.list $(TARGET_DIR)
include $(BUILDRULES)
-install:
+install: default
$(INSTALL) -m 755 -d $(TARGET_DIR)
$(INSTALL) -m 755 $(TESTS) $(TARGET_DIR)
$(INSTALL) -m 644 group.list $(TARGET_DIR)
include $(BUILDRULES)
-install:
+install: default
$(INSTALL) -m 755 -d $(TARGET_DIR)
$(INSTALL) -m 755 $(TESTS) $(TARGET_DIR)
$(INSTALL) -m 644 group.list $(TARGET_DIR)
# Test file copy up on overlayfs by changing mode bits.
#
. ./common/preamble
-_begin_fstest attr auto copyup quick
+_begin_fstest attr auto copyup quick perms
# Import common functions.
. ./common/filter
# d0e13f5 ovl: fix uid/gid when creating over whiteout
#
. ./common/preamble
-_begin_fstest auto quick whiteout
+_begin_fstest auto quick whiteout perms
# Import common functions.
. ./common/filter
# $upperdir overlaid on top of $lowerdir, so that "trusted.overlay.opaque"
# xattr should be honored and should not be listed
# mount readonly, because there's no upper and workdir
-$MOUNT_PROG -t overlay -o ro -o lowerdir=$upperdir:$lowerdir $OVL_BASE_SCRATCH_MNT $SCRATCH_MNT
+_overlay_scratch_mount_opts -o ro -o lowerdir=$upperdir:$lowerdir
# Dump trusted.overlay xattr, we should not see the "opaque" xattr
_getfattr -d -m overlay $SCRATCH_MNT/testdir
# SGID bit inheritance over whiteout.
#
. ./common/preamble
-_begin_fstest auto quick whiteout
+_begin_fstest auto quick whiteout perms
# Import common functions.
. ./common/filter
# real QA test starts here
-require_unshare() {
- unshare -f -r "$@" true &>/dev/null || \
- _notrun "unshare $@: not supported"
-}
-
# Modify as appropriate.
_supported_fs overlay
_fixed_by_kernel_commit 3fe6e52f0626 \
"ovl: override creds with the ones from the superblock mounter"
_require_scratch
-require_unshare -m -p -U
+_require_unshare
# Remove all files from previous tests
_scratch_mkfs
# Miklos Szeredi <mszeredi@redhat.com>
#
. ./common/preamble
-_begin_fstest auto quick attr
+_begin_fstest auto quick attr perms
# Import common functions.
. ./common/filter
# getfattr ok no attr ok ok
#
$SETFATTR_PROG -n "trusted.overlayfsrz" -v "n" \
- $SCRATCH_MNT/testf0 2>&1 | _filter_scratch
+ $SCRATCH_MNT/testf0 2>&1 | tee -a $seqres.full | _filter_scratch
_getfattr --absolute-names -n "trusted.overlayfsrz" \
- $SCRATCH_MNT/testf0 2>&1 | _filter_scratch
+ $SCRATCH_MNT/testf0 2>&1 | tee -a $seqres.full | _filter_scratch
-# {s,g}etfattr of "trusted.overlay.xxx" should fail.
+# {s,g}etfattr of "trusted.overlay.xxx" fail on older kernels
# The errno returned varies among kernel versions,
-# v4.3/7 v4.8-rc1 v4.8 v4.10
-# setfattr not perm not perm not perm not supp
-# getfattr no attr no attr not perm not supp
+# v4.3/7 v4.8-rc1 v4.8 v4.10 v6.7
+# setfattr not perm not perm not perm not supp ok
+# getfattr no attr no attr not perm not supp ok
#
-# Consider "Operation not {supported,permitted}" pass.
+# Consider "Operation not {supported,permitted}" pass for old kernels.
#
-$SETFATTR_PROG -n "trusted.overlay.fsz" -v "n" \
- $SCRATCH_MNT/testf1 2>&1 | _filter_scratch | \
- sed -e 's/permitted/supported/g'
+if _check_scratch_overlay_xattr_escapes $SCRATCH_MNT/testf0; then
+ setexp=""
+ getexp="No such attribute"
+else
+ setexp="Operation not supported"
+ getexp="Operation not supported"
+fi
-_getfattr --absolute-names -n "trusted.overlay.fsz" \
- $SCRATCH_MNT/testf1 2>&1 | _filter_scratch | \
- sed -e 's/permitted/supported/g'
+getres=$(_getfattr --absolute-names -n "trusted.overlay.fsz" \
+ $SCRATCH_MNT/testf1 2>&1 | tee -a $seqres.full | _filter_scratch | \
+ sed 's/permitted/supported/')
+
+[[ "$getres" =~ "$getexp" ]] || echo unexpected getattr result: $getres
+
+setres=$($SETFATTR_PROG -n "trusted.overlay.fsz" -v "n" \
+ $SCRATCH_MNT/testf1 2>&1 | tee -a $seqres.full |_filter_scratch | \
+ sed -e 's/permitted/supported/g')
+
+if [ "$setexp" ]; then
+ [[ "$setres" =~ "$expres" ]] || echo unexpected setattr result: $setres
+else
+ [[ "$setres" == "" ]] || echo unexpected setattr result: $setres
+fi
# success, all done
status=0
# file: SCRATCH_MNT/testf0
trusted.overlayfsrz="n"
-setfattr: SCRATCH_MNT/testf1: Operation not supported
-SCRATCH_MNT/testf1: trusted.overlay.fsz: Operation not supported
# Mount overlay with lower layers only.
# Verify that overlay is mounted read-only and that it cannot be remounted rw.
-$MOUNT_PROG -t overlay -o"lowerdir=$lowerdir2:$lowerdir1" \
- $OVL_BASE_SCRATCH_MNT $SCRATCH_MNT
+_overlay_scratch_mount_opts -o"lowerdir=$lowerdir2:$lowerdir1"
touch $SCRATCH_MNT/foo 2>&1 | _filter_scratch
$MOUNT_PROG -o remount,rw $SCRATCH_MNT 2>&1 | _filter_ro_mount
$UMOUNT_PROG $SCRATCH_MNT
# Check encode/decode/read of lower file handles on lower layers only r/o overlay.
# For non-upper overlay mount, nfs_export requires disabling redirect_dir.
-$MOUNT_PROG -t overlay $OVL_BASE_SCRATCH_MNT $SCRATCH_MNT \
+_overlay_scratch_mount_opts \
-o ro,redirect_dir=nofollow,nfs_export=on,lowerdir=$middle:$lower
test_file_handles $SCRATCH_MNT/lowertestdir -rp
test_file_handles $SCRATCH_MNT/lowertestdir/subdir -rp
# Overlay lookup cannot follow the redirect from $upper/lowertestdir.new to
# $lower/lowertestdir. Instead, we mount an overlay subtree rooted at these
# directories.
-$MOUNT_PROG -t overlay $OVL_BASE_SCRATCH_MNT $SCRATCH_MNT \
+_overlay_scratch_mount_opts \
-o ro,redirect_dir=nofollow,nfs_export=on,lowerdir=$upper/lowertestdir.new:$lower/lowertestdir
test_file_handles $SCRATCH_MNT -r
test_file_handles $SCRATCH_MNT/subdir -rp
# Check encode/decode/read of lower file handles on lower layers only r/o overlay.
# For non-upper overlay mount, nfs_export requires disabling redirect_dir.
-$MOUNT_PROG -t overlay $OVL_BASE_SCRATCH_MNT $SCRATCH_MNT \
+_overlay_scratch_mount_opts \
-o ro,redirect_dir=nofollow,nfs_export=on,lowerdir=$middle:$lower
test_file_handles $SCRATCH_MNT/lowertestdir -rp
test_file_handles $SCRATCH_MNT/lowertestdir/subdir -rp
# Overlay lookup cannot follow the redirect from $upper/lowertestdir.new to
# $lower/lowertestdir. Instead, we mount an overlay subtree rooted at these
# directories.
-$MOUNT_PROG -t overlay $OVL_BASE_SCRATCH_MNT $SCRATCH_MNT \
+_overlay_scratch_mount_opts \
-o ro,redirect_dir=nofollow,nfs_export=on,lowerdir=$upper/lowertestdir.new:$lower/lowertestdir
test_file_handles $SCRATCH_MNT -r
test_file_handles $SCRATCH_MNT/subdir -rp
# Test metadata only copy up functionality.
#
. ./common/preamble
-_begin_fstest auto quick metacopy
+_begin_fstest auto quick metacopy redirect prealloc
# Import common functions.
. ./common/filter
_overlay_scratch_mount_dirs "$_lowerdir" $upperdir $workdir -o redirect_dir=on,index=on,metacopy=on
}
+mount_ro_overlay()
+{
+ local _lowerdir=$1
+
+ _overlay_scratch_mount_dirs "$_lowerdir" "-" "-" -o ro,redirect_dir=follow,metacopy=on
+}
+
umount_overlay()
{
$UMOUNT_PROG $SCRATCH_MNT
check_file_size_contents $SCRATCH_MNT/$_target $_size "$_data"
check_file_blocks $SCRATCH_MNT/$_target $_blocks
- # Make sure copied up file is a metacopy file.
+ # Trigger metadata copy up and check existence of metacopy xattr.
+ chmod 400 $SCRATCH_MNT/$_target
umount_overlay
check_metacopy $upperdir/$_target "y"
check_file_size_contents $upperdir/$_target $_size ""
create_basic_files()
{
_scratch_mkfs
- mkdir -p $lowerdir $lowerdir2 $upperdir $workdir $workdir2
+ mkdir -p $lowerdir/subdir $lowerdir2 $upperdir $workdir $workdir2
mkdir -p $upperdir/$udirname
echo "$lowerdata" > $lowerdir/$lowername
chmod 600 $lowerdir/$lowername
prepare_midlayer()
{
+ local _redirect=$1
+
_scratch_mkfs
create_basic_files
+ [ -n "$_redirect" ] && mv "$lowerdir/$lowername" "$lowerdir/$_redirect"
# Create midlayer
_overlay_scratch_mount_dirs $lowerdir $lowerdir2 $workdir2 -o redirect_dir=on,index=on,metacopy=on
- # Trigger a metacopy
- chmod 400 $SCRATCH_MNT/$lowername
+ # Trigger a metacopy with or without redirect
+ if [ -n "$_redirect" ]; then
+ mv "$SCRATCH_MNT/$_redirect" "$SCRATCH_MNT/$lowername"
+ else
+ chmod 400 $SCRATCH_MNT/$lowername
+ fi
umount_overlay
}
mv $SCRATCH_MNT/$lowerlink $SCRATCH_MNT/$ufile
test_common $lowerdir $ufile $lowersize $lowerblocks "$lowerdata" "/$lowerlink"
+echo -e "\n== Check follow to lowerdata without redirect =="
+prepare_midlayer
+mount_ro_overlay "$lowerdir2:$lowerdir"
+test_common "$lowerdir2:$lowerdir" $lowername $lowersize $lowerblocks \
+ "$lowerdata"
+
+echo -e "\n== Check follow to lowerdata with relative redirect =="
+prepare_midlayer "$lowername.renamed"
+mount_ro_overlay "$lowerdir2:$lowerdir"
+test_common "$lowerdir2:$lowerdir" "$lowername" $lowersize $lowerblocks \
+ "$lowerdata"
+
+echo -e "\n== Check follow to lowerdata with absolute redirect =="
+prepare_midlayer "/subdir/$lowername"
+mount_ro_overlay "$lowerdir2:$lowerdir"
+test_common "$lowerdir2:$lowerdir" "$lowername" $lowersize $lowerblocks \
+ "$lowerdata"
+
# success, all done
status=0
exit
Unmount and Mount again
check properties of metadata copied up file
check properties of data copied up file
+
+== Check follow to lowerdata without redirect ==
+check properties of metadata copied up file
+Unmount and Mount again
+check properties of metadata copied up file
+check properties of data copied up file
+
+== Check follow to lowerdata with relative redirect ==
+check properties of metadata copied up file
+Unmount and Mount again
+check properties of metadata copied up file
+check properties of data copied up file
+
+== Check follow to lowerdata with absolute redirect ==
+check properties of metadata copied up file
+Unmount and Mount again
+check properties of metadata copied up file
+check properties of data copied up file
$MOUNT_PROG --bind $lowertestdir $lowertestdir
# For non-upper overlay mount, nfs_export requires disabling redirect_dir.
-$MOUNT_PROG -t overlay $OVL_BASE_SCRATCH_MNT $SCRATCH_MNT \
+_overlay_scratch_mount_opts \
-o ro,redirect_dir=nofollow,nfs_export=on,lowerdir=$lower:$lower2
# Decode an overlay directory file handle, whose underlying lower dir dentry
# Test overlayfs copy-up function for variant sparse files.
#
. ./common/preamble
-_begin_fstest auto quick copyup
+_begin_fstest auto quick copyup fiemap
# Import common functions..
. ./common/filter
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2018 Red Hat, Inc. All Rights Reserved.
+# Copyright (C) 2023 CTERA Networks. All Rights Reserved.
+#
+# FS QA Test No. 079
+#
+# Test data-only layers functionality.
+#
+. ./common/preamble
+_begin_fstest auto quick metacopy redirect prealloc
+
+# Import common functions.
+. ./common/filter
+. ./common/attr
+
+# real QA test starts here
+_supported_fs overlay
+# We use non-default scratch underlying overlay dirs, we need to check
+# them explicity after test.
+_require_scratch_nocheck
+_require_scratch_overlay_features redirect_dir metacopy
+_require_scratch_overlay_lowerdata_layers
+_require_xfs_io_command "falloc"
+
+# remove all files from previous tests
+_scratch_mkfs
+
+# File size on lower
+dataname="datafile"
+sharedname="shared"
+datacontent="data"
+dataname2="datafile2"
+datacontent2="data2"
+datasize="4096"
+
+# Number of blocks allocated by filesystem on lower. Will be queried later.
+datarblocks=""
+datarblocksize=""
+estimated_datablocks=""
+
+udirname="pureupper"
+ufile="upperfile"
+
+# Check metacopy xattr
+check_metacopy()
+{
+ local target=$1 exist=$2
+ local out_f target_f
+ local msg
+
+ out_f=$(_getfattr --absolute-names --only-values -n \
+ $OVL_XATTR_METACOPY $target 2>&1 | _filter_scratch)
+
+ if [ "$exist" == "y" ];then
+ [ "$out_f" == "" ] && return
+ echo "Metacopy xattr does not exist on ${target}. stdout=$out_f"
+ return
+ fi
+
+ if [ "$out_f" == "" ];then
+ echo "Metacopy xattr exists on ${target} unexpectedly."
+ return
+ fi
+
+ target_f=`echo $target | _filter_scratch`
+ msg="$target_f: trusted.overlay.metacopy: No such attribute"
+
+ [ "$out_f" == "$msg" ] && return
+
+ echo "Error while checking xattr on ${target}. stdout=$out"
+}
+
+# Check redirect xattr
+check_redirect()
+{
+ local target=$1
+ local expect=$2
+
+ value=$(_getfattr --absolute-names --only-values -n \
+ $OVL_XATTR_REDIRECT $target)
+
+ [[ "$value" == "$expect" ]] || echo "Redirect xattr incorrect. Expected=\"$expect\", actual=\"$value\""
+}
+
+# Check size
+check_file_size()
+{
+ local target=$1 expected_size=$2 actual_size
+
+ actual_size=$(_get_filesize $target)
+
+ [ "$actual_size" == "$expected_size" ] || echo "Expected file size $expected_size but actual size is $actual_size"
+}
+
+check_file_blocks()
+{
+ local target=$1 expected_blocks=$2 nr_blocks
+
+ nr_blocks=$(stat -c "%b" $target)
+
+ [ "$nr_blocks" == "$expected_blocks" ] || echo "Expected $expected_blocks blocks but actual number of blocks is ${nr_blocks}."
+}
+
+check_file_contents()
+{
+ local target=$1 expected=$2
+ local actual target_f
+
+ target_f=`echo $target | _filter_scratch`
+
+ read actual<$target
+
+ [ "$actual" == "$expected" ] || echo "Expected file $target_f contents to be \"$expected\" but actual contents are \"$actual\""
+}
+
+check_no_file_contents()
+{
+ local target=$1
+ local actual target_f out_f
+
+ target_f=`echo $target | _filter_scratch`
+ out_f=`cat $target 2>&1 | _filter_scratch`
+ msg="cat: $target_f: No such file or directory"
+
+ [ "$out_f" == "$msg" ] && return
+
+ echo "$target_f unexpectedly has content"
+}
+
+
+check_file_size_contents()
+{
+ local target=$1 expected_size=$2 expected_content=$3
+
+ check_file_size $target $expected_size
+ check_file_contents $target $expected_content
+}
+
+mount_overlay()
+{
+ local _lowerdir=$1 _datadir2=$2 _datadir=$3
+
+ _overlay_scratch_mount_opts \
+ -o"lowerdir=$_lowerdir::$_datadir2::$_datadir" \
+ -o"upperdir=$upperdir,workdir=$workdir" \
+ -o redirect_dir=on,metacopy=on
+}
+
+mount_ro_overlay()
+{
+ local _lowerdir=$1 _datadir2=$2 _datadir=$3
+
+ _overlay_scratch_mount_opts \
+ -o"lowerdir=$_lowerdir::$_datadir2::$_datadir" \
+ -o redirect_dir=follow,metacopy=on
+}
+
+umount_overlay()
+{
+ $UMOUNT_PROG $SCRATCH_MNT
+}
+
+test_no_access()
+{
+ local _target=$1
+
+ mount_ro_overlay "$lowerdir" "$datadir2" "$datadir"
+
+ stat $SCRATCH_MNT/$_target >> $seqres.full 2>&1 || \
+ echo "No access to lowerdata layer $_target"
+
+ echo "Unmount and Mount rw"
+ umount_overlay
+ mount_overlay "$lowerdir" "$datadir2" "$datadir"
+ stat $SCRATCH_MNT/$_target >> $seqres.full 2>&1 || \
+ echo "No access to lowerdata layer $_target"
+ umount_overlay
+}
+
+test_common()
+{
+ local _lowerdir=$1 _datadir2=$2 _datadir=$3
+ local _target=$4 _size=$5 _blocks=$6 _data="$7"
+ local _redirect=$8
+
+ echo "Mount ro"
+ mount_ro_overlay $_lowerdir $_datadir2 $_datadir
+
+ # Check redirect xattr to lowerdata
+ [ -n "$_redirect" ] && check_redirect $lowerdir/$_target "$_redirect"
+
+ echo "check properties of metadata copied up file $_target"
+ check_file_size_contents $SCRATCH_MNT/$_target $_size "$_data"
+ check_file_blocks $SCRATCH_MNT/$_target $_blocks
+
+ # Do a mount cycle and check size and contents again.
+ echo "Unmount and Mount rw"
+ umount_overlay
+ mount_overlay $_lowerdir $_datadir2 $_datadir
+ echo "check properties of metadata copied up file $_target"
+ check_file_size_contents $SCRATCH_MNT/$_target $_size "$_data"
+ check_file_blocks $SCRATCH_MNT/$_target $_blocks
+
+ # Trigger metadata copy up and check existence of metacopy xattr.
+ chmod 400 $SCRATCH_MNT/$_target
+ umount_overlay
+ check_metacopy $upperdir/$_target "y"
+ check_file_size_contents $upperdir/$_target $_size ""
+
+ # Trigger data copy up and check absence of metacopy xattr.
+ mount_overlay $_lowerdir $_datadir2 $_datadir
+ $XFS_IO_PROG -c "falloc 0 1" $SCRATCH_MNT/$_target >> $seqres.full
+ echo "check properties of data copied up file $_target"
+ check_file_size_contents $SCRATCH_MNT/$_target $_size "$_data"
+ umount_overlay
+ check_metacopy $upperdir/$_target "n"
+ check_file_size_contents $upperdir/$_target $_size "$_data"
+}
+
+test_lazy()
+{
+ local _target=$1
+
+ mount_overlay "$lowerdir" "$datadir2" "$datadir"
+
+ # Metadata should be valid
+ check_file_size $SCRATCH_MNT/$_target $datasize
+ check_file_blocks $SCRATCH_MNT/$_target $estimated_datablocks
+
+ # But have no content
+ check_no_file_contents $SCRATCH_MNT/$_target
+
+ umount_overlay
+}
+
+create_basic_files()
+{
+ _scratch_mkfs
+ mkdir -p $datadir/subdir $datadir2/subdir $lowerdir $lowerdir2 $upperdir $workdir $workdir2
+ mkdir -p $upperdir/$udirname
+ echo "$datacontent" > $datadir/$dataname
+ chmod 600 $datadir/$dataname
+ echo "$datacontent2" > $datadir2/$dataname2
+ chmod 600 $datadir2/$dataname2
+
+ echo "$datacontent" > $datadir/$sharedname
+ echo "$datacontent2" > $datadir2/$sharedname
+ chmod 600 $datadir/$sharedname $datadir2/$sharedname
+
+ # Create files of size datasize.
+ for f in $datadir/$dataname $datadir2/$dataname2 $datadir/$sharedname $datadir2/$sharedname; do
+ $XFS_IO_PROG -c "falloc 0 $datasize" $f
+ $XFS_IO_PROG -c "fsync" $f
+ done
+
+ # Query number of block
+ datablocks=$(stat -c "%b" $datadir/$dataname)
+
+ # For lazy lookup file the block count is estimated based on size and block size
+ datablocksize=$(stat -c "%B" $datadir/$dataname)
+ estimated_datablocks=$(( ($datasize + $datablocksize - 1)/$datablocksize ))
+}
+
+prepare_midlayer()
+{
+ local _redirect=$1
+
+ _scratch_mkfs
+ create_basic_files
+ if [ -n "$_redirect" ]; then
+ mv "$datadir/$dataname" "$datadir/$_redirect"
+ mv "$datadir2/$dataname2" "$datadir2/$_redirect.2"
+ mv "$datadir/$sharedname" "$datadir/$_redirect.shared"
+ mv "$datadir2/$sharedname" "$datadir2/$_redirect.shared"
+ fi
+ # Create midlayer
+ _overlay_scratch_mount_dirs $datadir2:$datadir $lowerdir $workdir2 -o redirect_dir=on,index=on,metacopy=on
+ # Trigger a metacopy with or without redirect
+ if [ -n "$_redirect" ]; then
+ mv "$SCRATCH_MNT/$_redirect" "$SCRATCH_MNT/$dataname"
+ mv "$SCRATCH_MNT/$_redirect.2" "$SCRATCH_MNT/$dataname2"
+ mv "$SCRATCH_MNT/$_redirect.shared" "$SCRATCH_MNT/$sharedname"
+ else
+ chmod 400 $SCRATCH_MNT/$dataname
+ chmod 400 $SCRATCH_MNT/$dataname2
+ chmod 400 $SCRATCH_MNT/$sharedname
+ fi
+ umount_overlay
+}
+
+# Create test directories
+datadir=$OVL_BASE_SCRATCH_MNT/data
+datadir2=$OVL_BASE_SCRATCH_MNT/data2
+lowerdir=$OVL_BASE_SCRATCH_MNT/lower
+upperdir=$OVL_BASE_SCRATCH_MNT/upper
+workdir=$OVL_BASE_SCRATCH_MNT/workdir
+workdir2=$OVL_BASE_SCRATCH_MNT/workdir2
+
+echo -e "\n== Check no follow to lowerdata layer without redirect =="
+prepare_midlayer
+test_no_access "$dataname"
+test_no_access "$dataname2"
+test_no_access "$sharedname"
+
+echo -e "\n== Check no follow to lowerdata layer with relative redirect =="
+prepare_midlayer "$dataname.renamed"
+test_no_access "$dataname"
+test_no_access "$dataname2"
+test_no_access "$sharedname"
+
+echo -e "\n== Check follow to lowerdata layer with absolute redirect =="
+prepare_midlayer "/subdir/$dataname"
+test_common "$lowerdir" "$datadir2" "$datadir" "$dataname" $datasize $datablocks \
+ "$datacontent" "/subdir/$dataname"
+test_common "$lowerdir" "$datadir2" "$datadir" "$dataname2" $datasize $datablocks \
+ "$datacontent2" "/subdir/$dataname.2"
+# Shared file should be picked from upper datadir
+test_common "$lowerdir" "$datadir2" "$datadir" "$sharedname" $datasize $datablocks \
+ "$datacontent2" "/subdir/$dataname.shared"
+
+echo -e "\n== Check lazy follow to lowerdata layer =="
+
+prepare_midlayer "/subdir/$dataname"
+rm $datadir/subdir/$dataname
+test_lazy $dataname
+
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 079
+
+== Check no follow to lowerdata layer without redirect ==
+No access to lowerdata layer datafile
+Unmount and Mount rw
+No access to lowerdata layer datafile
+No access to lowerdata layer datafile2
+Unmount and Mount rw
+No access to lowerdata layer datafile2
+No access to lowerdata layer shared
+Unmount and Mount rw
+No access to lowerdata layer shared
+
+== Check no follow to lowerdata layer with relative redirect ==
+No access to lowerdata layer datafile
+Unmount and Mount rw
+No access to lowerdata layer datafile
+No access to lowerdata layer datafile2
+Unmount and Mount rw
+No access to lowerdata layer datafile2
+No access to lowerdata layer shared
+Unmount and Mount rw
+No access to lowerdata layer shared
+
+== Check follow to lowerdata layer with absolute redirect ==
+Mount ro
+check properties of metadata copied up file datafile
+Unmount and Mount rw
+check properties of metadata copied up file datafile
+check properties of data copied up file datafile
+Mount ro
+check properties of metadata copied up file datafile2
+Unmount and Mount rw
+check properties of metadata copied up file datafile2
+check properties of data copied up file datafile2
+Mount ro
+check properties of metadata copied up file shared
+Unmount and Mount rw
+check properties of metadata copied up file shared
+check properties of data copied up file shared
+
+== Check lazy follow to lowerdata layer ==
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2023 Red Hat, Inc. All Rights Reserved.
+# Copyright (C) 2023 CTERA Networks. All Rights Reserved.
+#
+# FS QA Test No. 080
+#
+# Test fs-verity functionallity
+#
+. ./common/preamble
+_begin_fstest auto quick metacopy redirect verity
+
+# Import common functions.
+. ./common/filter
+. ./common/attr
+. ./common/verity
+
+# real QA test starts here
+_supported_fs overlay
+# We use non-default scratch underlying overlay dirs, we need to check
+# them explicity after test.
+_require_scratch_nocheck
+_require_scratch_overlay_features redirect_dir metacopy
+_require_scratch_overlay_lowerdata_layers
+_require_scratch_overlay_verity
+
+# remove all files from previous tests
+_scratch_mkfs
+
+verityname="verityfile"
+noverityname="noverityfile"
+wrongverityname="wrongverityfile"
+missingverityname="missingverityfile"
+lowerdata="data1"
+lowerdata2="data2"
+lowerdata3="data3"
+lowerdata4="data4"
+lowersize="5"
+
+# Create test directories
+lowerdir=$OVL_BASE_SCRATCH_MNT/lower
+lowerdir2=$OVL_BASE_SCRATCH_MNT/lower2
+upperdir=$OVL_BASE_SCRATCH_MNT/upper
+workdir=$OVL_BASE_SCRATCH_MNT/workdir
+workdir2=$OVL_BASE_SCRATCH_MNT/workdir2
+
+# Check metacopy xattr
+check_metacopy()
+{
+ local target=$1 exist=$2 dataonlybase=$3
+ local out_f target_f
+ local msg
+
+ out_f=$( { _getfattr --absolute-names --only-values -n \
+ $OVL_XATTR_METACOPY $target 2>&3 | od -A n -t x1 -w256 ; } 3>&1 | _filter_scratch)
+ has_version0=`echo $out_f | awk 'NR==1{print $1 == 0}'`
+
+ if [ "$exist" == "y" ];then
+ [ "$out_f" == "" -o "$has_version0" == "1" ] && return
+ echo "Metacopy xattr does not exist on ${target}. stdout=$out_f"
+ return
+ fi
+
+ if [ "$out_f" == "" -o "$has_version0" == "1" ];then
+ echo "Metacopy xattr exists on ${target} unexpectedly."
+ return
+ fi
+
+ target_f=`echo $target | _filter_scratch`
+ msg="$target_f: trusted.overlay.metacopy: No such attribute"
+
+ [ "$out_f" == "$msg" ] && return
+
+ echo "Error while checking xattr on ${target}. stdout=$out"
+}
+
+# Check verity set in metacopy
+check_verity()
+{
+ local target=$1 exist=$2
+ local out_f target_f
+ local msg
+
+ out_f=$( { _getfattr --absolute-names --only-values -n $OVL_XATTR_METACOPY $target 2>&3 | od -A n -t x1 -w256 ; } 3>&1 | _filter_scratch)
+
+ target_f=`echo $target | _filter_scratch`
+ msg="$target_f: trusted.overlay.metacopy: No such attribute"
+ has_digest=`echo $out_f | awk 'NR==1{print $4 == 1}'`
+
+ if [ "$exist" == "y" ]; then
+ [ "$out_f" == "$msg" -o "$has_digest" == "0" ] && echo "No verity on ${target}. stdout=$out_f"
+ return
+ fi
+
+ [ "$out_f" == "$msg" -o "$has_digest" == "0" ] && return
+ echo "Verity xattr exists on ${target} unexpectedly. stdout=$out_f"
+}
+
+# Check redirect xattr
+check_redirect()
+{
+ local target=$1
+ local expect=$2
+
+ value=$(_getfattr --absolute-names --only-values -n \
+ $OVL_XATTR_REDIRECT $target)
+
+ [[ "$value" == "$expect" ]] || echo "Redirect xattr incorrect. Expected=\"$expect\", actual=\"$value\""
+}
+
+# Check size
+check_file_size()
+{
+ local target=$1 expected_size=$2 actual_size
+
+ actual_size=$(_get_filesize $target)
+
+ [ "$actual_size" == "$expected_size" ] || echo "Expected file size of $target $expected_size but actual size is $actual_size"
+}
+
+check_file_contents()
+{
+ local target=$1 expected=$2
+ local actual target_f
+
+ target_f=`echo $target | _filter_scratch`
+
+ read actual<$target
+
+ [ "$actual" == "$expected" ] || echo "Expected file $target_f contents to be \"$expected\" but actual contents are \"$actual\""
+}
+
+check_file_size_contents()
+{
+ local target=$1 expected_size=$2 expected_content=$3
+
+ check_file_size $target $expected_size
+ check_file_contents $target $expected_content
+}
+
+check_io_error()
+{
+ local target=$1
+ local actual target_f out_f
+
+ target_f=`echo $target | _filter_scratch`
+ out_f=`cat $target 2>&1 | _filter_scratch`
+ msg="cat: $target_f: Input/output error"
+
+ [ "$out_f" == "$msg" ] && return
+
+ echo "$target_f unexpectedly has no I/O error"
+}
+
+create_basic_files()
+{
+ local subdir=$1
+
+ _scratch_mkfs
+ mkdir -p $lowerdir $lowerdir2 $upperdir $workdir $workdir2
+
+ if [ "$subdir" != "" ]; then
+ mkdir $lowerdir/$subdir
+ fi
+
+ echo -n "$lowerdata" > $lowerdir/$subdir$verityname
+ echo -n "$lowerdata2" > $lowerdir/$subdir$noverityname
+ echo -n "$lowerdata3" > $lowerdir/$subdir$wrongverityname
+ echo -n "$lowerdata4" > $lowerdir/$subdir$missingverityname
+
+ for f in $verityname $noverityname $wrongverityname $missingverityname; do
+ chmod 600 $lowerdir/$subdir$f
+
+ if [ "$f" != "$noverityname" ]; then
+ _fsv_enable $lowerdir/$subdir$f
+ fi
+ done
+}
+
+prepare_midlayer()
+{
+ local dataonlybase=$1
+
+ subdir=""
+ if [ "$dataonlybase" == "y" ]; then
+ subdir="base/"
+ fi
+
+ create_basic_files "$subdir"
+ # Create midlayer
+ _overlay_scratch_mount_dirs $lowerdir $lowerdir2 $workdir2 -o redirect_dir=on,index=on,verity=on,metacopy=on
+ for f in $verityname $noverityname $wrongverityname $missingverityname; do
+ if [ "$dataonlybase" == "y" ]; then
+ mv $SCRATCH_MNT/base/$f $SCRATCH_MNT/$f
+ else
+ chmod 400 $SCRATCH_MNT/$f
+ fi
+ done
+ umount_overlay
+
+ if [ "$dataonlybase" == "y" ]; then
+ rm -rf $lowerdir2/base
+ fi
+
+ for f in $verityname $noverityname $wrongverityname $missingverityname; do
+ # Ensure we have right metacopy and verity xattrs
+ check_metacopy $lowerdir2/$f "y"
+
+ if [ "$f" == "$noverityname" ]; then
+ check_verity $lowerdir2/$f "n"
+ else
+ check_verity $lowerdir2/$f "y"
+ fi
+
+ if [ "$dataonlybase" == "y" ]; then
+ check_redirect $lowerdir2/$f "/base/$f"
+ fi
+
+ check_file_size_contents $lowerdir2/$f $lowersize ""
+ done
+
+ # Fixup missing and wrong verity in lowerdir
+ rm -f $lowerdir/$subdir$wrongverityname $lowerdir/$subdir$missingverityname
+ echo -n "changed" > $lowerdir/$subdir$wrongverityname
+ _fsv_enable $lowerdir/$subdir$wrongverityname
+ echo "$lowerdata4" > $lowerdir/$subdir$missingverityname
+}
+
+test_common()
+{
+ local dataonlybase=$1
+ local verity=$2
+
+ if [ $dataonlybase == "y" ]; then
+ mount_overlay "$lowerdir2::$lowerdir" $verity
+ else
+ mount_overlay "$lowerdir2:$lowerdir" $verity
+ fi
+
+ check_file_size_contents $SCRATCH_MNT/$verityname $lowersize "$lowerdata"
+
+ if [ "$verity" == "require" ]; then
+ check_io_error $SCRATCH_MNT/$noverityname
+ else
+ check_file_size_contents $SCRATCH_MNT/$noverityname $lowersize "$lowerdata2"
+ fi
+
+ if [ "$verity" == "off" ]; then
+ check_file_size_contents $SCRATCH_MNT/$wrongverityname $lowersize "changed"
+ check_file_size_contents $SCRATCH_MNT/$missingverityname $lowersize "$lowerdata4"
+ else
+ check_io_error $SCRATCH_MNT/$missingverityname
+ check_io_error $SCRATCH_MNT/$wrongverityname
+ fi
+
+ umount_overlay
+}
+
+mount_overlay()
+{
+ local _lowerdir=$1
+ local _verity=$2
+
+ _overlay_scratch_mount_dirs "$_lowerdir" $upperdir $workdir -o redirect_dir=on,index=on,metacopy=on,verity=$_verity
+}
+
+umount_overlay()
+{
+ $UMOUNT_PROG $SCRATCH_MNT
+}
+
+
+echo -e "\n== Check fsverity validation =="
+
+prepare_midlayer "n"
+test_common "n" "off"
+prepare_midlayer "n"
+test_common "n" "on"
+
+# Now with data-only layers
+prepare_midlayer "y"
+test_common "y" "off"
+prepare_midlayer "y"
+test_common "y" "on"
+
+echo -e "\n== Check fsverity require =="
+
+prepare_midlayer "n"
+test_common "n" "require"
+
+# Now with data-only layers
+prepare_midlayer "y"
+test_common "y" "require"
+
+echo -e "\n== Check fsverity copy-up =="
+
+# Ensure Second level metacopy sets verity xattr
+prepare_midlayer "n"
+mount_overlay "$lowerdir2:$lowerdir" "on"
+chmod 200 $SCRATCH_MNT/$verityname
+umount_overlay
+check_metacopy $upperdir/$verityname "y"
+check_verity $upperdir/$verityname "y"
+
+# Ensure data copy up remove verity xattr
+create_basic_files ""
+mount_overlay "$lowerdir" "on"
+echo foo >> $SCRATCH_MNT/$verityname
+umount_overlay
+check_metacopy $upperdir/$verityname "n"
+check_verity $upperdir/$verityname "n"
+
+# Ensure metacopy is only used if verity is enabled in lower for verity=require
+create_basic_files ""
+mount_overlay "$lowerdir" "require"
+chmod 200 $SCRATCH_MNT/$verityname
+chmod 200 $SCRATCH_MNT/$noverityname
+umount_overlay
+check_metacopy $upperdir/$verityname "y"
+check_verity $upperdir/$verityname "y"
+check_metacopy $upperdir/$noverityname "n"
+check_verity $upperdir/$noverityname "n"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 080
+
+== Check fsverity validation ==
+
+== Check fsverity require ==
+
+== Check fsverity copy-up ==
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2023 CTERA Networks. All Rights Reserved.
+#
+# FSQA Test No. 081
+#
+# Test persistent (and optionally unique) overlayfs fsid
+# with mount options uuid=null/auto/on introduced in kernel v6.6
+#
+. ./common/preamble
+_begin_fstest auto quick
+
+# Import common functions.
+. ./common/filter
+. ./common/attr
+
+# real QA test starts here
+_supported_fs overlay
+
+_scratch_mkfs >>$seqres.full 2>&1
+
+# Create overlay layer with pre-packaged merge dir
+upperdir=$OVL_BASE_SCRATCH_MNT/$OVL_UPPER
+workdir=$OVL_BASE_SCRATCH_MNT/$OVL_WORK
+lowerdir=$OVL_BASE_SCRATCH_MNT/$OVL_LOWER
+mkdir -p $upperdir/test_dir
+mkdir -p $lowerdir/test_dir
+test_dir=$SCRATCH_MNT/test_dir/
+
+# Record base fs fsid
+upper_fsid=$(stat -f -c '%i' $upperdir)
+lower_fsid=$(stat -f -c '%i' $lowerdir)
+
+# Sanity tests
+[[ -n "$upper_fsid" ]] || \
+ echo "invalid upper fs fsid"
+[[ "$lower_fsid" == "$upper_fsid" ]] || \
+ echo "lower fs and upper fs fsid differ"
+
+# Test legacy behavior - ovl fsid inherited from upper fs
+_overlay_scratch_mount_dirs $lowerdir $upperdir $workdir -o uuid=null 2>/dev/null || \
+ _notrun "Overlayfs does not support unique fsid feature"
+
+# Lookup of test_dir marks upper root as "impure", so following (uuid=auto) mounts
+# will NOT behave as first time mount of a new overlayfs
+ovl_fsid=$(stat -f -c '%i' $test_dir)
+[[ "$ovl_fsid" == "$upper_fsid" ]] || \
+ echo "Overlayfs (uuid=null) and upper fs fsid differ"
+
+# Keep base fs mounted in case it has a volatile fsid (e.g. tmpfs)
+$UMOUNT_PROG $SCRATCH_MNT
+
+# Test legacy behavior is preserved by default for existing "impure" overlayfs
+_scratch_mount
+
+ovl_fsid=$(stat -f -c '%i' $test_dir)
+[[ "$ovl_fsid" == "$upper_fsid" ]] || \
+ echo "Overlayfs (after uuid=null) and upper fs fsid differ"
+
+$UMOUNT_PROG $SCRATCH_MNT
+
+# Test unique fsid on explicit opt-in for existing "impure" overlayfs
+_scratch_mount -o uuid=on
+
+ovl_fsid=$(stat -f -c '%i' $test_dir)
+ovl_unique_fsid=$ovl_fsid
+[[ "$ovl_fsid" != "$upper_fsid" ]] || \
+ echo "Overlayfs (uuid=on) and upper fs fsid are the same"
+
+$UMOUNT_PROG $SCRATCH_MNT
+
+# Test unique fsid is persistent by default after it was created
+_scratch_mount
+
+ovl_fsid=$(stat -f -c '%i' $test_dir)
+[[ "$ovl_fsid" == "$ovl_unique_fsid" ]] || \
+ echo "Overlayfs (after uuid=on) unique fsid is not persistent"
+
+$UMOUNT_PROG $SCRATCH_MNT
+
+# Test ignore existing persistent fsid on explicit opt-out
+_scratch_mount -o uuid=null
+
+ovl_fsid=$(stat -f -c '%i' $test_dir)
+[[ "$ovl_fsid" == "$upper_fsid" ]] || \
+ echo "Overlayfs (uuid=null) and upper fs fsid differ"
+
+$UMOUNT_PROG $SCRATCH_MNT
+
+# Test fallback to uuid=null with non-upper ovelray
+_overlay_scratch_mount_dirs "$upperdir:$lowerdir" "-" "-" -o ro,uuid=on
+
+ovl_fsid=$(stat -f -c '%i' $test_dir)
+[[ "$ovl_fsid" == "$lower_fsid" ]] || \
+ echo "Overlayfs (no upper) and lower fs fsid differ"
+
+# Re-create fresh overlay layers, so following (uuid=auto) mounts
+# will behave as first time mount of a new overlayfs
+_scratch_unmount
+_scratch_mkfs >>$seqres.full 2>&1
+mkdir -p $upperdir/test_dir
+mkdir -p $lowerdir/test_dir
+
+# Record new base fs fsid
+upper_fsid=$(stat -f -c '%i' $upperdir)
+
+# Test unique fsid by default for first time mount of new overlayfs
+_scratch_mount
+
+ovl_fsid=$(stat -f -c '%i' $test_dir)
+ovl_unique_fsid=$ovl_fsid
+[[ "$ovl_fsid" != "$upper_fsid" ]] || \
+ echo "Overlayfs (new) and upper fs fsid are the same"
+
+$UMOUNT_PROG $SCRATCH_MNT
+
+# Test unique fsid is persistent by default after it was created
+_scratch_mount -o uuid=on
+
+ovl_fsid=$(stat -f -c '%i' $test_dir)
+[[ "$ovl_fsid" == "$ovl_unique_fsid" ]] || \
+ echo "Overlayfs (uuid=on) unique fsid is not persistent"
+
+$UMOUNT_PROG $SCRATCH_MNT
+
+echo "Silence is golden"
+status=0
+exit
--- /dev/null
+QA output created by 081
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2023 CTERA Networks. All Rights Reserved.
+#
+# FS QA Test 082
+#
+# Test for a regression in copy up of symlink that has the noatime inode
+# attribute.
+#
+# kernel commit 72db82115d2b ("ovl: copy up sync/noatime fileattr flags")
+# from v5.15 introduced the regression.
+#
+. ./common/preamble
+_begin_fstest auto quick copyup symlink atime
+
+# real QA test starts here
+_supported_fs overlay
+_fixed_by_kernel_commit ab048302026d \
+ "ovl: fix failed copyup of fileattr on a symlink"
+
+_require_scratch
+_require_chattr A
+
+# remove all files from previous runs
+_scratch_mkfs
+
+# prepare lower test dir with NOATIME flag
+lowerdir=$OVL_BASE_SCRATCH_MNT/$OVL_LOWER
+mkdir -p $lowerdir/testdir
+$CHATTR_PROG +A $lowerdir/testdir >> $seqres.full 2>&1 ||
+ echo "failed to set No_Atime flag on $lowerdir/testdir"
+
+# The NOATIME is inherited to children symlink in ext4/fs2fs
+# (and on tmpfs on recent kernels).
+# The overlayfs test will not fail unless base fs is
+# one of those filesystems.
+#
+# The problem with this inheritence is that the NOATIME flag is inherited
+# to a symlink and the flag does take effect, but there is no way to query
+# the flag (lsattr) or change it (chattr) on a symlink, so overlayfs will
+# fail when trying to copy up NOATIME flag from lower to upper symlink.
+#
+touch $lowerdir/testdir/foo
+ln -sf foo $lowerdir/testdir/lnk
+$LSATTR_PROG -l $lowerdir/testdir/foo >> $seqres.full
+
+before=$(stat -c %x $lowerdir/testdir/lnk)
+echo "symlink atime before readlink: $before" >> $seqres.full 2>&1
+sleep 2s
+cat $lowerdir/testdir/lnk
+after=$(stat -c %x $lowerdir/testdir/lnk)
+echo "symlink atime after readlink: $after" >> $seqres.full 2>&1
+
+[ "$before" == "$after" ] || \
+ _notrun "base fs $OVL_BASE_FSTYP does not inherit No_Atime flag on symlink"
+
+# mounting overlay
+_scratch_mount
+
+# moving symlink will try to copy up lower symlink flags
+# and fails with error ENXIO, if the bug is reproduced
+mv $SCRATCH_MNT/testdir/lnk $SCRATCH_MNT/
+
+# success, all done
+echo "Silence is golden"
+status=0
+exit
--- /dev/null
+QA output created by 082
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2023 CTERA Networks. All Rights Reserved.
+#
+# FS QA Test 083
+#
+# Test regressions in parsing and display of special chars in mount options.
+#
+# The following kernel commits from v6.5 introduced regressions:
+# b36a5780cb44 ("ovl: modify layer parameter parsing")
+# 1784fbc2ed9c ("ovl: port to new mount api")
+#
+
+. ./common/preamble
+_begin_fstest auto quick mount
+
+# Import common functions.
+. ./common/filter
+
+# real QA test starts here
+_supported_fs overlay
+_fixed_by_kernel_commit 32db51070850 \
+ "ovl: fix regression in showing lowerdir mount option"
+_fixed_by_kernel_commit c34706acf40b \
+ "ovl: fix regression in parsing of mount options with escaped comma"
+
+# _overlay_check_* helpers do not handle special chars well
+_require_scratch_nocheck
+
+# Remove all files from previous tests
+_scratch_mkfs
+
+# Create lowerdirs with special characters
+lowerdir_spaces="$OVL_BASE_SCRATCH_MNT/lower1 with spaces"
+lowerdir_colons="$OVL_BASE_SCRATCH_MNT/lower2:with::colons"
+lowerdir_commas="$OVL_BASE_SCRATCH_MNT/lower3,with,,commas"
+lowerdir_colons_esc="$OVL_BASE_SCRATCH_MNT/lower2\:with\:\:colons"
+lowerdir_commas_esc="$OVL_BASE_SCRATCH_MNT/lower3\,with\,\,commas"
+upperdir=$OVL_BASE_SCRATCH_MNT/$OVL_UPPER
+workdir=$OVL_BASE_SCRATCH_MNT/$OVL_WORK
+mkdir -p "$lowerdir_spaces" "$lowerdir_colons" "$lowerdir_commas"
+
+# _overlay_mount_* helpers do not handle special chars well, so execute mount directly.
+# if escaped colons are not parsed correctly, mount will fail.
+$MOUNT_PROG -t overlay ovl_esc_test $SCRATCH_MNT \
+ -o"upperdir=$upperdir,workdir=$workdir" \
+ -o"lowerdir=$lowerdir_colons_esc:$lowerdir_spaces" \
+ 2>&1 | tee -a $seqres.full
+
+# if spaces are not escaped when showing mount options,
+# mount command will not show the word 'spaces' after the spaces
+$MOUNT_PROG -t overlay | grep ovl_esc_test | tee -a $seqres.full | grep -v spaces && \
+ echo "ERROR: escaped spaces truncated from lowerdir mount option"
+
+# Re-create the upper/work dirs to mount them with a different lower
+# This is required in case index feature is enabled
+$UMOUNT_PROG $SCRATCH_MNT
+rm -rf "$upperdir" "$workdir"
+mkdir -p "$upperdir" "$workdir"
+
+# Kernel commit c34706acf40b fixes parsing of mount options with escaped comma
+# when the mount options string is provided via data argument to mount(2) syscall.
+# With libmount >= 2.39, libmount itself will try to split the comma separated
+# options list provided to mount(8) commnad line and call fsconfig(2) for each
+# mount option seperately. Since libmount does not obay to overlayfs escaped
+# commas format, it will call fsconfig(2) with the wrong path (i.e. ".../lower3")
+# and this test will fail, but the failure would indicate a libmount issue, not
+# a kernel issue. Therefore, force libmount to use mount(2) syscall, so we only
+# test the kernel fix.
+LIBMOUNT_FORCE_MOUNT2=always $MOUNT_PROG -t overlay $OVL_BASE_SCRATCH_DEV $SCRATCH_MNT \
+ -o"upperdir=$upperdir,workdir=$workdir,lowerdir=$lowerdir_commas_esc" 2>> $seqres.full || \
+ echo "ERROR: incorrect parsing of escaped comma in lowerdir mount option"
+
+echo "Silence is golden"
+status=0
+exit
--- /dev/null
+QA output created by 083
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2023 Red Hat, Inc. All Rights Reserved.
+# Copyright (C) 2023 CTERA Networks. All Rights Reserved.
+#
+# FS QA Test No. 084
+#
+# Test advanded nesting functionallity
+#
+. ./common/preamble
+_begin_fstest auto quick nested
+
+# Override the default cleanup function.
+_cleanup()
+{
+ cd /
+ # Unmount nested mounts if things fail
+ $UMOUNT_PROG $OVL_BASE_SCRATCH_MNT/nested 2>/dev/null
+ rm -rf $tmp
+}
+
+# Import common functions.
+. ./common/filter
+. ./common/attr
+
+# real QA test starts here
+_supported_fs overlay
+# This test does not run on kernels prior ro v6.7 and now it will also make sure
+# that the following on-disk format change was backported to v6.7 based kernels
+_fixed_by_kernel_commit 420332b94119 \
+ "ovl: mark xwhiteouts directory with overlay.opaque='x'"
+
+# We use non-default scratch underlying overlay dirs, we need to check
+# them explicity after test.
+_require_scratch_nocheck
+_require_scratch_overlay_xattr_escapes
+
+# remove all files from previous tests
+_scratch_mkfs
+
+lowerdir=$OVL_BASE_SCRATCH_MNT/lower
+middir=$OVL_BASE_SCRATCH_MNT/mid
+upperdir=$OVL_BASE_SCRATCH_MNT/upper
+workdir=$OVL_BASE_SCRATCH_MNT/workdir
+nesteddir=$OVL_BASE_SCRATCH_MNT/nested
+
+umount_overlay()
+{
+ $UMOUNT_PROG $SCRATCH_MNT
+}
+
+test_escape()
+{
+ local prefix=$1
+
+ echo -e "\n== Check xattr escape $prefix =="
+
+ # index feature would require nfs_export on $nesteddir mount
+ local extra_options="-o index=off"
+ if [ "$prefix" == "user" ]; then
+ extra_options+=",userxattr"
+ fi
+
+ _scratch_mkfs
+ mkdir -p $lowerdir $middir $upperdir $workdir $nesteddir
+
+ _overlay_scratch_mount_dirs $lowerdir $middir $workdir $extra_options
+
+ mkdir -p $SCRATCH_MNT/layer1/dir/ $SCRATCH_MNT/layer2/dir
+
+ touch $SCRATCH_MNT/layer1/dir/file
+
+ # Make layer2/dir an opaque file
+ # Only one of these will be escaped, but both should succeed
+ setfattr -n user.overlay.opaque -v "y" $SCRATCH_MNT/layer2/dir
+ setfattr -n trusted.overlay.opaque -v "y" $SCRATCH_MNT/layer2/dir
+
+ getfattr -m "overlay\\." --absolute-names -d $SCRATCH_MNT/layer2/dir | _filter_scratch
+
+ umount_overlay
+
+ getfattr -m "overlay\\." --absolute-names -d $middir/layer2/dir | _filter_scratch
+
+ # Remount as lower and try again
+ _overlay_scratch_mount_dirs $middir:$lowerdir $upperdir $workdir $extra_options
+
+ getfattr -m "overlay\\." --absolute-names -d $SCRATCH_MNT/layer2/dir | _filter_scratch
+
+ # Recursively mount and ensure the opaque dir is working with both trusted and user xattrs
+ echo "nested xattr mount with trusted.overlay"
+ _overlay_mount_dirs $SCRATCH_MNT/layer2:$SCRATCH_MNT/layer1 - - overlayfs $nesteddir
+ stat $nesteddir/dir/file 2>&1 | _filter_scratch
+ $UMOUNT_PROG $nesteddir
+
+ echo "nested xattr mount with user.overlay"
+ _overlay_mount_dirs $SCRATCH_MNT/layer2:$SCRATCH_MNT/layer1 - - -o userxattr overlayfs $nesteddir
+ stat $nesteddir/dir/file 2>&1 | _filter_scratch
+ $UMOUNT_PROG $nesteddir
+
+ # Also ensure propagate the escaped xattr when we copy-up layer2/dir
+ echo "copy-up of escaped xattrs"
+ touch $SCRATCH_MNT/layer2/dir/other_file
+ getfattr -m "$prefix.overlay\\.overlay" --absolute-names -d $upperdir/layer2/dir | _filter_scratch
+
+ umount_overlay
+}
+
+test_escape trusted
+test_escape user
+
+do_test_xwhiteout()
+{
+ local prefix=$1
+ local basedir=$2
+
+ local extra_options=""
+ if [ "$prefix" == "user" ]; then
+ extra_options="-o userxattr"
+ fi
+
+ mkdir -p $basedir/lower $basedir/upper $basedir/work
+ touch $basedir/lower/regular $basedir/lower/hidden $basedir/upper/hidden
+ # overlay.opaque="x" means directory has xwhiteout children
+ setfattr -n $prefix.overlay.opaque -v "x" $basedir/upper
+ setfattr -n $prefix.overlay.whiteout -v "y" $basedir/upper/hidden
+
+ # Test the hidden is invisible
+ _overlay_scratch_mount_dirs $basedir/upper:$basedir/lower - - $extra_options
+ ls $SCRATCH_MNT
+ stat $SCRATCH_MNT/hidden 2>&1 | _filter_scratch
+ umount_overlay
+}
+
+# Validate that xwhiteouts work like whiteouts
+test_xwhiteout()
+{
+ local prefix=$1
+
+ echo -e "\n== Check xwhiteout $prefix =="
+
+ _scratch_mkfs
+
+ do_test_xwhiteout $prefix $OVL_BASE_SCRATCH_MNT
+}
+
+test_xwhiteout trusted
+test_xwhiteout user
+
+# Validate that (escaped) xwhiteouts work inside a nested overlayfs mount
+test_escaped_xwhiteout()
+{
+ local prefix=$1
+
+ echo -e "\n== Check escaped xwhiteout $prefix =="
+
+ # index feature would require nfs_export on $nesteddir mount
+ local extra_options="-o index=off"
+ if [ "$prefix" == "user" ]; then
+ extra_options+=",userxattr"
+ fi
+
+ _scratch_mkfs
+ mkdir -p $lowerdir $upperdir $workdir $nesteddir
+
+ _overlay_mount_dirs $lowerdir $upperdir $workdir $extra_options overlayfs $nesteddir
+
+ do_test_xwhiteout $prefix $nesteddir
+
+ $UMOUNT_PROG $nesteddir
+}
+
+test_escaped_xwhiteout trusted
+test_escaped_xwhiteout user
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 084
+
+== Check xattr escape trusted ==
+# file: SCRATCH_MNT/layer2/dir
+trusted.overlay.opaque="y"
+user.overlay.opaque="y"
+
+# file: SCRATCH_DEV/mid/layer2/dir
+trusted.overlay.overlay.opaque="y"
+user.overlay.opaque="y"
+
+# file: SCRATCH_MNT/layer2/dir
+trusted.overlay.opaque="y"
+user.overlay.opaque="y"
+
+nested xattr mount with trusted.overlay
+stat: cannot statx 'SCRATCH_DEV/nested/dir/file': No such file or directory
+nested xattr mount with user.overlay
+stat: cannot statx 'SCRATCH_DEV/nested/dir/file': No such file or directory
+copy-up of escaped xattrs
+# file: SCRATCH_DEV/upper/layer2/dir
+trusted.overlay.overlay.opaque="y"
+
+
+== Check xattr escape user ==
+# file: SCRATCH_MNT/layer2/dir
+trusted.overlay.opaque="y"
+user.overlay.opaque="y"
+
+# file: SCRATCH_DEV/mid/layer2/dir
+trusted.overlay.opaque="y"
+user.overlay.overlay.opaque="y"
+
+# file: SCRATCH_MNT/layer2/dir
+trusted.overlay.opaque="y"
+user.overlay.opaque="y"
+
+nested xattr mount with trusted.overlay
+stat: cannot statx 'SCRATCH_DEV/nested/dir/file': No such file or directory
+nested xattr mount with user.overlay
+stat: cannot statx 'SCRATCH_DEV/nested/dir/file': No such file or directory
+copy-up of escaped xattrs
+# file: SCRATCH_DEV/upper/layer2/dir
+user.overlay.overlay.opaque="y"
+
+
+== Check xwhiteout trusted ==
+regular
+stat: cannot statx 'SCRATCH_MNT/hidden': No such file or directory
+
+== Check xwhiteout user ==
+regular
+stat: cannot statx 'SCRATCH_MNT/hidden': No such file or directory
+
+== Check escaped xwhiteout trusted ==
+regular
+stat: cannot statx 'SCRATCH_MNT/hidden': No such file or directory
+
+== Check escaped xwhiteout user ==
+regular
+stat: cannot statx 'SCRATCH_MNT/hidden': No such file or directory
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2018 Red Hat, Inc. All Rights Reserved.
+# Copyright (C) 2023 CTERA Networks. All Rights Reserved.
+#
+# FS QA Test No. 085
+#
+# Test data-only layers functionality.
+# This is a variant of test overlay/079 with lowerdir+,datadir+ mount options
+#
+. ./common/preamble
+_begin_fstest auto quick metacopy redirect prealloc
+
+# Import common functions.
+. ./common/filter
+. ./common/attr
+
+# real QA test starts here
+_supported_fs overlay
+# We use non-default scratch underlying overlay dirs, we need to check
+# them explicity after test.
+_require_scratch_nocheck
+_require_scratch_overlay_features redirect_dir metacopy
+_require_scratch_overlay_lowerdir_add_layers
+_require_xfs_io_command "falloc"
+
+# remove all files from previous tests
+_scratch_mkfs
+
+# File size on lower
+dataname="datafile"
+sharedname="shared"
+datacontent="data"
+dataname2="datafile2"
+datacontent2="data2"
+datasize="4096"
+
+# Number of blocks allocated by filesystem on lower. Will be queried later.
+datarblocks=""
+datarblocksize=""
+estimated_datablocks=""
+
+udirname="pureupper"
+ufile="upperfile"
+
+# Check metacopy xattr
+check_metacopy()
+{
+ local target=$1 exist=$2
+ local out_f target_f
+ local msg
+
+ out_f=$(_getfattr --absolute-names --only-values -n \
+ $OVL_XATTR_METACOPY $target 2>&1 | _filter_scratch)
+
+ if [ "$exist" == "y" ];then
+ [ "$out_f" == "" ] && return
+ echo "Metacopy xattr does not exist on ${target}. stdout=$out_f"
+ return
+ fi
+
+ if [ "$out_f" == "" ];then
+ echo "Metacopy xattr exists on ${target} unexpectedly."
+ return
+ fi
+
+ target_f=`echo $target | _filter_scratch`
+ msg="$target_f: trusted.overlay.metacopy: No such attribute"
+
+ [ "$out_f" == "$msg" ] && return
+
+ echo "Error while checking xattr on ${target}. stdout=$out"
+}
+
+# Check redirect xattr
+check_redirect()
+{
+ local target=$1
+ local expect=$2
+
+ value=$(_getfattr --absolute-names --only-values -n \
+ $OVL_XATTR_REDIRECT $target)
+
+ [[ "$value" == "$expect" ]] || echo "Redirect xattr incorrect. Expected=\"$expect\", actual=\"$value\""
+}
+
+# Check size
+check_file_size()
+{
+ local target=$1 expected_size=$2 actual_size
+
+ actual_size=$(_get_filesize $target)
+
+ [ "$actual_size" == "$expected_size" ] || echo "Expected file size $expected_size but actual size is $actual_size"
+}
+
+check_file_blocks()
+{
+ local target=$1 expected_blocks=$2 nr_blocks
+
+ nr_blocks=$(stat -c "%b" $target)
+
+ [ "$nr_blocks" == "$expected_blocks" ] || echo "Expected $expected_blocks blocks but actual number of blocks is ${nr_blocks}."
+}
+
+check_file_contents()
+{
+ local target=$1 expected=$2
+ local actual target_f
+
+ target_f=`echo $target | _filter_scratch`
+
+ read actual<$target
+
+ [ "$actual" == "$expected" ] || echo "Expected file $target_f contents to be \"$expected\" but actual contents are \"$actual\""
+}
+
+check_no_file_contents()
+{
+ local target=$1
+ local actual target_f out_f
+
+ target_f=`echo $target | _filter_scratch`
+ out_f=`cat $target 2>&1 | _filter_scratch`
+ msg="cat: $target_f: No such file or directory"
+
+ [ "$out_f" == "$msg" ] && return
+
+ echo "$target_f unexpectedly has content"
+}
+
+
+check_file_size_contents()
+{
+ local target=$1 expected_size=$2 expected_content=$3
+
+ check_file_size $target $expected_size
+ check_file_contents $target $expected_content
+}
+
+mount_overlay()
+{
+ local _lowerdir=$1 _datadir2=$2 _datadir=$3
+
+ _overlay_scratch_mount_opts \
+ -o"lowerdir+=$_lowerdir,datadir+=$_datadir2,datadir+=$_datadir" \
+ -o"upperdir=$upperdir,workdir=$workdir" \
+ -o redirect_dir=on,metacopy=on
+}
+
+mount_ro_overlay()
+{
+ local _lowerdir=$1 _datadir2=$2 _datadir=$3
+
+ _overlay_scratch_mount_opts \
+ -o"lowerdir+=$_lowerdir,datadir+=$_datadir2,datadir+=$_datadir" \
+ -o redirect_dir=follow,metacopy=on
+}
+
+umount_overlay()
+{
+ $UMOUNT_PROG $SCRATCH_MNT
+}
+
+test_no_access()
+{
+ local _target=$1
+
+ mount_ro_overlay "$lowerdir" "$datadir2" "$datadir"
+
+ stat $SCRATCH_MNT/$_target >> $seqres.full 2>&1 || \
+ echo "No access to lowerdata layer $_target"
+
+ echo "Unmount and Mount rw"
+ umount_overlay
+ mount_overlay "$lowerdir" "$datadir2" "$datadir"
+ stat $SCRATCH_MNT/$_target >> $seqres.full 2>&1 || \
+ echo "No access to lowerdata layer $_target"
+ umount_overlay
+}
+
+test_common()
+{
+ local _lowerdir=$1 _datadir2=$2 _datadir=$3
+ local _target=$4 _size=$5 _blocks=$6 _data="$7"
+ local _redirect=$8
+
+ echo "Mount ro"
+ mount_ro_overlay $_lowerdir $_datadir2 $_datadir
+
+ # Check redirect xattr to lowerdata
+ [ -n "$_redirect" ] && check_redirect $lowerdir/$_target "$_redirect"
+
+ echo "check properties of metadata copied up file $_target"
+ check_file_size_contents $SCRATCH_MNT/$_target $_size "$_data"
+ check_file_blocks $SCRATCH_MNT/$_target $_blocks
+
+ # Do a mount cycle and check size and contents again.
+ echo "Unmount and Mount rw"
+ umount_overlay
+ mount_overlay $_lowerdir $_datadir2 $_datadir
+ echo "check properties of metadata copied up file $_target"
+ check_file_size_contents $SCRATCH_MNT/$_target $_size "$_data"
+ check_file_blocks $SCRATCH_MNT/$_target $_blocks
+
+ # Trigger metadata copy up and check existence of metacopy xattr.
+ chmod 400 $SCRATCH_MNT/$_target
+ umount_overlay
+ check_metacopy $upperdir/$_target "y"
+ check_file_size_contents $upperdir/$_target $_size ""
+
+ # Trigger data copy up and check absence of metacopy xattr.
+ mount_overlay $_lowerdir $_datadir2 $_datadir
+ $XFS_IO_PROG -c "falloc 0 1" $SCRATCH_MNT/$_target >> $seqres.full
+ echo "check properties of data copied up file $_target"
+ check_file_size_contents $SCRATCH_MNT/$_target $_size "$_data"
+ umount_overlay
+ check_metacopy $upperdir/$_target "n"
+ check_file_size_contents $upperdir/$_target $_size "$_data"
+}
+
+test_lazy()
+{
+ local _target=$1
+
+ mount_overlay "$lowerdir" "$datadir2" "$datadir"
+
+ # Metadata should be valid
+ check_file_size $SCRATCH_MNT/$_target $datasize
+ check_file_blocks $SCRATCH_MNT/$_target $estimated_datablocks
+
+ # But have no content
+ check_no_file_contents $SCRATCH_MNT/$_target
+
+ umount_overlay
+}
+
+create_basic_files()
+{
+ _scratch_mkfs
+ mkdir -p $datadir/subdir $datadir2/subdir $lowerdir $lowerdir2 $upperdir $workdir $workdir2
+ mkdir -p $upperdir/$udirname
+ echo "$datacontent" > $datadir/$dataname
+ chmod 600 $datadir/$dataname
+ echo "$datacontent2" > $datadir2/$dataname2
+ chmod 600 $datadir2/$dataname2
+
+ echo "$datacontent" > $datadir/$sharedname
+ echo "$datacontent2" > $datadir2/$sharedname
+ chmod 600 $datadir/$sharedname $datadir2/$sharedname
+
+ # Create files of size datasize.
+ for f in $datadir/$dataname $datadir2/$dataname2 $datadir/$sharedname $datadir2/$sharedname; do
+ $XFS_IO_PROG -c "falloc 0 $datasize" $f
+ $XFS_IO_PROG -c "fsync" $f
+ done
+
+ # Query number of block
+ datablocks=$(stat -c "%b" $datadir/$dataname)
+
+ # For lazy lookup file the block count is estimated based on size and block size
+ datablocksize=$(stat -c "%B" $datadir/$dataname)
+ estimated_datablocks=$(( ($datasize + $datablocksize - 1)/$datablocksize ))
+}
+
+prepare_midlayer()
+{
+ local _redirect=$1
+
+ _scratch_mkfs
+ create_basic_files
+ if [ -n "$_redirect" ]; then
+ mv "$datadir/$dataname" "$datadir/$_redirect"
+ mv "$datadir2/$dataname2" "$datadir2/$_redirect.2"
+ mv "$datadir/$sharedname" "$datadir/$_redirect.shared"
+ mv "$datadir2/$sharedname" "$datadir2/$_redirect.shared"
+ fi
+ # Create midlayer
+ _overlay_scratch_mount_dirs $datadir2:$datadir $lowerdir $workdir2 -o redirect_dir=on,index=on,metacopy=on
+ # Trigger a metacopy with or without redirect
+ if [ -n "$_redirect" ]; then
+ mv "$SCRATCH_MNT/$_redirect" "$SCRATCH_MNT/$dataname"
+ mv "$SCRATCH_MNT/$_redirect.2" "$SCRATCH_MNT/$dataname2"
+ mv "$SCRATCH_MNT/$_redirect.shared" "$SCRATCH_MNT/$sharedname"
+ else
+ chmod 400 $SCRATCH_MNT/$dataname
+ chmod 400 $SCRATCH_MNT/$dataname2
+ chmod 400 $SCRATCH_MNT/$sharedname
+ fi
+ umount_overlay
+}
+
+# Create test directories
+datadir=$OVL_BASE_SCRATCH_MNT/data
+datadir2=$OVL_BASE_SCRATCH_MNT/data2
+lowerdir=$OVL_BASE_SCRATCH_MNT/lower
+upperdir=$OVL_BASE_SCRATCH_MNT/upper
+workdir=$OVL_BASE_SCRATCH_MNT/workdir
+workdir2=$OVL_BASE_SCRATCH_MNT/workdir2
+
+echo -e "\n== Check no follow to lowerdata layer without redirect =="
+prepare_midlayer
+test_no_access "$dataname"
+test_no_access "$dataname2"
+test_no_access "$sharedname"
+
+echo -e "\n== Check no follow to lowerdata layer with relative redirect =="
+prepare_midlayer "$dataname.renamed"
+test_no_access "$dataname"
+test_no_access "$dataname2"
+test_no_access "$sharedname"
+
+echo -e "\n== Check follow to lowerdata layer with absolute redirect =="
+prepare_midlayer "/subdir/$dataname"
+test_common "$lowerdir" "$datadir2" "$datadir" "$dataname" $datasize $datablocks \
+ "$datacontent" "/subdir/$dataname"
+test_common "$lowerdir" "$datadir2" "$datadir" "$dataname2" $datasize $datablocks \
+ "$datacontent2" "/subdir/$dataname.2"
+# Shared file should be picked from upper datadir
+test_common "$lowerdir" "$datadir2" "$datadir" "$sharedname" $datasize $datablocks \
+ "$datacontent2" "/subdir/$dataname.shared"
+
+echo -e "\n== Check lazy follow to lowerdata layer =="
+
+prepare_midlayer "/subdir/$dataname"
+rm $datadir/subdir/$dataname
+test_lazy $dataname
+
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 085
+
+== Check no follow to lowerdata layer without redirect ==
+No access to lowerdata layer datafile
+Unmount and Mount rw
+No access to lowerdata layer datafile
+No access to lowerdata layer datafile2
+Unmount and Mount rw
+No access to lowerdata layer datafile2
+No access to lowerdata layer shared
+Unmount and Mount rw
+No access to lowerdata layer shared
+
+== Check no follow to lowerdata layer with relative redirect ==
+No access to lowerdata layer datafile
+Unmount and Mount rw
+No access to lowerdata layer datafile
+No access to lowerdata layer datafile2
+Unmount and Mount rw
+No access to lowerdata layer datafile2
+No access to lowerdata layer shared
+Unmount and Mount rw
+No access to lowerdata layer shared
+
+== Check follow to lowerdata layer with absolute redirect ==
+Mount ro
+check properties of metadata copied up file datafile
+Unmount and Mount rw
+check properties of metadata copied up file datafile
+check properties of data copied up file datafile
+Mount ro
+check properties of metadata copied up file datafile2
+Unmount and Mount rw
+check properties of metadata copied up file datafile2
+check properties of data copied up file datafile2
+Mount ro
+check properties of metadata copied up file shared
+Unmount and Mount rw
+check properties of metadata copied up file shared
+check properties of data copied up file shared
+
+== Check lazy follow to lowerdata layer ==
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2023 CTERA Networks. All Rights Reserved.
+#
+# FS QA Test 086
+#
+# Test lowerdir+,datadir+ mount option restrictions.
+#
+
+. ./common/preamble
+_begin_fstest auto quick mount
+
+# Import common functions.
+. ./common/filter
+
+# real QA test starts here
+_supported_fs overlay
+
+# _overlay_check_* helpers do not handle special chars well
+_require_scratch_nocheck
+_require_scratch_overlay_lowerdir_add_layers
+
+# Remove all files from previous tests
+_scratch_mkfs
+
+# Create lowerdirs with special characters
+lowerdir_spaces="$OVL_BASE_SCRATCH_MNT/lower1 with spaces"
+lowerdir_colons="$OVL_BASE_SCRATCH_MNT/lower2:with::colons"
+lowerdir_colons_esc="$OVL_BASE_SCRATCH_MNT/lower2\:with\:\:colons"
+lowerdir=$OVL_BASE_SCRATCH_MNT/$OVL_LOWER
+upperdir=$OVL_BASE_SCRATCH_MNT/$OVL_UPPER
+workdir=$OVL_BASE_SCRATCH_MNT/$OVL_WORK
+mkdir -p "$lowerdir_spaces" "$lowerdir_colons"
+
+# _overlay_mount_* helpers do not handle lowerdir+,datadir+, so execute mount directly.
+
+# check illegal combinations and order of lowerdir,lowerdir+,datadir+
+$MOUNT_PROG -t overlay none $SCRATCH_MNT \
+ -o"lowerdir=$lowerdir,lowerdir+=$lowerdir_colons" \
+ 2>> $seqres.full && \
+ echo "ERROR: invalid combination of lowerdir and lowerdir+ mount options"
+
+$UMOUNT_PROG $SCRATCH_MNT 2>/dev/null
+
+$MOUNT_PROG -t overlay none $SCRATCH_MNT \
+ -o"lowerdir=$lowerdir,datadir+=$lowerdir_colons" \
+ -o redirect_dir=follow,metacopy=on 2>> $seqres.full && \
+ echo "ERROR: invalid combination of lowerdir and datadir+ mount options"
+
+$UMOUNT_PROG $SCRATCH_MNT 2>/dev/null
+
+$MOUNT_PROG -t overlay none $SCRATCH_MNT \
+ -o"datadir+=$lowerdir,lowerdir+=$lowerdir_colons" \
+ -o redirect_dir=follow,metacopy=on 2>> $seqres.full && \
+ echo "ERROR: invalid order of lowerdir+ and datadir+ mount options"
+
+$UMOUNT_PROG $SCRATCH_MNT 2>/dev/null
+
+# mount is expected to fail with escaped colons.
+$MOUNT_PROG -t overlay none $SCRATCH_MNT \
+ -o"lowerdir+=$lowerdir_colons_esc" \
+ 2>> $seqres.full && \
+ echo "ERROR: incorrect parsing of escaped colons in lowerdir+ mount option"
+
+$UMOUNT_PROG $SCRATCH_MNT 2>/dev/null
+
+# mount is expected to succeed without escaped colons.
+$MOUNT_PROG -t overlay ovl_esc_test $SCRATCH_MNT \
+ -o"lowerdir+=$lowerdir_colons,datadir+=$lowerdir_spaces" \
+ -o redirect_dir=follow,metacopy=on \
+ 2>&1 | tee -a $seqres.full
+
+# if spaces are not escaped when showing mount options,
+# mount command will not show the word 'spaces' after the spaces
+$MOUNT_PROG -t overlay | grep ovl_esc_test | tee -a $seqres.full | \
+ grep -q 'datadir+'.*spaces || \
+ echo "ERROR: escaped spaces truncated from datadir+ mount option"
+
+echo "Silence is golden"
+status=0
+exit
--- /dev/null
+QA output created by 086
+Silence is golden
include $(BUILDRULES)
-install:
+install: default
$(INSTALL) -m 755 -d $(TARGET_DIR)
$(INSTALL) -m 755 $(TESTS) $(TARGET_DIR)
$(INSTALL) -m 644 group.list $(TARGET_DIR)
include $(BUILDRULES)
-install:
+install: default
$(INSTALL) -m 755 -d $(TARGET_DIR)
$(INSTALL) -m 755 $(TESTS) $(TARGET_DIR)
$(INSTALL) -m 644 group.list $(TARGET_DIR)
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# FS QA Test No. 001
+#
+# A test that should always pass
+#
+. ./common/preamble
+_begin_fstest selftest
+
+echo "Silence is golden"
+status=0
+exit
--- /dev/null
+QA output created by 001
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# FS QA Test No. 002
+#
+# A test that should always fail due to output mismatch
+#
+. ./common/preamble
+_begin_fstest selftest
+
+echo "I am intentionally broken"
+status=0
+exit
--- /dev/null
+QA output created by 002
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# FS QA Test No. 003
+#
+# A test that _fail's
+#
+
+. ./common/preamble
+_begin_fstest selftest
+
+_fail "I have _fail'ed"
+
+status=0
+exit
--- /dev/null
+QA output created by 003
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# FS QA Test No. 004
+#
+# A test that should always be skipped
+#
+. ./common/preamble
+_begin_fstest selftest
+
+_notrun "Always skip me"
+
+echo "I should be skipped"
+status=0
+exit
--- /dev/null
+QA output created by 004
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# FS QA Test No. 005
+#
+# A test that crashes
+#
+. ./common/preamble
+_begin_fstest dangerous_selftest
+
+echo 1 > /proc/sys/kernel/sysrq
+echo c > /proc/sysrq-trigger
+
+echo "I should have crashed by now"
+status=0
+exit
--- /dev/null
+QA output created by 005
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# FS QA Test No. 006
+#
+# A test that hangs
+#
+
+. ./common/preamble
+_begin_fstest dangerous_selftest
+
+while :
+do
+ sleep 1d
+done
+
+echo "I should still be sleeping"
+status=0
+exit
--- /dev/null
+QA output created by 006
+Silence is golden
--- /dev/null
+#
+# Copyright (c) 2023 Google, Inc. All Rights Reserved.
+#
+
+TOPDIR = ../..
+include $(TOPDIR)/include/builddefs
+include $(TOPDIR)/include/buildgrouplist
+
+SELFTEST_DIR = selftest
+TARGET_DIR = $(PKG_LIB_DIR)/$(TESTS_DIR)/$(SELFTEST_DIR)
+DIRT = group.list
+
+default: $(DIRT)
+
+include $(BUILDRULES)
+
+install: default
+ $(INSTALL) -m 755 -d $(TARGET_DIR)
+ $(INSTALL) -m 755 $(TESTS) $(TARGET_DIR)
+ $(INSTALL) -m 644 group.list $(TARGET_DIR)
+ $(INSTALL) -m 644 $(OUTFILES) $(TARGET_DIR)
+
+# Nothing.
+install-dev install-lib:
# Test that filesystem sends discard requests only on free blocks
#
. ./common/preamble
-_begin_fstest auto trim
+_begin_fstest auto trim fiemap
_supported_fs ext4 xfs btrfs
_require_test
fssize=3000
else
_require_fs_space $TEST_DIR 307200
- fssize=300
+ fssize=$(_small_fs_size_mb 300) # 200m phys/virt size
fi
[ "$FSTYP" = "ext4" ] && _require_dumpe2fs
# to established convention which requires the filesystem to be
# unmounted while we probe the underlying file.
$UMOUNT_PROG $loop_mnt
- $XFS_IO_PROG -F -c fiemap $1 | grep hole | $SED_PROG 's/.*\[\(.*\)\.\.\(.*\)\].*/\1 \2/'
+
+ # FIEMAP only works on regular files, so call it on the backing file
+ # and not the loop device like everything else
+ $XFS_IO_PROG -F -c fiemap $img_file | grep hole | \
+ $SED_PROG 's/.*\[\(.*\)\.\.\(.*\)\].*/\1 \2/'
_mount $loop_dev $loop_mnt
}
case $FSTYP in
ext4)
$UMOUNT_PROG $loop_mnt
- $DUMPE2FS_PROG $img_file 2>&1 | grep " Free blocks" | cut -d ":" -f2- | \
+ $DUMPE2FS_PROG $loop_dev 2>&1 | grep " Free blocks" | cut -d ":" -f2- | \
tr ',' '\n' | $SED_PROG 's/^ //' | \
$AWK_PROG -v spb=$sectors_per_block 'BEGIN{FS="-"};
NF {
agsize=`$XFS_INFO_PROG $loop_mnt | $SED_PROG -n 's/.*agsize=\(.*\) blks.*/\1/p'`
# Convert free space (agno, block, length) to (start sector, end sector)
$UMOUNT_PROG $loop_mnt
- $XFS_DB_PROG -r -c "freesp -d" $img_file | $SED_PROG '/^.*from/,$d'| \
+ $XFS_DB_PROG -r -c "freesp -d" $loop_dev | $SED_PROG '/^.*from/,$d'| \
$AWK_PROG -v spb=$sectors_per_block -v agsize=$agsize \
'{ print spb * ($1 * agsize + $2), spb * ($1 * agsize + $2 + $3) - 1 }'
;;
local device_size=$($BTRFS_UTIL_PROG filesystem show --raw $loop_mnt 2>&1 \
| sed -n "s/^.*size \([0-9]*\).*$/\1/p")
- local nodesize=$($BTRFS_UTIL_PROG inspect-internal dump-super $img_file \
+ local nodesize=$($BTRFS_UTIL_PROG inspect-internal dump-super $loop_dev \
| sed -n 's/nodesize\s*\(.*\)/\1/p')
# Get holes within block groups
- $BTRFS_UTIL_PROG inspect-internal dump-tree -t extent $img_file \
+ $BTRFS_UTIL_PROG inspect-internal dump-tree -t extent $loop_dev \
| $AWK_PROG -v sectorsize=512 -v nodesize=$nodesize -f $here/src/parse-extent-tree.awk
# Get holes within unallocated space on disk
- $BTRFS_UTIL_PROG inspect-internal dump-tree -t dev $img_file \
+ $BTRFS_UTIL_PROG inspect-internal dump-tree -t dev $loop_dev \
| $AWK_PROG -v sectorsize=512 -v devsize=$device_size -f $here/src/parse-dev-tree.awk
;;
# Get reference fiemap, this can contain i.e. uninitialized inode table
sync
-get_holes $img_file > $fiemap_ref
+get_holes > $fiemap_ref
# Delete some files
find $loop_mnt -type f -print | $AWK_PROG \
echo -n "Detecting interesting holes in image..."
# Get after-trim fiemap
sync
-get_holes $img_file > $fiemap_after
+get_holes > $fiemap_after
echo "done."
echo -n "Comparing holes to the reported space from FS..."
include $(BUILDRULES)
-install:
+install: default
$(INSTALL) -m 755 -d $(TARGET_DIR)
$(INSTALL) -m 755 $(TESTS) $(TARGET_DIR)
$(INSTALL) -m 644 group.list $(TARGET_DIR)
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023 Rodrigo Campos Catelin (Microsoft). All Rights Reserved.
+#
+# FS QA Test 001
+#
+# Test that idmapped mounts behave correctly with tmpfs filesystem.
+#
+. ./common/preamble
+_begin_fstest auto quick idmapped
+
+# get standard environment, filters and checks
+. ./common/filter
+
+# real QA test starts here
+
+_supported_fs tmpfs
+_require_idmapped_mounts
+_require_test
+
+echo "Silence is golden"
+
+$here/src/vfs/vfstest --test-tmpfs --device "$TEST_DEV" \
+ --mount "$TEST_DIR" --fstype "$FSTYP"
+
+status=$?
+exit
--- /dev/null
+QA output created by 001
+Silence is golden
--- /dev/null
+#
+# Copyright (c) 2023 Rodrigo Campos Catelin (Microsoft). All Rights Reserved.
+#
+
+TOPDIR = ../..
+include $(TOPDIR)/include/builddefs
+include $(TOPDIR)/include/buildgrouplist
+
+TMPFS_DIR = tmpfs
+TARGET_DIR = $(PKG_LIB_DIR)/$(TESTS_DIR)/$(TMPFS_DIR)
+DIRT = group.list
+
+default: $(DIRT)
+
+include $(BUILDRULES)
+
+install: default
+ $(INSTALL) -m 755 -d $(TARGET_DIR)
+ $(INSTALL) -m 755 $(TESTS) $(TARGET_DIR)
+ $(INSTALL) -m 644 group.list $(TARGET_DIR)
+ $(INSTALL) -m 644 $(OUTFILES) $(TARGET_DIR)
+
+# Nothing.
+install-dev install-lib:
include $(BUILDRULES)
-install:
+install: default
$(INSTALL) -m 755 -d $(TARGET_DIR)
$(INSTALL) -m 755 $(TESTS) $(TARGET_DIR)
$(INSTALL) -m 644 group.list $(TARGET_DIR)
QA output created by 006
error/fail_at_unmount=1
error/metadata/default/max_retries=-1
-error/metadata/default/retry_timeout_seconds=0
+error/metadata/default/retry_timeout_seconds=-1
error/metadata/EIO/max_retries=-1
-error/metadata/EIO/retry_timeout_seconds=0
+error/metadata/EIO/retry_timeout_seconds=-1
error/metadata/ENOSPC/max_retries=-1
-error/metadata/ENOSPC/retry_timeout_seconds=0
+error/metadata/ENOSPC/retry_timeout_seconds=-1
echo "holes is in range"
else
# quick check - how many holes did we get?
- count=`xfs_bmap $out | egrep -c ': hole'`
+ count=`xfs_bmap $out | grep -E -c ': hole'`
# blocks can end up adjacent, therefore number of holes varies
_within_tolerance "holes" $count $_holes 10% -v
fi
# Override the default cleanup function.
_cleanup()
{
+ # Make sure $SCRATCH_MNT is unfreezed
+ xfs_freeze -u $SCRATCH_MNT 2>/dev/null
$KILLALL_PROG -9 fsstress 2>/dev/null
wait
cd /
- _scratch_unmount 2>/dev/null
rm -f $tmp.*
}
if [ $failed -eq 0 ]
then
# quick check - how many holes did we get?
- count=`xfs_bmap $out | egrep -c ': hole'`
+ count=`xfs_bmap $out | grep -E -c ': hole'`
echo " $count hole(s) detected"
# and how big was the file?
_filesize $out
# ENOSPC/EDQUOT.
#
. ./common/preamble
-_begin_fstest auto enospc quick quota
+_begin_fstest auto enospc quick quota prealloc
# Import common functions.
. ./common/filter
# need 128M space, don't make any assumption
_scratch_mkfs >/dev/null 2>&1
_scratch_mount
-_require_fs_space $SCRATCH_MNT 131072
+_require_fs_space $SCRATCH_MNT 196608
_scratch_unmount
-_scratch_mkfs_sized $((32 * 1024 * 1024)) > $tmp.mkfs.raw || _fail "mkfs failed"
+_scratch_mkfs_sized $((96 * 1024 * 1024)) > $tmp.mkfs.raw || _fail "mkfs failed"
cat $tmp.mkfs.raw | _filter_mkfs >$seqres.full 2>$tmp.mkfs
# get original data blocks number and agcount
. $tmp.mkfs
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022, Oracle and/or its affiliates. All Rights Reserved.
+#
+# FS QA Test No. 018
+#
+# Log attribute replay test
+#
+. ./common/preamble
+_begin_fstest auto quick attr
+
+# get standard environment, filters and checks
+. ./common/filter
+. ./common/attr
+. ./common/inject
+
+_cleanup()
+{
+ rm -rf $tmp.*
+ if [ -n "$ORIG_XFS_LARP" ];then
+ echo $ORIG_XFS_LARP > /sys/fs/xfs/debug/larp
+ fi
+}
+
+test_attr_replay()
+{
+ testfile=$testdir/$1
+ attr_name=$2
+ attr_value=$3
+ flag=$4
+ error_tag=$5
+
+ # Inject error
+ _scratch_inject_error $error_tag
+
+ # Set attribute, being careful not to include the trailing newline
+ # in the attr value.
+ echo -n "$attr_value" | ${ATTR_PROG} -$flag "$attr_name" $testfile 2>&1 | \
+ _filter_scratch
+
+ # FS should be shut down, touch will fail
+ touch $testfile 2>&1 | _filter_scratch
+
+ # Remount to replay log
+ _scratch_remount_dump_log >> $seqres.full
+
+ # FS should be online, touch should succeed
+ touch $testfile
+
+ # Verify attr recovery
+ $ATTR_PROG -l $testfile >> $seqres.full
+ echo "Checking contents of $attr_name" >> $seqres.full
+ echo -n "$attr_name: "
+ $ATTR_PROG -q -g $attr_name $testfile 2> /dev/null | md5sum;
+
+ echo ""
+}
+
+create_test_file()
+{
+ filename=$testdir/$1
+ count=$2
+ attr_value=$3
+
+ touch $filename
+
+ for i in `seq $count`
+ do
+ $ATTR_PROG -s "attr_name$i" -V $attr_value $filename >> \
+ $seqres.full
+ done
+}
+
+require_larp()
+{
+ touch $SCRATCH_MNT/a
+
+ # Set attribute, being careful not to include the trailing newline
+ # in the attr value.
+ echo -n "attr_value" | ${ATTR_PROG} -s "attr_name" $SCRATCH_MNT/a 2>&1 | \
+ grep 'Operation not supported' && \
+ _notrun 'LARP not supported on this filesystem'
+}
+
+# real QA test starts here
+_supported_fs xfs
+
+_require_scratch
+_require_scratch_xfs_crc
+_require_attrs
+_require_xfs_io_error_injection "larp"
+_require_xfs_io_error_injection "da_leaf_split"
+_require_xfs_io_error_injection "attr_leaf_to_node"
+_require_xfs_sysfs debug/larp
+test -w /sys/fs/xfs/debug/larp || _notrun "larp knob not writable"
+
+ORIG_XFS_LARP=`cat /sys/fs/xfs/debug/larp`
+# turn on log attributes
+echo 1 > /sys/fs/xfs/debug/larp
+
+attr16="0123456789ABCDEF"
+attr17="X$attr16"
+attr64="$attr16$attr16$attr16$attr16"
+attr256="$attr64$attr64$attr64$attr64"
+attr1k="$attr256$attr256$attr256$attr256"
+attr4k="$attr1k$attr1k$attr1k$attr1k"
+attr8k="$attr4k$attr4k"
+attr16k="$attr8k$attr8k"
+attr32k="$attr16k$attr16k"
+attr32l="X$attr32k"
+attr64k="$attr32k$attr32k"
+
+echo "*** mkfs"
+_scratch_mkfs >/dev/null
+
+blk_sz=$(_scratch_xfs_get_sb_field blocksize)
+err_inj_attr_sz=$(( blk_sz / 3 - 50 ))
+err_inj_attr_val=$(printf "A%.0s" $(seq $err_inj_attr_sz))
+
+echo "*** mount FS"
+_scratch_mount
+
+testdir=$SCRATCH_MNT/testdir
+mkdir $testdir
+
+require_larp
+
+# empty, inline
+create_test_file empty_file1 0
+test_attr_replay empty_file1 "attr_name" $attr64 "s" "larp"
+test_attr_replay empty_file1 "attr_name" $attr64 "r" "larp"
+
+# empty, inline with an unaligned value
+create_test_file empty_fileX 0
+test_attr_replay empty_fileX "attr_nameX" $attr17 "s" "larp"
+test_attr_replay empty_fileX "attr_nameX" $attr17 "r" "larp"
+
+# empty, internal
+create_test_file empty_file2 0
+test_attr_replay empty_file2 "attr_name" $attr1k "s" "larp"
+test_attr_replay empty_file2 "attr_name" $attr1k "r" "larp"
+
+# empty, remote
+create_test_file empty_file3 0
+test_attr_replay empty_file3 "attr_name" $attr64k "s" "larp"
+test_attr_replay empty_file3 "attr_name" $attr64k "r" "larp"
+
+# empty, remote with an unaligned value
+create_test_file empty_fileY 0
+test_attr_replay empty_fileY "attr_name" $attr32l "s" "larp"
+test_attr_replay empty_fileY "attr_name" $attr32l "r" "larp"
+
+# inline, inline
+create_test_file inline_file1 1 $attr16
+test_attr_replay inline_file1 "attr_name2" $attr64 "s" "larp"
+test_attr_replay inline_file1 "attr_name2" $attr64 "r" "larp"
+
+# inline, internal
+create_test_file inline_file2 1 $attr16
+test_attr_replay inline_file2 "attr_name2" $attr1k "s" "larp"
+test_attr_replay inline_file2 "attr_name2" $attr1k "r" "larp"
+
+# inline, remote
+create_test_file inline_file3 1 $attr16
+test_attr_replay inline_file3 "attr_name2" $attr64k "s" "larp"
+test_attr_replay inline_file3 "attr_name2" $attr64k "r" "larp"
+
+# extent, internal
+create_test_file extent_file1 1 $attr1k
+test_attr_replay extent_file1 "attr_name2" $attr1k "s" "larp"
+test_attr_replay extent_file1 "attr_name2" $attr1k "r" "larp"
+
+# extent, inject error on split
+create_test_file extent_file2 3 $err_inj_attr_val
+test_attr_replay extent_file2 "attr_name4" $attr256 "s" "da_leaf_split"
+
+# extent, inject error on fork transition
+create_test_file extent_file3 3 $err_inj_attr_val
+test_attr_replay extent_file3 "attr_name4" $attr256 "s" "attr_leaf_to_node"
+
+# extent, remote
+create_test_file extent_file4 1 $attr1k
+test_attr_replay extent_file4 "attr_name2" $attr64k "s" "larp"
+test_attr_replay extent_file4 "attr_name2" $attr64k "r" "larp"
+
+# remote, internal
+create_test_file remote_file1 1 $attr64k
+test_attr_replay remote_file1 "attr_name2" $attr1k "s" "larp"
+test_attr_replay remote_file1 "attr_name2" $attr1k "r" "larp"
+
+# remote, remote
+create_test_file remote_file2 1 $attr64k
+test_attr_replay remote_file2 "attr_name2" $attr64k "s" "larp"
+test_attr_replay remote_file2 "attr_name2" $attr64k "r" "larp"
+
+# replace shortform with different value
+create_test_file sf_file 2 $attr64
+test_attr_replay sf_file "attr_name2" $attr16 "s" "larp"
+
+# replace leaf with different value
+create_test_file leaf_file 3 $attr1k
+test_attr_replay leaf_file "attr_name2" $attr256 "s" "larp"
+
+# replace node with a different value
+create_test_file node_file 1 $attr64k
+$ATTR_PROG -s "attr_name2" -V $attr1k $testdir/node_file \
+ >> $seqres.full
+test_attr_replay node_file "attr_name2" $attr256 "s" "larp"
+
+echo "*** done"
+status=0
+exit
--- /dev/null
+QA output created by 018
+*** mkfs
+*** mount FS
+attr_set: Input/output error
+Could not set "attr_name" for SCRATCH_MNT/testdir/empty_file1
+touch: cannot touch 'SCRATCH_MNT/testdir/empty_file1': Input/output error
+attr_name: e889d82dd111d6315d7b1edce2b1b30f -
+
+attr_remove: Input/output error
+Could not remove "attr_name" for SCRATCH_MNT/testdir/empty_file1
+touch: cannot touch 'SCRATCH_MNT/testdir/empty_file1': Input/output error
+attr_name: d41d8cd98f00b204e9800998ecf8427e -
+
+attr_set: Input/output error
+Could not set "attr_nameX" for SCRATCH_MNT/testdir/empty_fileX
+touch: cannot touch 'SCRATCH_MNT/testdir/empty_fileX': Input/output error
+attr_nameX: cb72c43fb97dd3cb4ac6ad2d9bd365e1 -
+
+attr_remove: Input/output error
+Could not remove "attr_nameX" for SCRATCH_MNT/testdir/empty_fileX
+touch: cannot touch 'SCRATCH_MNT/testdir/empty_fileX': Input/output error
+attr_nameX: d41d8cd98f00b204e9800998ecf8427e -
+
+attr_set: Input/output error
+Could not set "attr_name" for SCRATCH_MNT/testdir/empty_file2
+touch: cannot touch 'SCRATCH_MNT/testdir/empty_file2': Input/output error
+attr_name: 4198214ee02e6ad7ac39559cd3e70070 -
+
+attr_remove: Input/output error
+Could not remove "attr_name" for SCRATCH_MNT/testdir/empty_file2
+touch: cannot touch 'SCRATCH_MNT/testdir/empty_file2': Input/output error
+attr_name: d41d8cd98f00b204e9800998ecf8427e -
+
+attr_set: Input/output error
+Could not set "attr_name" for SCRATCH_MNT/testdir/empty_file3
+touch: cannot touch 'SCRATCH_MNT/testdir/empty_file3': Input/output error
+attr_name: 7f6fd1b6d872108bd44bd143cbcdfa19 -
+
+attr_remove: Input/output error
+Could not remove "attr_name" for SCRATCH_MNT/testdir/empty_file3
+touch: cannot touch 'SCRATCH_MNT/testdir/empty_file3': Input/output error
+attr_name: d41d8cd98f00b204e9800998ecf8427e -
+
+attr_set: Input/output error
+Could not set "attr_name" for SCRATCH_MNT/testdir/empty_fileY
+touch: cannot touch 'SCRATCH_MNT/testdir/empty_fileY': Input/output error
+attr_name: 3ad10faae9dcfa4083f8dfa641688733 -
+
+attr_remove: Input/output error
+Could not remove "attr_name" for SCRATCH_MNT/testdir/empty_fileY
+touch: cannot touch 'SCRATCH_MNT/testdir/empty_fileY': Input/output error
+attr_name: d41d8cd98f00b204e9800998ecf8427e -
+
+attr_set: Input/output error
+Could not set "attr_name2" for SCRATCH_MNT/testdir/inline_file1
+touch: cannot touch 'SCRATCH_MNT/testdir/inline_file1': Input/output error
+attr_name2: e889d82dd111d6315d7b1edce2b1b30f -
+
+attr_remove: Input/output error
+Could not remove "attr_name2" for SCRATCH_MNT/testdir/inline_file1
+touch: cannot touch 'SCRATCH_MNT/testdir/inline_file1': Input/output error
+attr_name2: d41d8cd98f00b204e9800998ecf8427e -
+
+attr_set: Input/output error
+Could not set "attr_name2" for SCRATCH_MNT/testdir/inline_file2
+touch: cannot touch 'SCRATCH_MNT/testdir/inline_file2': Input/output error
+attr_name2: 4198214ee02e6ad7ac39559cd3e70070 -
+
+attr_remove: Input/output error
+Could not remove "attr_name2" for SCRATCH_MNT/testdir/inline_file2
+touch: cannot touch 'SCRATCH_MNT/testdir/inline_file2': Input/output error
+attr_name2: d41d8cd98f00b204e9800998ecf8427e -
+
+attr_set: Input/output error
+Could not set "attr_name2" for SCRATCH_MNT/testdir/inline_file3
+touch: cannot touch 'SCRATCH_MNT/testdir/inline_file3': Input/output error
+attr_name2: 7f6fd1b6d872108bd44bd143cbcdfa19 -
+
+attr_remove: Input/output error
+Could not remove "attr_name2" for SCRATCH_MNT/testdir/inline_file3
+touch: cannot touch 'SCRATCH_MNT/testdir/inline_file3': Input/output error
+attr_name2: d41d8cd98f00b204e9800998ecf8427e -
+
+attr_set: Input/output error
+Could not set "attr_name2" for SCRATCH_MNT/testdir/extent_file1
+touch: cannot touch 'SCRATCH_MNT/testdir/extent_file1': Input/output error
+attr_name2: 4198214ee02e6ad7ac39559cd3e70070 -
+
+attr_remove: Input/output error
+Could not remove "attr_name2" for SCRATCH_MNT/testdir/extent_file1
+touch: cannot touch 'SCRATCH_MNT/testdir/extent_file1': Input/output error
+attr_name2: d41d8cd98f00b204e9800998ecf8427e -
+
+attr_set: Input/output error
+Could not set "attr_name4" for SCRATCH_MNT/testdir/extent_file2
+touch: cannot touch 'SCRATCH_MNT/testdir/extent_file2': Input/output error
+attr_name4: 5bbe95fdc1361656e833390aefafceb6 -
+
+attr_set: Input/output error
+Could not set "attr_name4" for SCRATCH_MNT/testdir/extent_file3
+touch: cannot touch 'SCRATCH_MNT/testdir/extent_file3': Input/output error
+attr_name4: 5bbe95fdc1361656e833390aefafceb6 -
+
+attr_set: Input/output error
+Could not set "attr_name2" for SCRATCH_MNT/testdir/extent_file4
+touch: cannot touch 'SCRATCH_MNT/testdir/extent_file4': Input/output error
+attr_name2: 7f6fd1b6d872108bd44bd143cbcdfa19 -
+
+attr_remove: Input/output error
+Could not remove "attr_name2" for SCRATCH_MNT/testdir/extent_file4
+touch: cannot touch 'SCRATCH_MNT/testdir/extent_file4': Input/output error
+attr_name2: d41d8cd98f00b204e9800998ecf8427e -
+
+attr_set: Input/output error
+Could not set "attr_name2" for SCRATCH_MNT/testdir/remote_file1
+touch: cannot touch 'SCRATCH_MNT/testdir/remote_file1': Input/output error
+attr_name2: 4198214ee02e6ad7ac39559cd3e70070 -
+
+attr_remove: Input/output error
+Could not remove "attr_name2" for SCRATCH_MNT/testdir/remote_file1
+touch: cannot touch 'SCRATCH_MNT/testdir/remote_file1': Input/output error
+attr_name2: d41d8cd98f00b204e9800998ecf8427e -
+
+attr_set: Input/output error
+Could not set "attr_name2" for SCRATCH_MNT/testdir/remote_file2
+touch: cannot touch 'SCRATCH_MNT/testdir/remote_file2': Input/output error
+attr_name2: 7f6fd1b6d872108bd44bd143cbcdfa19 -
+
+attr_remove: Input/output error
+Could not remove "attr_name2" for SCRATCH_MNT/testdir/remote_file2
+touch: cannot touch 'SCRATCH_MNT/testdir/remote_file2': Input/output error
+attr_name2: d41d8cd98f00b204e9800998ecf8427e -
+
+attr_set: Input/output error
+Could not set "attr_name2" for SCRATCH_MNT/testdir/sf_file
+touch: cannot touch 'SCRATCH_MNT/testdir/sf_file': Input/output error
+attr_name2: e43df9b5a46b755ea8f1b4dd08265544 -
+
+attr_set: Input/output error
+Could not set "attr_name2" for SCRATCH_MNT/testdir/leaf_file
+touch: cannot touch 'SCRATCH_MNT/testdir/leaf_file': Input/output error
+attr_name2: 5bbe95fdc1361656e833390aefafceb6 -
+
+attr_set: Input/output error
+Could not set "attr_name2" for SCRATCH_MNT/testdir/node_file
+touch: cannot touch 'SCRATCH_MNT/testdir/node_file': Input/output error
+attr_name2: 5bbe95fdc1361656e833390aefafceb6 -
+
+*** done
#
_filter_bad_ids()
{
- egrep -v 'bad user id 0xffffffff|bad group id 0xffffffff'
+ grep -E -v 'bad user id 0xffffffff|bad group id 0xffffffff'
}
# real QA test starts here
onemeginblocks=`expr 1048576 / $bsize`
_scratch_mount
+# We're growing the data device, so force new file creation there
+_xfs_force_bdev data $SCRATCH_MNT
+
echo "done"
# full allocation group -> partial; partial -> expand partial + new partial;
set +x
. ./common/preamble
-_begin_fstest fsr ioctl auto
+_begin_fstest fsr ioctl auto prealloc
# Override the default cleanup function.
_cleanup()
_do_die_on_error=message_only
-echo -n "Make a 48 megabyte filesystem on SCRATCH_DEV and mount... "
-_scratch_mkfs_xfs -dsize=48m,agcount=3 2>&1 >/dev/null || _fail "mkfs failed"
+echo -n "Make a 96 megabyte filesystem on SCRATCH_DEV and mount... "
+_scratch_mkfs_xfs -dsize=96m,agcount=3 2>&1 >/dev/null || _fail "mkfs failed"
_scratch_mount
echo "done"
-echo -n "Reserve 16 1Mb unfragmented regions... "
-for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
+echo -n "Reserve 32 1Mb unfragmented regions... "
+for i in `seq 1 32`
do
_do "$XFS_IO_PROG -f -c \"resvsp 0 1m\" $SCRATCH_MNT/hole$i"
_do "$XFS_IO_PROG -f -c \"resvsp 0 4k\" $SCRATCH_MNT/space$i"
# set up filesystem
echo -n "Fill filesystem with fill file... "
-for i in `seq 0 1 31`; do
+for i in `seq 0 1 63`; do
_do "$XFS_IO_PROG -f -c \"falloc ${i}m 1m\" $SCRATCH_MNT/fill"
done
_do "xfs_bmap -vp $SCRATCH_MNT/fill"
# create fragmented file
#_do "Delete every second file" "_cull_files"
echo -n "Punch every second 4k block... "
-for i in `seq 0 8 32768`; do
+for i in `seq 0 8 65536`; do
# This generates excessive output that significantly slows down the
# test. It's not necessary for debug, so just bin it.
$XFS_IO_PROG -f -c "unresvsp ${i}k 4k" $SCRATCH_MNT/fill \
echo "done"
echo -n "Create one very large file... "
-_do "$here/src/fill2 -d nbytes=16000000,file=$SCRATCH_MNT/fragmented"
+_do "$here/src/fill2 -d nbytes=32000000,file=$SCRATCH_MNT/fragmented"
echo "done"
_do "xfs_bmap -v $SCRATCH_MNT/fragmented"
_do "sum $SCRATCH_MNT/fragmented >$tmp.sum1"
QA output created by 042
-Make a 48 megabyte filesystem on SCRATCH_DEV and mount... done
-Reserve 16 1Mb unfragmented regions... done
+Make a 96 megabyte filesystem on SCRATCH_DEV and mount... done
+Reserve 32 1Mb unfragmented regions... done
Fill filesystem with fill file... done
Use up any further available space... done
Punch every second 4k block... done
# Start a workload and shutdown the fs. The subsequent mount will require log
# recovery.
-$FSSTRESS_PROG -n 9999 -p 2 -w -d $SCRATCH_MNT > /dev/null 2>&1 &
+$FSSTRESS_PROG -n 9999 -p 2 -w -d $SCRATCH_MNT &>> $seqres.full &
sleep 5
_scratch_shutdown -f
$KILLALL_PROG -q $FSSTRESS_PROG
_scratch_mount
# populate the fs with some data and cycle the mount to reset the log head/tail
-$FSSTRESS_PROG -d $SCRATCH_MNT -z -fcreat=1 -p 4 -n 100000 > /dev/null 2>&1
+$FSSTRESS_PROG -d $SCRATCH_MNT -z -fcreat=1 -p 4 -n 100000 >> $seqres.full
_scratch_cycle_mount || _fail "cycle mount failed"
# Pin the tail and start a file removal workload. File removal tends to
# Filter out the housekeeping files of xfsrestore
#
$AWK_PROG 'NF == 9 { print $5, $9 }' |\
- egrep -v 'dumpdir|housekeeping|dirattr|dirextattr|namreg|state|tree'
+ grep -E -v 'dumpdir|housekeeping|dirattr|dirextattr|namreg|state|tree'
}
# real QA test starts here
if [ $i -gt 0 ]; then
sleep 2
_modify_level $i
- fi
+ fi
_stable_fs
sleep 2
find $__dir -exec $here/src/lstat64 -t {} \; |\
sed -e 's/.*dumpdir/dumpdir/' -e '/^dumpdir /d' |\
sed -e 's/.*restoredir/restoredir/' -e '/^restoredir /d' |\
- egrep -v 'housekeeping|dirattr|dirextattr|namreg|state|tree' |\
+ grep -E -v 'housekeeping|dirattr|dirextattr|namreg|state|tree' |\
awk '$3 ~ /^d/ { $2 = "XXX" } {print}' |\
LC_COLLATE=POSIX sort
}
_scratch_mkfs_xfs >/dev/null 2>&1
_scratch_mount
+_require_congruent_file_oplen $SCRATCH_MNT 8388608
small=$SCRATCH_MNT/small
big=$SCRATCH_MNT/big
# inodes (.i.e., free space) have been consumed.
#
. ./common/preamble
-_begin_fstest auto enospc punch
+_begin_fstest auto enospc punch prealloc
# Import common functions.
. ./common/filter
# bitmap consuming all the free space in our small data device.
unset SCRATCH_RTDEV
-_scratch_mkfs "-d size=50m -m crc=1 -i sparse" | tee -a $seqres.full |
+_scratch_mkfs "-d size=96m -m crc=1 -i sparse" | tee -a $seqres.full |
_filter_mkfs > /dev/null 2> $tmp.mkfs
. $tmp.mkfs # for isize
# rwtest (iogen|doio)
#
. ./common/preamble
-_begin_fstest rw ioctl
+_begin_fstest rw ioctl auto quick
# Import common functions.
. ./common/filter
_supported_fs xfs
_require_test
+_require_xfs_io_command falloc # iogen requires falloc
quiet=-q
clean=-c
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Red Hat, Inc. All Rights Reserved.
+#
+# FS QA Test No. 081
+#
+# This's a regression test for:
+# a1de97fe296c ("xfs: Fix the free logic of state in xfs_attr_node_hasname")
+#
+# After we corrupted an attr leaf block (under node block), getxattr might hit
+# EFSCORRUPTED in xfs_attr_node_get when it does xfs_attr_node_hasname. A bug
+# cause xfs_attr_node_get won't do xfs_buf_trans release job, then a subsequent
+# removexattr will hang.
+#
+. ./common/preamble
+_begin_fstest auto quick attr
+
+# Import common functions.
+. ./common/filter
+. ./common/attr
+. ./common/populate
+
+# real QA test starts here
+_supported_fs xfs
+_fixed_by_kernel_commit a1de97fe296c \
+ "xfs: Fix the free logic of state in xfs_attr_node_hasname"
+
+_require_scratch_nocheck
+# Only test with v5 xfs on-disk format
+_require_scratch_xfs_crc
+_require_attrs
+_require_populate_commands
+_require_xfs_db_blocktrash_z_command
+
+_scratch_mkfs_xfs | _filter_mkfs >$seqres.full 2>$tmp.mkfs
+source $tmp.mkfs
+_scratch_mount
+
+# Create more than $((dbsize / 32)) xattr entries to force the creation of a
+# node block and two (or more) leaf blocks, which we need for this test.
+nr_xattr="$((dbsize / 32))"
+localfile="${SCRATCH_MNT}/attrfile"
+touch $localfile
+for ((i=0; i<nr_xattr; i++));do
+ $SETFATTR_PROG -n user.x$(printf "%.09d" "$i") -v "aaaaaaaaaaaaaaaa" $localfile
+done
+inumber="$(stat -c '%i' $localfile)"
+_scratch_unmount
+
+# Expect the ablock 0 is a node block, later ablocks(>=1) are leaf blocks, then corrupt
+# the last leaf block. (Don't corrupt node block, or can't reproduce the bug)
+magic=$(_scratch_xfs_get_metadata_field "hdr.info.hdr.magic" "inode $inumber" "ablock 0")
+level=$(_scratch_xfs_get_metadata_field "hdr.level" "inode $inumber" "ablock 0")
+count=$(_scratch_xfs_get_metadata_field "hdr.count" "inode $inumber" "ablock 0")
+if [ "$magic" = "0x3ebe" -a "$level" = "1" ];then
+ # Corrupt the last leaf block
+ _scratch_xfs_db -x -c "inode ${inumber}" -c "ablock $count" -c "stack" \
+ -c "blocktrash -x 32 -y $((dbsize*8)) -3 -z" >> $seqres.full
+else
+ _fail "The ablock 0 (magic=$magic, level=$level) isn't a root node block, maybe case issue"
+fi
+
+# This's the real testing, expect removexattr won't hang or panic.
+if _try_scratch_mount >> $seqres.full 2>&1; then
+ for ((i=0; i<nr_xattr; i++));do
+ $GETFATTR_PROG -n user.x$(printf "%.09d" "$i") $localfile >/dev/null 2>&1
+ $SETFATTR_PROG -x user.x$(printf "%.09d" "$i") $localfile 2>/dev/null
+ done
+else
+ # Due to we corrupt the xfs manually, so can't be sure if xfs can
+ # detect this corruption and refuse the mount directly in one day.
+ # If so, it's not a testing fail, so "notrun" directly to mark this
+ # test isn't really done
+ _notrun "XFS refused to mount with this xattr corruption, test skipped"
+fi
+
+echo "Silence is golden"
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 081
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 082
+#
+# Regression test for xfsprogs commit:
+#
+# XXXXXXXX ("xfs_copy: don't use cached buffer reads until after libxfs_mount")
+#
+# It was discovered that passing xfs_copy a source device containing an ext4
+# filesystem would cause xfs_copy to crash. Further investigation revealed
+# that any readable path that didn't have a plausible XFS superblock in block
+# zero would produce the same crash, so this regression test exploits that.
+#
+. ./common/preamble
+_begin_fstest auto copy quick
+
+# real QA test starts here
+
+# Modify as appropriate.
+_supported_fs xfs
+_require_xfs_copy
+_require_test
+
+rm -f $TEST_DIR/$seq.*
+$XFS_IO_PROG -f -c 'truncate 100m' $TEST_DIR/$seq.a
+$XFS_IO_PROG -f -c 'truncate 100m' $TEST_DIR/$seq.b
+
+filter_copy() {
+ sed -e 's/Superblock has bad magic number.*/bad magic number/'
+}
+
+$XFS_COPY_PROG $TEST_DIR/$seq.a $TEST_DIR/$seq.b 2>&1 | filter_copy
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 082
+bad magic number
+xfs_copy: couldn't read superblock, error=22
echo "++ check fs (2)" >> $seqres.full
_repair_scratch_fs >> $seqres.full 2>&1
-egrep -q '(did not fix|makes no progress)' $seqres.full && echo "xfs_repair failed" | tee -a $seqres.full
+grep -E -q '(did not fix|makes no progress)' $seqres.full && echo "xfs_repair failed" | tee -a $seqres.full
if [ "$(wc -l < "$ROUND2_LOG")" -ne 8 ]; then
echo "xfs_repair did not fix everything" | tee -a $seqres.full
fi
# for data corruption (zeroes read) near the end of file.
#
. ./common/preamble
-_begin_fstest ioctl rw auto
+_begin_fstest ioctl rw auto prealloc
# Import common functions.
. ./common/filter
echo "+ mount fs image"
_scratch_mount
-$XFS_INFO_PROG "${SCRATCH_MNT}" | grep -q "finobt=1" || _notrun "finobt not enabled"
+_require_xfs_has_feature "$SCRATCH_MNT" finobt
blksz="$(stat -f -c '%s' "${SCRATCH_MNT}")"
echo "+ make some files"
echo "+ mount fs image"
_scratch_mount
-dblksz="$($XFS_INFO_PROG "${SCRATCH_MNT}" | grep naming.*bsize | sed -e 's/^.*bsize=//g' -e 's/\([0-9]*\).*$/\1/g')"
+dblksz=$(_xfs_get_dir_blocksize "$SCRATCH_MNT")
nr="$((dblksz / 40))"
blksz="$(stat -f -c '%s' "${SCRATCH_MNT}")"
leaf_lblk="$((32 * 1073741824 / blksz))"
echo "+ mount fs image"
_scratch_mount
-dblksz="$($XFS_INFO_PROG "${SCRATCH_MNT}" | grep naming.*bsize | sed -e 's/^.*bsize=//g' -e 's/\([0-9]*\).*$/\1/g')"
+dblksz=$(_xfs_get_dir_blocksize "$SCRATCH_MNT")
nr="$((dblksz / 12))"
blksz="$(stat -f -c '%s' "${SCRATCH_MNT}")"
leaf_lblk="$((32 * 1073741824 / blksz))"
echo "+ mount fs image"
_scratch_mount
-dblksz="$($XFS_INFO_PROG "${SCRATCH_MNT}" | grep naming.*bsize | sed -e 's/^.*bsize=//g' -e 's/\([0-9]*\).*$/\1/g')"
+dblksz=$(_xfs_get_dir_blocksize "$SCRATCH_MNT")
nr="$((dblksz / 12))"
blksz="$(stat -f -c '%s' "${SCRATCH_MNT}")"
leaf_lblk="$((32 * 1073741824 / blksz))"
echo "+ mount fs image"
_scratch_mount
-dblksz="$($XFS_INFO_PROG "${SCRATCH_MNT}" | grep naming.*bsize | sed -e 's/^.*bsize=//g' -e 's/\([0-9]*\).*$/\1/g')"
+dblksz=$(_xfs_get_dir_blocksize "$SCRATCH_MNT")
nr="$((16 * dblksz / 40))"
blksz="$(stat -f -c '%s' "${SCRATCH_MNT}")"
leaf_lblk="$((32 * 1073741824 / blksz))"
echo "+ mount fs image"
_scratch_mount
-dblksz="$($XFS_INFO_PROG "${SCRATCH_MNT}" | grep naming.*bsize | sed -e 's/^.*bsize=//g' -e 's/\([0-9]*\).*$/\1/g')"
+dblksz=$(_xfs_get_dir_blocksize "$SCRATCH_MNT")
nr="$((16 * dblksz / 40))"
blksz="$(stat -f -c '%s' "${SCRATCH_MNT}")"
leaf_lblk="$((32 * 1073741824 / blksz))"
filter_quot()
{
- _filter_quota | grep -v "root \|\#0 " \
+ _filter_quota | grep -v "root \|#0 " \
| sed -e '/#[0-9]*/s/#[0-9]*/#ID/g'
}
filter_report()
{
- _filter_quota | grep -v "^root \|^\#0 " \
+ _filter_quota | grep -v "^root \|^#0 " \
| sed -e '/^#[0-9]*/s/^#[0-9]*/#ID/g'
}
done
}
+# Some filesystem configurations fragment the file mapping more than others,
+# which leads to the quota block counts being slightly higher than the 48MB
+# written.
+filter_quota()
+{
+ sed -e 's/48\.[01]M/48M/g' | _filter_quota
+}
+
test_accounting()
{
echo "### some controlled buffered, direct and mmapd IO (type=$type)"
$here/src/lstat64 $file | head -3 | _filter_scratch
done
$XFS_IO_PROG -c syncfs $SCRATCH_MNT
- $XFS_QUOTA_PROG -c "quota -hnb -$type $id" $QARGS | _filter_quota
- $XFS_QUOTA_PROG -c "quota -hni -$type $id" $QARGS | _filter_quota
- $XFS_QUOTA_PROG -c "quota -hnr -$type $id" $QARGS | _filter_quota
+ $XFS_QUOTA_PROG -c "quota -hnb -$type $id" $QARGS | filter_quota
+ $XFS_QUOTA_PROG -c "quota -hni -$type $id" $QARGS | filter_quota
+ $XFS_QUOTA_PROG -c "quota -hnr -$type $id" $QARGS | filter_quota
}
export MOUNT_OPTIONS="-opquota"
fi
_scratch_unmount
-_scratch_mkfs_xfs -dsize=160m,agcount=4 $faststart | _filter_mkfs 2>$tmp.mkfs
+_scratch_mkfs_xfs -dsize=320m,agcount=4 $faststart | _filter_mkfs 2>$tmp.mkfs
cat $tmp.mkfs >>$seqres.full
_scratch_mount
echo "+ mount fs image"
_scratch_mount
-dblksz="$($XFS_INFO_PROG "${SCRATCH_MNT}" | grep naming.*bsize | sed -e 's/^.*bsize=//g' -e 's/\([0-9]*\).*$/\1/g')"
+dblksz=$(_xfs_get_dir_blocksize "$SCRATCH_MNT")
nr="$((16 * dblksz / 40))"
blksz="$(stat -f -c '%s' "${SCRATCH_MNT}")"
leaf_lblk="$((32 * 1073741824 / blksz))"
echo "+ mount fs image"
_scratch_mount
-dblksz="$($XFS_INFO_PROG "${SCRATCH_MNT}" | grep naming.*bsize | sed -e 's/^.*bsize=//g' -e 's/\([0-9]*\).*$/\1/g')"
+dblksz=$(_xfs_get_dir_blocksize "$SCRATCH_MNT")
nr="$((128 * dblksz / 40))"
blksz="$(stat -f -c '%s' "${SCRATCH_MNT}")"
leaf_lblk="$((32 * 1073741824 / blksz))"
# extents on either side of the collapse area are mergeable.
#
. ./common/preamble
-_begin_fstest auto quick clone rmap collapse insert
+_begin_fstest auto quick clone rmap collapse insert prealloc
# Import common functions.
. ./common/filter
# real QA test starts here
_supported_fs xfs
_require_test_program "punch-alternating"
+_require_cp_reflink
+_require_scratch_reflink
_require_xfs_scratch_rmapbt
_require_xfs_io_command "falloc"
_require_xfs_io_command "fcollapse"
len2=$((blocks2 * blksz))
file_blksz=$(_get_file_block_size $SCRATCH_MNT)
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
+
echo "Create some files"
$XFS_IO_PROG -f \
-c "falloc 0 $len1" \
# ip->i_df.if_bytes not ip->i_d.di_nextents in xfs_swap_extent_forks
#
. ./common/preamble
-_begin_fstest auto quick fsr
+_begin_fstest auto quick fsr prealloc
# Import common functions.
. ./common/filter
. ./common/preamble
_begin_fstest log v2log auto freeze
+# Override the default cleanup function.
+_cleanup()
+{
+ # Make sure $SCRATCH_MNT is unfreezed
+ xfs_freeze -u $SCRATCH_MNT 2>/dev/null
+ cd /
+ rm -f $tmp.*
+}
+
# Import common functions.
. ./common/filter
_supported_fs xfs
_require_scratch
+_require_freeze
# this may hang
sync
_supported_fs xfs
_require_command "$INDENT_PROG" indent
+# Starting in Linux 6.1, the EFI log formats were adjusted away from using
+# single-element arrays as flex arrays.
+_wants_kernel_commit 03a7485cd701 \
+ "xfs: fix memcpy fortify errors in EFI log format copying"
+
# filter out known changes to xfs type sizes
_type_size_filter()
{
# lazy SB adds __be32 agf_btreeblks - pv960372
+ # flexarray conversion of the attr structures in Linux 6.5 changed
+ # the sizeof output
if [ "$($MKFS_XFS_PROG 2>&1 | grep -c lazy-count )" == "0" ]; then
perl -ne '
s/sizeof\( xfs_agf_t \) = 60/sizeof( xfs_agf_t ) = <SIZE>/;
+s/sizeof\(struct xfs_attr3_leafblock\) = 80/sizeof(struct xfs_attr3_leafblock) = 88/;
+s/sizeof\(struct xfs_attr_shortform\) = 4/sizeof(struct xfs_attr_shortform) = 8/;
+s/sizeof\(xfs_attr_leafblock_t\) = 32/sizeof(xfs_attr_leafblock_t) = 40/;
print;'
else
perl -ne '
s/sizeof\( xfs_agf_t \) = 64/sizeof( xfs_agf_t ) = <SIZE>/;
+s/sizeof\(struct xfs_attr3_leafblock\) = 80/sizeof(struct xfs_attr3_leafblock) = 88/;
+s/sizeof\(struct xfs_attr_shortform\) = 4/sizeof(struct xfs_attr_shortform) = 8/;
+s/sizeof\(xfs_attr_leafblock_t\) = 32/sizeof(xfs_attr_leafblock_t) = 40/;
print;'
fi
}
#
cat /usr/include/xfs/xfs*.h | indent |\
_attribute_filter |\
-egrep '(} *xfs_.*_t|^struct xfs_[a-z0-9_]*$)' |\
-egrep -v -f $tmp.ignore |\
+grep -E '(} *xfs_.*_t|^(union|struct) xfs_[a-z0-9_]*$)' |\
+grep -E -v -f $tmp.ignore |\
sed -e 's/^.*}[[:space:]]*//g' -e 's/;.*$//g' -e 's/_t, /_t\n/g' |\
sort | uniq |\
-awk '{printf("printf(\"sizeof(%s) = \\%zu\\n\", sizeof(%s));\n", $0, $0);}' |\
+awk '{printf("printf(\"sizeof(%s) = %%zu\\n\", sizeof(%s));\n", $0, $0);}' |\
cat >> $cprog
#
/typedef struct xfs_sb/ { structon = 1; next }
structon && $2 ~ /^sb_/ { sub(/[;,]/,"",$2)
sub(/XFSLABEL_MAX/,"12",$2)
- printf("printf(\"offsetof(xfs_sb_t, %s) = \\%zu\\n\", offsetof(xfs_sb_t, %s));", $2, $2); next}
+ printf("printf(\"offsetof(xfs_sb_t, %s) = %%zu\\n\", offsetof(xfs_sb_t, %s));", $2, $2); next}
structon && /}/ { structon = 0; next}
'>>$cprog
sizeof(struct xfs_attr3_leaf_hdr) = 80
sizeof(struct xfs_attr3_leafblock) = 88
sizeof(struct xfs_attr3_rmt_hdr) = 56
+sizeof(struct xfs_attr_sf_entry) = 3
+sizeof(struct xfs_attr_sf_hdr) = 4
sizeof(struct xfs_attr_shortform) = 8
sizeof(struct xfs_attrd_log_format) = 16
sizeof(struct xfs_attri_log_format) = 40
sizeof(struct xfs_dqblk) = 136
sizeof(struct xfs_dsb) = 264
sizeof(struct xfs_dsymlink_hdr) = 56
+sizeof(struct xfs_exch_range) = 120
sizeof(struct xfs_extent_data) = 24
sizeof(struct xfs_extent_data_info) = 32
sizeof(struct xfs_fs_eofblocks) = 128
sizeof(struct xfs_rud_log_format) = 16
sizeof(struct xfs_rui_log_format) = 16
sizeof(struct xfs_scrub_metadata) = 64
+sizeof(struct xfs_swap_extent) = 64
+sizeof(struct xfs_sxd_log_format) = 16
+sizeof(struct xfs_sxi_log_format) = 80
sizeof(struct xfs_unmount_log_format) = 8
+sizeof(union xfs_rtword_raw) = 4
+sizeof(union xfs_suminfo_raw) = 4
sizeof(xfs_agf_t) = 224
sizeof(xfs_agfl_t) = 36
sizeof(xfs_agi_t) = 344
sizeof(xfs_dq_logformat_t) = 24
sizeof(xfs_dqblk_t) = 136
sizeof(xfs_dsb_t) = 264
-sizeof(xfs_efd_log_format_32_t) = 28
-sizeof(xfs_efd_log_format_64_t) = 32
-sizeof(xfs_efi_log_format_32_t) = 28
-sizeof(xfs_efi_log_format_64_t) = 32
+sizeof(xfs_efd_log_format_32_t) = 16
+sizeof(xfs_efd_log_format_64_t) = 16
+sizeof(xfs_efi_log_format_32_t) = 16
+sizeof(xfs_efi_log_format_64_t) = 16
sizeof(xfs_error_injection_t) = 8
sizeof(xfs_exntfmt_t) = 4
sizeof(xfs_exntst_t) = 4
# Ensure that xfs_fsr un-reflinks files while defragmenting
#
. ./common/preamble
-_begin_fstest auto quick clone fsr
+_begin_fstest auto quick clone fsr prealloc
# Import common functions.
. ./common/filter
blksz=65536
real_blksz="$(_get_block_size $testdir)"
blksz_factor=$((blksz / real_blksz))
+
+# The expected free space numbers in this test require file1 and file4 to share
+# the same blocks at the end of the test. Therefore, we need the allocator to
+# give file1 a single extent at the start of the test so that fsr will not be
+# tempted to "defragment" a multi-extent file1 or file4. Defragmenting really
+# means rewriting the file, and if that succeeds on either file, we'll have
+# unshared the space and there will be too little free space. Therefore,
+# preallocate space to try to produce a single extent.
+$XFS_IO_PROG -f -c "falloc 0 $((blks * blksz))" $testdir/file1 >> $seqres.full
_pwrite_byte 0x61 0 $((blks * blksz)) $testdir/file1 >> $seqres.full
+sync
+
+nextents=$($XFS_IO_PROG -c 'stat' $testdir/file1 | grep 'fsxattr.nextents' | awk '{print $3}')
+
_cp_reflink $testdir/file1 $testdir/file2
_cp_reflink $testdir/file2 $testdir/file3
_cp_reflink $testdir/file3 $testdir/file4
c14=$(_md5_checksum $testdir/file4)
echo "Defragment"
-lsattr -l $testdir/ | _filter_scratch | _filter_spaces
+lsattr -l $testdir/ | _filter_scratch | _filter_spaces | sed -e 's/DAX/---/g'
$XFS_FSR_PROG -v -d $testdir/file1 >> $seqres.full
$XFS_FSR_PROG -v -d $testdir/file2 >> $seqres.full # fsr probably breaks the link
$XFS_FSR_PROG -v -d $testdir/file3 >> $seqres.full # fsr probably breaks the link
#echo $free_blocks0 $free_blocks1 $free_blocks2 $free_blocks3
-_within_tolerance "free blocks after creating some reflink copies" $free_blocks1 $((free_blocks0 - (blks * blksz_factor) )) $margin -v
-_within_tolerance "free blocks after CoW some reflink copies" $free_blocks2 $((free_blocks1 - 2)) $margin -v
-_within_tolerance "free blocks after defragging all reflink copies" $free_blocks3 $((free_blocks2 - (blks * 2 * blksz_factor))) $margin -v
-_within_tolerance "free blocks after all tests" $free_blocks3 $((free_blocks0 - (blks * 3 * blksz_factor))) $margin -v
+freesp_bad=0
+
+_within_tolerance "free blocks after creating some reflink copies" \
+ $free_blocks1 $((free_blocks0 - (blks * blksz_factor) )) $margin -v || freesp_bad=1
+
+_within_tolerance "free blocks after CoW some reflink copies" \
+ $free_blocks2 $((free_blocks1 - 2)) $margin -v || freesp_bad=1
+
+_within_tolerance "free blocks after defragging all reflink copies" \
+ $free_blocks3 $((free_blocks2 - (blks * 2 * blksz_factor))) $margin -v || freesp_bad=1
+
+_within_tolerance "free blocks after all tests" \
+ $free_blocks3 $((free_blocks0 - (blks * 3 * blksz_factor))) $margin -v || freesp_bad=1
+
+if [ $freesp_bad -ne 0 ] && [ $nextents -gt 1 ]; then
+ echo "free space checks probably failed because file1 nextents was $nextents"
+fi
# success, all done
status=0
{
cd /
_scratch_unmount > /dev/null 2>&1
- rm -rf $tmp.* $testdir $metadump_file $TEST_DIR/image
+ _xfs_cleanup_verify_metadump
+ rm -rf $tmp.* $testdir
}
# Import common functions.
. ./common/filter
. ./common/reflink
+. ./common/metadump
# real QA test starts here
_supported_fs xfs
+_require_command "$XFS_MDRESTORE_PROG" "xfs_mdrestore"
_require_loop
_require_scratch_reflink
+_xfs_setup_verify_metadump
_scratch_mkfs >/dev/null 2>&1
_scratch_mount
testdir=$SCRATCH_MNT/test-$seq
mkdir $testdir
-metadump_file=$TEST_DIR/${seq}_metadump
echo "Create the original file blocks"
blksz="$(_get_file_block_size $testdir)"
$testdir/file2 $((nr * blksz)) $blksz >> $seqres.full
done
-echo "Create metadump file"
_scratch_unmount
-_scratch_xfs_metadump $metadump_file
-# Now restore the obfuscated one back and take a look around
-echo "Restore metadump"
-xfs_mdrestore $metadump_file $TEST_DIR/image
-SCRATCH_DEV=$TEST_DIR/image _scratch_mount
-SCRATCH_DEV=$TEST_DIR/image _scratch_unmount
-
-echo "Check restored fs"
-_check_generic_filesystem $metadump_file
+echo "Create metadump file, restore it and check restored fs"
+_xfs_verify_metadumps
# success, all done
status=0
QA output created by 129
Create the original file blocks
Reflink every other block
-Create metadump file
-Restore metadump
-Check restored fs
+Create metadump file, restore it and check restored fs
(echo "sb 0"; sleep 0.5;
echo "p magicnum"; sleep 0.5;
echo "source $tmp.a"; sleep 0.5;
- echo "p magicnum"; sleep 0.5) | _scratch_xfs_db 2>&1 | sed -e 's/xfs_db> //g' -e 's/0x58465342/XFS_MAGIC/g' | egrep '(This is file|magicnum =)'
+ echo "p magicnum"; sleep 0.5) | _scratch_xfs_db 2>&1 | sed -e 's/xfs_db> //g' -e 's/0x58465342/XFS_MAGIC/g' | grep -E '(This is file|magicnum =)'
# success, all done
status=0
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 144
+#
+# Now that we've increased the default log size calculation, test mkfs with
+# various stripe units and filesystem sizes to see if we can provoke mkfs into
+# breaking.
+#
+. ./common/preamble
+_begin_fstest auto mkfs
+
+# real QA test starts here
+
+# Modify as appropriate.
+_supported_fs xfs
+_require_test
+
+# The last testcase creates a (sparse) fs image with a 2GB log, so we need
+# 3GB to avoid failing the mkfs due to ENOSPC.
+_require_fs_space $TEST_DIR $((3 * 1048576))
+echo Silence is golden
+
+testfile=$TEST_DIR/a
+rm -f $testfile
+
+test_format() {
+ local tag="$1"
+ shift
+
+ echo "$tag" >> $seqres.full
+ $MKFS_XFS_PROG -f $@ -d file,name=$testfile &>> $seqres.full
+ local res=$?
+ test $res -eq 0 || echo "$tag FAIL $res" | tee -a $seqres.full
+}
+
+# First we try various small filesystems and stripe sizes.
+for M in `seq 298 302` `seq 490 520`; do
+ for S in `seq 32 4 64`; do
+ test_format "M=$M S=$S" -dsu=${S}k,sw=1,size=${M}m -N
+ done
+done
+
+# log end rounded beyond EOAG due to stripe unit
+test_format "log end beyond eoag" -d agcount=3200,size=6366g -d su=256k,sw=4 -N
+
+# Log so large it pushes the root dir into AG 1. We can't use -N for the mkfs
+# because this check only occurs after the root directory has been allocated,
+# which mkfs -N doesn't do.
+test_format "log pushes rootdir into AG 1" -d agcount=3200,size=6366g -lagnum=0
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 144
+Silence is golden
# real QA test starts here
_supported_fs xfs
+_fixed_by_kernel_commit 1aecf3734a95 \
+ "xfs: fix chown leaking delalloc quota blocks when fssetxattr fails"
+
_require_command "$FILEFRAG_PROG" filefrag
_require_test_program "chprojid_fail"
_require_quota
# error and returns EFSCORRUPTED.
. ./common/preamble
-_begin_fstest auto quick rw realtime
+_begin_fstest auto quick rw realtime prealloc
# Import common functions.
. ./common/filter
_supported_fs xfs
_require_scratch
_require_realtime
+_require_xfs_io_command "falloc"
_require_test_program "punch-alternating"
# Format filesystem to get the block size
_scratch_mount >> $seqres.full
blksz=$(_get_block_size $SCRATCH_MNT)
-rextsize=$($XFS_INFO_PROG $SCRATCH_MNT | grep realtime.*extsz | sed -e 's/^.*extsz=\([0-9]*\).*$/\1/g')
+rextsize=$(_xfs_get_rtextsize "$SCRATCH_MNT")
rextblks=$((rextsize / blksz))
echo "blksz $blksz rextsize $rextsize rextblks $rextblks" >> $seqres.full
_scratch_mount >> $seqres.full
blksz=$(_get_block_size $SCRATCH_MNT)
-rextsize=$($XFS_INFO_PROG $SCRATCH_MNT | grep realtime.*extsz | sed -e 's/^.*extsz=\([0-9]*\).*$/\1/g')
+rextsize=$(_xfs_get_rtextsize "$SCRATCH_MNT")
rextblks=$((rextsize / blksz))
echo "blksz $blksz rextsize $rextsize rextblks $rextblks" >> $seqres.full
_scratch_mkfs > $seqres.full
_scratch_mount >> $seqres.full
-$XFS_INFO_PROG $SCRATCH_MNT | grep -q ftype=1 || \
- _notrun "filesystem does not support ftype"
+_require_xfs_has_feature "$SCRATCH_MNT" ftype
filter_ls() {
awk '
filter_quot()
{
- _filter_quota | grep -v "root \|\#0 " \
+ _filter_quota | grep -v "root \|#0 " \
| sed -e '/#[0-9]*/s/#[0-9]*/#ID/g'
}
filter_report()
{
- _filter_quota | grep -v "^root \|^\#0 " \
+ _filter_quota | grep -v "^root \|^#0 " \
| sed -e '/^#[0-9]*/s/^#[0-9]*/#ID/g'
}
_check_scratch_xfs_features NEEDSREPAIR
_try_scratch_mount &> $tmp.mount
res=$?
-_filter_scratch < $tmp.mount
+_filter_error_mount < $tmp.mount
if [ $res -eq 0 ]; then
echo "Should not be able to mount after needsrepair crash"
_scratch_unmount
QA output created by 154
FEATURES: NEEDSREPAIR:YES
-mount: SCRATCH_MNT: mount(2) system call failed: Structure needs cleaning.
+mount: Structure needs cleaning
FEATURES: NEEDSREPAIR:NO
_require_scratch_xfs_crc # needsrepair only exists for v5
_require_populate_commands
_require_libxfs_debug_flag LIBXFS_DEBUG_WRITE_CRASH
+_require_command "$TIMEOUT_PROG" timeout
+
+# Inject a 10 minute abortive timeout on the repair program so that deadlocks
+# in the program do not cause fstests to hang indefinitely.
+XFS_REPAIR_PROG="$TIMEOUT_PROG -s ABRT 10m $XFS_REPAIR_PROG"
# Populate the filesystem
_scratch_populate_cached nofill >> $seqres.full 2>&1
# but repair -n says the fs is clean, then it's possible that the
# injected error caused it to abort immediately after the write that
# cleared NEEDSREPAIR.
- if ! _check_scratch_xfs_features NEEDSREPAIR > /dev/null &&
+ if ! _check_scratch_xfs_features NEEDSREPAIR &> /dev/null &&
! _scratch_xfs_repair -n &>> $seqres.full; then
echo "NEEDSREPAIR should be set on corrupt fs"
fi
# If NEEDSREPAIR is still set on the filesystem, ensure that a full run
# cleans everything up.
-if _check_scratch_xfs_features NEEDSREPAIR > /dev/null; then
+echo "Checking filesystem one last time after $allowed_writes writes." >> $seqres.full
+if _check_scratch_xfs_features NEEDSREPAIR &> /dev/null; then
echo "Clearing NEEDSREPAIR" >> $seqres.full
_scratch_xfs_repair 2>> $seqres.full
_check_scratch_xfs_features NEEDSREPAIR > /dev/null && \
- echo "Repair failed to clear NEEDSREPAIR on the $nr_writes writes test"
+ echo "Repair failed to clear NEEDSREPAIR on the $allowed_writes writes test"
fi
# success, all done
_check_scratch_xfs_features NEEDSREPAIR INOBTCNT
_try_scratch_mount &> $tmp.mount
res=$?
-_filter_scratch < $tmp.mount
+_filter_error_mount < $tmp.mount
if [ $res -eq 0 ]; then
echo "needsrepair should have prevented mount"
_scratch_unmount
Fail partway through upgrading
Adding inode btree counts to filesystem.
FEATURES: NEEDSREPAIR:YES INOBTCNT:YES
-mount: SCRATCH_MNT: mount(2) system call failed: Structure needs cleaning.
+mount: Structure needs cleaning
Re-run repair to finish upgrade
FEATURES: NEEDSREPAIR:NO INOBTCNT:YES
Filesystem should be usable again
# ->page-mkwrite test - unwritten extents and mmap
#
. ./common/preamble
-_begin_fstest rw metadata auto quick
+_begin_fstest rw metadata auto quick prealloc
# Import common functions.
. ./common/filter
# the others are unwritten.
_filter_blocks()
{
- $AWK_PROG '
+ $AWK_PROG -v file_size=$FILE_SIZE '
/^ +[0-9]/ {
if (!written_size) {
written_size = $6
- unwritten1 = ((1048576/512) / 2) - written_size
- unwritten2 = ((1048576/512) / 2) - 2 * written_size
+ unwritten1 = ((file_size/512) / 2) - written_size
+ unwritten2 = ((file_size/512) / 2) - 2 * written_size
}
# is the extent unwritten?
TEST_FILE=$SCRATCH_MNT/test_file
TEST_PROG=$here/src/unwritten_mmap
-FILE_SIZE=1048576
+
+# Beginning with 5.18, some filesystems support creating large folios for the
+# page cache. A system with 64k pages can create 256k folios, which means
+# that with the old file size of 1M, the last half of the file is completely
+# converted from unwritten to written by page_mkwrite. The test will fail on
+# the golden output when this happens, so increase the size from the original
+# 1MB file size to at least (6 * 256k == 1.5MB) prevent this from happening.
+#
+# However, increasing the file size to around 2MB causes regressions when fsdax
+# is enabled because fsdax will try to use PMD entries for the mappings. Hence
+# we need to set the file size to (6 * 2MB == 12MB) to cover all cases.
+FILE_SIZE=$((12 * 1048576))
+
+# The xfs_bmap results in the golden output requires file allocations to align
+# to 1M boundaries.
+_require_congruent_file_oplen $SCRATCH_MNT $FILE_SIZE
rm -f $TEST_FILE
$TEST_PROG $FILE_SIZE $TEST_FILE
# unwritten extent conversion test
#
. ./common/preamble
-_begin_fstest rw metadata auto stress
+_begin_fstest rw metadata auto stress prealloc
# Override the default cleanup function.
_cleanup()
TEST_FILE=$SCRATCH_MNT/test_file
TEST_PROG=$here/src/unwritten_sync
-LOOPS=50
+LOOPS=$((5 * $TIME_FACTOR))
echo "*** test unwritten extent conversion under heavy I/O"
-
-workout
-
rm -f $TEST_FILE
+workout
$TEST_PROG $LOOPS $TEST_FILE
echo " *** test done"
# of the filesystem is now in the middle of a sparse inode cluster.
#
. ./common/preamble
-_begin_fstest auto quick shrinkfs
+_begin_fstest auto quick shrinkfs prealloc punch
# Import common functions.
. ./common/filter
_xfs_force_bdev data $SCRATCH_MNT
old_dblocks=$($XFS_IO_PROG -c 'statfs' $SCRATCH_MNT | grep geom.datablocks)
-mkdir $SCRATCH_MNT/save/
+mkdir $SCRATCH_MNT/save/ $SCRATCH_MNT/urk/
sino=$(stat -c '%i' $SCRATCH_MNT/save)
_consume_freesp()
# Modify as appropriate.
_supported_fs xfs
+_fixed_by_kernel_commit f38a032b165d "xfs: fix I_DONTCACHE"
+
_require_xfs_io_command "bulkstat"
_require_scratch
. ./common/preamble
_begin_fstest mkfs other auto
+filter_repair() {
+ _filter_repair | sed \
+ -e '/unable to verify superblock, continuing/d' \
+ -e '/found candidate secondary superblock/d' \
+ -e '/error reading superblock.*-- seek to offset/d'
+}
+
# dd the 1st sector then repair
_dd_repair_check()
{
#dd first sector
dd if=/dev/zero of=$1 bs=$2 count=1 2>&1 | _filter_dd
#xfs_repair
- _scratch_xfs_repair 2>&1 | _filter_repair
+ _scratch_xfs_repair 2>&1 | filter_repair
#check repair
if _check_scratch_fs; then
echo "repair passed"
bad primary superblock - bad magic number !!!
attempting to find secondary superblock...
-found candidate secondary superblock...
verified secondary superblock...
writing modified primary superblock
sb root inode INO inconsistent with calculated value INO
bad primary superblock - bad magic number !!!
attempting to find secondary superblock...
-found candidate secondary superblock...
verified secondary superblock...
writing modified primary superblock
sb root inode INO inconsistent with calculated value INO
_require_cp_reflink
_require_test_program "punch-alternating"
+_fixed_by_kernel_commit b25d1984aa88 \
+ "xfs: estimate post-merge refcounts correctly"
+
echo "Format and mount"
_scratch_mkfs -d agcount=1 > $seqres.full 2>&1
_scratch_mount >> $seqres.full 2>&1
+# This test modifies the refcount btree on the data device, so we must force
+# rtinherit off so that the test files are created there.
+_xfs_force_bdev data $SCRATCH_MNT
+
testdir=$SCRATCH_MNT/test-$seq
mkdir $testdir
+# Set the file size to 10x the block size to guarantee that the COW writes will
+# touch multiple blocks and exercise the refcount extent merging code. This is
+# necessary to catch a bug in the refcount extent merging code that handles
+# MAXREFCOUNT edge cases.
blksz=65536
+filesz=$((blksz * 10))
echo "Create original files"
-_pwrite_byte 0x61 0 $blksz $testdir/file1 >> $seqres.full
+_pwrite_byte 0x61 0 $filesz $testdir/file1 >> $seqres.full
_cp_reflink $testdir/file1 $testdir/file2 >> $seqres.full
echo "Change reference count"
_scratch_mount >> $seqres.full
echo "CoW a couple files"
-_pwrite_byte 0x62 0 $blksz $testdir/file3 >> $seqres.full
-_pwrite_byte 0x62 0 $blksz $testdir/file5 >> $seqres.full
+_pwrite_byte 0x62 0 $filesz $testdir/file3 >> $seqres.full
+_pwrite_byte 0x62 0 $filesz $testdir/file5 >> $seqres.full
+
+# For the last COW test, write single blocks at the start, middle, and end of
+# the shared file to exercise a refcount btree update that targets a single
+# block of the multiblock refcount record that we just modified.
+#
+# This trips a bug where XFS didn't correctly identify refcount record merge
+# candidates when any of the records are pinned at MAXREFCOUNT. The bug was
+# originally discovered by enabling fsdax + reflink, but the bug can be
+# triggered by any COW that doesn't target the entire extent.
+#
+# The bug was fixed by kernel commit b25d1984aa88 ("xfs: estimate post-merge
+# refcounts correctly")
_pwrite_byte 0x62 0 $blksz $testdir/file7 >> $seqres.full
+_pwrite_byte 0x62 $((blksz * 4)) $blksz $testdir/file7 >> $seqres.full
+_pwrite_byte 0x62 $((filesz - blksz)) $blksz $testdir/file7 >> $seqres.full
echo "Check scratch fs"
_scratch_unmount
# - Check the number of extents.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap
# Import common functions.
. ./common/filter
_require_cp_reflink
_require_xfs_io_command "fiemap"
_require_xfs_io_command "cowextsize"
+_require_no_xfs_always_cow
echo "Format and mount"
_scratch_mkfs > $seqres.full 2>&1
mkdir $testdir
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
nr=128
filesize=$((blksz * nr))
bufnr=16
# - Check the number of extents.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap
# Import common functions.
. ./common/filter
_require_xfs_io_command "fiemap"
_require_xfs_io_command "cowextsize"
_require_odirect
+_require_no_xfs_always_cow # writes have to converge to overwrites
echo "Format and mount"
_scratch_mkfs > $seqres.full 2>&1
mkdir $testdir
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
nr=128
filesize=$((blksz * nr))
bufnr=16
echo "CoW and unmount"
$XFS_IO_PROG -f -c "cowextsize" $testdir/file2 >> $seqres.full
-$XFS_IO_PROG -d -f -c "pwrite -R -S 0x63 -b $real_blksz 0 $((filesize + 1))" \
+$XFS_IO_PROG -d -f -c "pwrite -R -S 0x63 -b $real_blksz 0 $filesize" \
$testdir/file2 2>&1 >> $seqres.full | _filter_xfs_io_error
-$XFS_IO_PROG -d -f -c "pwrite -S 0x63 -b $real_blksz 0 $((filesize + 1))" \
+$XFS_IO_PROG -d -f -c "pwrite -S 0x63 -b $real_blksz 0 $filesize" \
$testdir/file2 2>&1 >> $seqres.full | _filter_xfs_io_error
_scratch_cycle_mount
2909feb63a37b0e95fe5cfb7f274f7b1 SCRATCH_MNT/test-182/file1
2909feb63a37b0e95fe5cfb7f274f7b1 SCRATCH_MNT/test-182/file2
CoW and unmount
-pwrite: Invalid argument
Compare files
2909feb63a37b0e95fe5cfb7f274f7b1 SCRATCH_MNT/test-182/file1
c6ba35da9f73ced20d7781a448cc11d4 SCRATCH_MNT/test-182/file2
# - Check the number of extents.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap unshare
# Import common functions.
. ./common/filter
# real QA test starts here
_supported_fs xfs
+_require_scratch
+_require_scratch_delalloc
_require_scratch_reflink
_require_cp_reflink
_require_xfs_io_command "fiemap"
mkdir $testdir
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
nr=128
filesize=$((blksz * nr))
bufnr=16
# smaller than the rt device.
#
. ./common/preamble
-_begin_fstest auto fsmap
+_begin_fstest auto fsmap prealloc punch
_cleanup()
{
# file_offset file_end physical_offset physical_end
rtfile_exts() {
$XFS_IO_PROG -c 'bmap -elp' $rtfile | \
- egrep -v '(^/|EXT:|hole)' | \
+ grep -E -v '(^/|EXT:|hole)' | \
tr ':.[]' ' ' | \
while read junk foff fend physoff physend junk; do
echo "$foff $fend $physoff $physend"
{
tee -a $seqres.full | \
sed -e "s/core.forkoff/forkoff/g" | \
- egrep '^u.sfdir2|^u.sfdir3|^a.sfattr|forkoff' | \
- egrep -v 'inumber|parent' | \
+ grep -E '^u.sfdir2|^u.sfdir3|^a.sfattr|forkoff' | \
+ grep -E -v 'inumber|parent' | \
sed -e s/sfdir3/sfdir2/g | \
grep -v filetype
}
# the fix patches themselves.
#
. ./common/preamble
-_begin_fstest auto quick rw realtime
+_begin_fstest auto quick rw realtime prealloc punch
# Import common functions.
. ./common/filter
#
# <---- Normal programming is resumed ---->
#
+# <---- Bbbzzzzzzztttt ---->
+#
+# < systemd enters the chat >
+#
+# xfs/189 [not run] noattr2 mount option not supported on /dev/vdc
+# xfs/190 1s ... mount: (hint) your fstab has been modified, but systemd still uses
+# the old version; use 'systemctl daemon-reload' to reload.
+# 1s
+# xfs/192 3s ... mount: (hint) your fstab has been modified, but systemd still uses
+# the old version; use 'systemctl daemon-reload' to reload.
+#
+# mount/systemd sees that /etc/fstab has changed (because mtime changed)
+# and so it whines that systemd needs updating on every mount from this point
+# onwards. Yes, that's totally obnoxious behaviour from mount/systemd but we
+# have to work around it.
+#
+# < systemd leaves the chat >
#
. ./common/preamble
_begin_fstest mount auto quick
# Example fstab entry
# /dev/sdb2 /mnt/scratch1 xfs defaults 0 0
#
+# Note that to avoid mnt/systemd whining about /etc/fstab being modified, we
+# need to ensure that it reloads it's state once we restore the fstab to
+# original.
+#
_add_scratch_fstab()
{
# comment out any existing SCRATCH_DEV
_modify_scratch_fstab()
{
- opts=$1
+ local opts=$1
# modify our fstab entry that we added
# modify opts by looking for last word which has non-space chars
# remove the one we added at the end
$SED_PROG -i "/# $tag/d" /etc/fstab
+
+ # stop mount/systemd whining that /etcfstab was changed.
+ command -v systemctl > /dev/null 2>&1 && systemctl daemon-reload
}
# Import common functions.
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 191
+#
+# Make sure that XFS can handle empty leaf xattr blocks correctly. These
+# blocks can appear in files as a result of system crashes in the middle of
+# xattr operations, which means that we /must/ handle them gracefully.
+# Check that read and write verifiers won't trip, that the get/list/setxattr
+# operations don't stumble over them, and that xfs_repair will offer to remove
+# the entire xattr fork if the root xattr leaf block is empty.
+#
+# Regression test for
+# kernel commit:
+# 7be3bd8856fb ("xfs: empty xattr leaf header blocks are not corruption")
+# xfsprogs commit:
+# f50d3462c654 ("xfs_repair: ignore empty xattr leaf blocks")
+#
+. ./common/preamble
+_begin_fstest auto quick attr
+
+# Import common functions.
+. ./common/filter
+. ./common/attr
+
+# real QA test starts here
+
+_supported_fs xfs
+_require_scratch
+_require_scratch_xfs_crc # V4 is deprecated
+_fixed_by_kernel_commit 7be3bd8856fb "xfs: empty xattr leaf header blocks are not corruption"
+_fixed_by_kernel_commit e87021a2bc10 "xfs: use larger in-core attr firstused field and detect overflow"
+_fixed_by_git_commit xfsprogs f50d3462c654 "xfs_repair: ignore empty xattr leaf blocks"
+
+_scratch_mkfs_xfs | _filter_mkfs >$seqres.full 2>$tmp.mkfs
+cat $tmp.mkfs >> $seqres.full
+source $tmp.mkfs
+_scratch_mount
+
+$XFS_IO_PROG -f -c 'pwrite -S 0x58 0 64k' $SCRATCH_MNT/largefile >> $seqres.full
+$XFS_IO_PROG -f -c "pwrite -S 0x58 0 $isize" $SCRATCH_MNT/smallfile >> $seqres.full
+
+smallfile_md5=$(_md5_checksum $SCRATCH_MNT/smallfile)
+largefile_md5=$(_md5_checksum $SCRATCH_MNT/largefile)
+
+# Try to force the creation of a single leaf block in each of three files.
+# The first one gets a local attr, the second a remote attr, and the third
+# is left for scrub and repair to find.
+touch $SCRATCH_MNT/e0
+touch $SCRATCH_MNT/e1
+touch $SCRATCH_MNT/e2
+
+$ATTR_PROG -s x $SCRATCH_MNT/e0 < $SCRATCH_MNT/smallfile >> $seqres.full
+$ATTR_PROG -s x $SCRATCH_MNT/e1 < $SCRATCH_MNT/smallfile >> $seqres.full
+$ATTR_PROG -s x $SCRATCH_MNT/e2 < $SCRATCH_MNT/smallfile >> $seqres.full
+
+e0_ino=$(stat -c '%i' $SCRATCH_MNT/e0)
+e1_ino=$(stat -c '%i' $SCRATCH_MNT/e1)
+e2_ino=$(stat -c '%i' $SCRATCH_MNT/e2)
+
+_scratch_unmount
+
+# We used to think that it wasn't possible for empty xattr leaf blocks to
+# exist, but it turns out that setting a large xattr on a file that has no
+# xattrs can race with a log flush and crash, which results in an empty
+# leaf block being logged and recovered. This is rather hard to trip, so we
+# use xfs_db to turn a regular leaf block into an empty one.
+make_empty_leaf() {
+ local inum="$1"
+
+ echo "editing inode $inum" >> $seqres.full
+
+ magic=$(_scratch_xfs_get_metadata_field hdr.info.hdr.magic "inode $inum" "ablock 0")
+ if [ "$magic" != "0x3bee" ]; then
+ _scratch_xfs_db -x -c "inode $inum" -c "ablock 0" -c print >> $seqres.full
+ _fail "inode $inum ablock 0 is not a leaf block?"
+ fi
+
+ base=$(_scratch_xfs_get_metadata_field "hdr.freemap[0].base" "inode $inum" "ablock 0")
+
+ # 64k dbsize is a special case since it overflows the 16 bit firstused
+ # field and it needs to be set to the special XFS_ATTR3_LEAF_NULLOFF (0)
+ # value to indicate a null leaf.
+ if [ $dbsize -eq 65536 ]; then
+ firstused=0
+ else
+ firstused=$dbsize
+ fi
+
+ _scratch_xfs_db -x -c "inode $inum" -c "ablock 0" \
+ -c "write -d hdr.count 0" \
+ -c "write -d hdr.usedbytes 0" \
+ -c "write -d hdr.firstused $firstused" \
+ -c "write -d hdr.freemap[0].size $((dbsize - base))" \
+ -c print >> $seqres.full
+}
+
+make_empty_leaf $e0_ino
+make_empty_leaf $e1_ino
+make_empty_leaf $e2_ino
+
+_scratch_mount
+
+# Check that listxattr/getxattr/removexattr do nothing.
+$ATTR_PROG -l $SCRATCH_MNT/e0 2>&1 | _filter_scratch
+$ATTR_PROG -g x $SCRATCH_MNT/e0 2>&1 | _filter_scratch
+$ATTR_PROG -r x $SCRATCH_MNT/e0 2>&1 | _filter_scratch
+
+# Add a small attr to e0
+$ATTR_PROG -s x $SCRATCH_MNT/e0 < $SCRATCH_MNT/smallfile > /dev/null
+$ATTR_PROG -l $SCRATCH_MNT/e0 2>&1 | sed -e 's/\([0-9]*\) byte/XXX byte/g' | _filter_scratch
+small_md5="$($GETFATTR_PROG -n user.x --absolute-names --only-values $SCRATCH_MNT/e0 | _md5_checksum)"
+test "$small_md5" = "$smallfile_md5" || \
+ echo "smallfile $smallfile_md5 does not match small attr $small_md5"
+
+# Add a large attr to e1
+$ATTR_PROG -s x $SCRATCH_MNT/e1 < $SCRATCH_MNT/largefile > /dev/null
+$ATTR_PROG -l $SCRATCH_MNT/e1 2>&1 | _filter_scratch
+large_md5="$($GETFATTR_PROG -n user.x --absolute-names --only-values $SCRATCH_MNT/e1 | _md5_checksum)"
+test "$large_md5" = "$largefile_md5" || \
+ echo "largefile $largefile_md5 does not match large attr $large_md5"
+
+
+# Leave e2 to try to trip the repair tools, since xfs_repair used to flag
+# empty leaf blocks incorrectly too.
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 191
+attr_get: No data available
+Could not get "x" for SCRATCH_MNT/e0
+attr_remove: No data available
+Could not remove "x" for SCRATCH_MNT/e0
+Attribute "x" has a XXX byte value for SCRATCH_MNT/e0
+Attribute "x" has a 65536 byte value for SCRATCH_MNT/e1
# - Check the number of extents.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap unshare
# Import common functions.
. ./common/filter
# real QA test starts here
_supported_fs xfs
+_require_scratch
+_require_scratch_delalloc
_require_scratch_reflink
_require_cp_reflink
_require_xfs_io_command "fiemap"
_require_xfs_io_command "cowextsize"
_require_xfs_io_command "funshare"
_require_odirect
+_require_no_xfs_always_cow # writes have to converge to overwrites
echo "Format and mount"
_scratch_mkfs > $seqres.full 2>&1
mkdir $testdir
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
nr=128
filesize=$((blksz * nr))
bufnr=16
# - Check the number of extents.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap
# Import common functions.
. ./common/filter
mkdir $testdir
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
nr=128
filesize=$((blksz * nr))
bufnr=16
# - Check the number of extents.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap
# Import common functions.
. ./common/filter
_require_xfs_io_command "fiemap"
_require_xfs_io_command "cowextsize"
_require_odirect
+_require_no_xfs_always_cow # writes have to converge to overwrites
echo "Format and mount"
_scratch_mkfs > $seqres.full 2>&1
mkdir $testdir
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
nr=128
filesize=$((blksz * nr))
bufnr=16
# - Check the number of extents.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap unshare
# Import common functions.
. ./common/filter
# real QA test starts here
_supported_fs xfs
+_require_scratch
+_require_scratch_delalloc
_require_scratch_reflink
_require_cp_reflink
_require_xfs_io_command "fiemap"
mkdir $testdir
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
nr=128
filesize=$((blksz * nr))
bufnr=16
_scratch_mkfs > /dev/null 2>&1
_scratch_mount > /dev/null 2>&1
+# The xfs_bmap results in the golden output requires file allocations to align
+# to 64k boundaries.
+_require_congruent_file_oplen $SCRATCH_MNT 65536
+
for i in 10 14 15 16 17 28 29 30 31; do
rm -f $SCRATCH_MNT/hole_file
_write_holes $SCRATCH_MNT/hole_file${i} ${i}
# - Check the number of extents.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap unshare
# Import common functions.
. ./common/filter
# real QA test starts here
_supported_fs xfs
+_require_scratch
+_require_scratch_delalloc
_require_scratch_reflink
_require_cp_reflink
_require_xfs_io_command "fiemap"
_require_xfs_io_command "cowextsize"
_require_xfs_io_command "funshare"
_require_odirect
+_require_no_xfs_always_cow # writes have to converge to overwrites
echo "Format and mount"
_scratch_mkfs > $seqres.full 2>&1
mkdir $testdir
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
nr=128
filesize=$((blksz * nr))
bufnr=16
# - Ensure that whatever we set we get back later.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap
# Import common functions.
. ./common/filter
# - Repeat, but with extsz = 1MB and cowextsz = $blocksize.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap
# Import common functions.
. ./common/filter
_require_cp_reflink
_require_xfs_io_command "fiemap"
_require_xfs_io_command "cowextsize"
+_require_no_xfs_always_cow
echo "Format and mount"
_scratch_mkfs > $seqres.full 2>&1
mkdir $testdir
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
nr=128
filesize=$((blksz * nr))
bufnr=16
# Make sure setting cowextsz on a directory propagates it to subfiles.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap
# Import common functions.
. ./common/filter
echo "Format and mount"
_scratch_mkfs > $seqres.full 2>&1
_scratch_mount >> $seqres.full 2>&1
+_require_congruent_file_oplen $SCRATCH_MNT 1048576
testdir=$SCRATCH_MNT/test-$seq
mkdir $testdir
# otherwise.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap
# Import common functions.
. ./common/filter
echo "Format and mount"
_scratch_mkfs > $seqres.full 2>&1
_scratch_mount >> $seqres.full 2>&1
+_require_congruent_file_oplen $SCRATCH_MNT 65536
testdir=$SCRATCH_MNT/test-$seq
mkdir $testdir
# - Check the number of extents.
#
. ./common/preamble
-_begin_fstest clone_stress
+_begin_fstest clone_stress fiemap
# Import common functions.
. ./common/filter
_require_xfs_io_command "fiemap"
_require_xfs_io_command "cowextsize"
_require_odirect
+_require_no_xfs_always_cow # writes have to converge to overwrites
echo "Format and mount"
_scratch_mkfs > $seqres.full 2>&1
mkdir $testdir
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
nr=50000
filesize=$((blksz * nr))
bufnr=16
# - Crash the FS to test recovery.
#
. ./common/preamble
-_begin_fstest shutdown auto quick clone
+_begin_fstest shutdown auto quick clone fiemap
# Import common functions.
. ./common/filter
mkdir $testdir
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
nr=16
filesize=$((blksz * nr))
bufnr=2
# play with cowextsz.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap
# Import common functions.
. ./common/filter
# with cowextsz.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap
# Import common functions.
. ./common/filter
# - Check that the files are now different where we say they're different.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
# Import common functions.
. ./common/filter
echo "Create the original files"
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
nr=64
filesize=$((blksz * nr))
real_blksz=$(_get_block_size $testdir)
# - Check that the files are now different where we say they're different.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
# Import common functions.
. ./common/filter
echo "Create the original files"
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
nr=64
filesize=$((blksz * nr))
real_blksz=$(_get_block_size $testdir)
# - Check that the files are now different where we say they're different.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
# Import common functions.
. ./common/filter
echo "Create the original files"
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
nr=64
filesize=$((blksz * nr))
real_blksz=$(_get_block_size $testdir)
# - Check that the files are now different where we say they're different.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
# Import common functions.
. ./common/filter
echo "Create the original files"
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
nr=64
filesize=$((blksz * nr))
real_blksz=$(_get_block_size $testdir)
# - Check that the files are now different where we say they're different.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
# Import common functions.
. ./common/filter
echo "Create the original files"
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
nr=64
filesize=$((blksz * nr))
real_blksz=$(_get_block_size $testdir)
# - Check that the files are now different where we say they're different.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
# Import common functions.
. ./common/filter
echo "Create the original files"
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
nr=64
filesize=$((blksz * nr))
real_blksz=$(_get_block_size $testdir)
# - Check that the files are now different where we say they're different.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
# Import common functions.
. ./common/filter
echo "Create the original files"
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
nr=64
filesize=$((blksz * nr))
real_blksz=$(_get_block_size $testdir)
# - Check that the files are now different where we say they're different.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
# Import common functions.
. ./common/filter
echo "Create the original files"
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
nr=64
filesize=$((blksz * nr))
real_blksz=$(_get_block_size $testdir)
# - Check that the files are now different where we say they're different.
#
. ./common/preamble
-_begin_fstest auto quick clone punch
+_begin_fstest auto quick clone punch prealloc
# Import common functions.
. ./common/filter
echo "Create the original files"
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
nr=64
filesize=$((blksz * nr))
real_blksz=$(_get_block_size $testdir)
TDIR="${TEST_DIR}/t_holes"
NFILES="10"
EXTSIZE="256k"
-_xfs_force_bdev data $TEST_DIR
# Create the test directory
mkdir ${TDIR}
+# Per-directory extent size hints aren't particularly useful for files that
+# are created on the realtime section. Force the test file to be created on
+# the data directory. Do not change the rtinherit flag on $TEST_DIR because
+# that will affect other tests.
+_xfs_force_bdev data $TDIR
+
# Set the test directory extsize
$XFS_IO_PROG -c "extsize ${EXTSIZE}" ${TDIR}
# - Check that the files are now different where we say they're different.
#
. ./common/preamble
-_begin_fstest auto quick clone punch
+_begin_fstest auto quick clone punch prealloc
# Import common functions.
. ./common/filter
echo "Create the original files"
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
nr=64
filesize=$((blksz * nr))
real_blksz=$(_get_block_size $testdir)
# - Write more and see how bad fragmentation is.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap prealloc
# Override the default cleanup function.
_cleanup()
mkdir $testdir
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
nr=64
filesize=$((blksz * nr))
bufnr=2
# - Write more and see how bad fragmentation is.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap prealloc
# Override the default cleanup function.
_cleanup()
# real QA test starts here
_supported_fs xfs
+_require_scratch
+_require_scratch_delalloc
_require_xfs_io_command "cowextsize"
_require_scratch_reflink
_require_cp_reflink
mkdir $testdir
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
nr=64
filesize=$((blksz * nr))
bufnr=2
_cleanup()
{
cd /
- _scratch_unmount > /dev/null 2>&1
- rm -rf $tmp.* $metadump_file $TEST_DIR/image
+ _xfs_cleanup_verify_metadump
+ rm -rf $tmp.* $testdir
}
# Import common functions.
. ./common/filter
+. ./common/metadump
# real QA test starts here
_supported_fs xfs
+_require_command "$XFS_MDRESTORE_PROG" "xfs_mdrestore"
_require_loop
_require_xfs_scratch_rmapbt
_require_xfs_io_command "fpunch"
+_xfs_setup_verify_metadump
_scratch_mkfs >/dev/null 2>&1
_scratch_mount
testdir=$SCRATCH_MNT/test-$seq
mkdir $testdir
-metadump_file=$TEST_DIR/${seq}_metadump
echo "Create the original file blocks"
blksz="$(_get_block_size $testdir)"
$XFS_IO_PROG -c "fpunch $((nr * blksz)) $blksz" $testdir/file1 >> $seqres.full
done
-echo "Create metadump file"
_scratch_unmount
-_scratch_xfs_metadump $metadump_file
-# Now restore the obfuscated one back and take a look around
-echo "Restore metadump"
-xfs_mdrestore $metadump_file $TEST_DIR/image
-SCRATCH_DEV=$TEST_DIR/image _scratch_mount
-SCRATCH_DEV=$TEST_DIR/image _scratch_unmount
-
-echo "Check restored fs"
-_check_generic_filesystem $metadump_file
+echo "Create metadump file, restore it and check restored fs"
+_xfs_verify_metadumps
# success, all done
status=0
QA output created by 234
Create the original file blocks
Punch every other block
-Create metadump file
-Restore metadump
-Check restored fs
+Create metadump file, restore it and check restored fs
alignment=`_min_dio_alignment $TEST_DEV`
_require_fs_space $SCRATCH_MNT $((filesize / 1024 * 3 * 5 / 4))
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
echo "Create the original files"
$XFS_IO_PROG -c "cowextsize $((bufsize * 2))" $testdir
mkdir $testdir
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
nr=640
bufnr=128
filesize=$((blksz * nr))
mkdir $testdir
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
nr=640
bufnr=128
filesize=$((blksz * nr))
mkdir $testdir
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
nr=640
bufnr=128
filesize=$((blksz * nr))
# - Check that the files are now different where we say they're different.
#
. ./common/preamble
-_begin_fstest auto quick clone punch
+_begin_fstest auto quick clone punch prealloc
# Import common functions.
. ./common/filter
_supported_fs xfs
_require_xfs_debug
_require_scratch_reflink
+_require_scratch_delalloc
_require_xfs_io_command "falloc"
_require_xfs_io_command "cowextsize"
_require_xfs_io_command "fpunch"
test $(_xfs_bmapx_find cow $testdir/file3 delalloc) -gt 0 || \
echo "Expected to find a delalloc CoW extent"
echo "Shared data extents:"
-test $(_xfs_bmapx_find data $testdir/file3 '100000$') -gt 0 || \
+test $(_xfs_bmapx_find data $testdir/file3 -E '10[01]{4}$') -gt 0 || \
echo "Expected to find a shared data extent"
echo "Unwritten data extents:"
-test $(_xfs_bmapx_find data $testdir/file3 '10000$') -gt 0 || \
+test $(_xfs_bmapx_find data $testdir/file3 -E '1[01]{4}$') -gt 0 || \
echo "Expected to find an unwritten data extent"
echo "Hole data extents:"
test $(_xfs_bmapx_find data $testdir/file3 hole) -gt 0 || \
echo "Expected to find a hole data extent"
echo "Regular data extents:"
-test $(_xfs_bmapx_find data $testdir/file3 '000000$') -gt 0 || \
+test $(_xfs_bmapx_find data $testdir/file3 -E '00[01]{4}$') -gt 0 || \
echo "Expected to find a regular data extent"
sync
test $(_xfs_bmapx_find cow $testdir/file3 delalloc ) -eq 0 || \
echo "Expected to find zero delalloc CoW extent"
echo "Shared data extents:"
-test $(_xfs_bmapx_find data $testdir/file3 '100000$') -gt 0 || \
+test $(_xfs_bmapx_find data $testdir/file3 -E '10[01]{4}$') -gt 0 || \
echo "Expected to find a shared data extent"
echo "Unwritten data extents:"
-test $(_xfs_bmapx_find data $testdir/file3 '10000$') -gt 0 || \
+test $(_xfs_bmapx_find data $testdir/file3 -E '1[01]{4}$') -gt 0 || \
echo "Expected to find an unwritten data extent"
echo "Hole data extents:"
test $(_xfs_bmapx_find data $testdir/file3 hole) -gt 0 || \
echo "Expected to find a hole data extent"
echo "Regular data extents:"
-test $(_xfs_bmapx_find data $testdir/file3 '000000$') -gt 0 || \
+test $(_xfs_bmapx_find data $testdir/file3 -E '00[01]{4}$') -gt 0 || \
echo "Expected to find a regular data extent"
_scratch_cycle_mount
# - compare file[12]
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
# Import common functions.
. ./common/filter
md5sum $testdir/file2 | _filter_scratch
echo "Unwritten data extents"
-test $(_xfs_bmapx_find data $testdir/file1 '10000$') -gt 0 || \
+test $(_xfs_bmapx_find data $testdir/file1 -E '1[01]{4}$') -gt 0 || \
echo "Expected to find an unwritten file1 extent"
echo "Shared data extents"
-test $(_xfs_bmapx_find data $testdir/file1 '100000$') -gt 0 || \
+test $(_xfs_bmapx_find data $testdir/file1 -E '10[01]{4}$') -gt 0 || \
echo "Expected to find a shared data extent"
echo "Hole data extents"
test $(_xfs_bmapx_find data $testdir/file2 'hole') -gt 0 || \
echo "Expected to find a hole data extent"
echo "Shared data extents"
-test $(_xfs_bmapx_find data $testdir/file2 '100000$') -gt 0 || \
+test $(_xfs_bmapx_find data $testdir/file2 -E '10[01]{4}$') -gt 0 || \
echo "Expected to find a shared data extent"
echo "Hole cow extents"
# - Check that the files are now different where we say they're different.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
# Import common functions.
. ./common/filter
echo "Create the original files"
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
nr=64
filesize=$((blksz * nr))
$XFS_IO_PROG -c "cowextsize $((blksz * 16))" $testdir >> $seqres.full
# - Check that the files are now different where we say they're different.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
# Import common functions.
. ./common/filter
echo "Create the original files"
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
nr=64
filesize=$((blksz * nr))
$XFS_IO_PROG -c "cowextsize $((blksz * 16))" $testdir >> $seqres.full
# - Check that the files are now different where we say they're different.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
# Import common functions.
. ./common/filter
echo "Create the original files"
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
nr=64
filesize=$((blksz * nr))
$XFS_IO_PROG -c "cowextsize $((blksz * 16))" $testdir >> $seqres.full
# Test fallocate hole punching
#
. ./common/preamble
-_begin_fstest auto quick prealloc punch
+_begin_fstest auto quick prealloc punch fiemap
# Import common functions.
. ./common/filter
cd /
rm -f $tmp.*
rm -rf "${OUTPUT_DIR}"
- rm -f "${METADUMP_FILE}"
+ _xfs_cleanup_verify_metadump
}
# Import common functions.
. ./common/filter
+. ./common/metadump
+_require_command "$XFS_MDRESTORE_PROG" "xfs_mdrestore"
_require_test
_require_scratch
+_xfs_setup_verify_metadump
# real QA test starts here
OUTPUT_DIR="${SCRATCH_MNT}/test_${seq}"
-METADUMP_FILE="${TEST_DIR}/${seq}_metadump"
ORPHANAGE="lost+found"
_supported_fs xfs
touch $(printf "$@")
}
+extra_test() {
+ cd "${SCRATCH_MNT}"
+
+ # Get a listing of all the files after obfuscation
+ echo "Metadump v1" >> $seqres.full
+ ls -R >> $seqres.full
+ ls -R | od -c >> $seqres.full
+
+ cd /
+}
+
echo "Disciplyne of silence is goed."
_scratch_mkfs >/dev/null 2>&1
_scratch_mount
-# Initialize and mount the scratch filesystem, then create a bunch
-# of files that exercise the original problem.
+# Initialize and mount the scratch filesystem, then create a bunch of
+# files that exercise the original problem.
#
# The problem arose when a file name produced a hash that contained
-# either 0x00 (string terminator) or 0x27 ('/' character) in a
-# spot used to determine a character in an obfuscated name. This
-# occurred in one of 5 spots at the end of the name, at position
-# (last-4), (last-3), (last-2), (last-1), or (last).
-
-rm -f "${METADUMP_FILE}"
+# either 0x00 (string terminator) or 0x27 ('/' character) in a spot used
+# to determine a character in an obfuscated name. This occurred in one
+# of 5 spots at the end of the name, at position (last-4), (last-3),
+# (last-2), (last-1), or (last).
mkdir -p "${OUTPUT_DIR}"
create_file 'pqrstu' # hash 0x1e5cf9f2 (6-byte name)
create_file 'abcdefghijklmnopqrstuvwxyz' # a most remarkable word (0x55004ae3)
-# Create a short directory name; it won't be obfuscated. Populate
-# it with some longer named-files. The first part of the obfuscated
+# Create a short directory name; it won't be obfuscated. Populate it
+# with some longer named-files. The first part of the obfuscated
# filenames should use printable characters.
mkdir foo
create_file 'foo/longer_file_name_1' # hash 0xe83634ec
cd $here
_scratch_unmount
-_scratch_xfs_metadump $METADUMP_FILE
-# Now restore the obfuscated one back and take a look around
-xfs_mdrestore "${METADUMP_FILE}" "${SCRATCH_DEV}"
-
-_scratch_mount
-
-# Get a listing of all the files after obfuscation
-cd ${SCRATCH_MNT}
-ls -R >> $seqres.full
-ls -R | od -c >> $seqres.full
+_xfs_verify_metadumps '' extra_test
# Finally, re-make the filesystem since to ensure we don't
# leave a directory with duplicate entries lying around.
cd /
-_scratch_unmount
_scratch_mkfs >/dev/null 2>&1
# all done
# - Check that the files are now different where we say they're different.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
# Import common functions.
. ./common/filter
echo "Create the original files"
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
nr=64
filesize=$((blksz * nr))
$XFS_IO_PROG -c "cowextsize $((blksz * 16))" $testdir >> $seqres.full
# - Check that the files are now different where we say they're different.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
# Import common functions.
. ./common/filter
echo "Create the original files"
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
nr=64
filesize=$((blksz * nr))
$XFS_IO_PROG -c "cowextsize $((blksz * 16))" $testdir >> $seqres.full
# - Check that the files are now different where we say they're different.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
# Import common functions.
. ./common/filter
echo "Create the original files"
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
nr=64
filesize=$((blksz * nr))
$XFS_IO_PROG -c "cowextsize $((blksz * 16))" $testdir >> $seqres.full
# - Check that the files are now different where we say they're different.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
# Import common functions.
. ./common/filter
echo "Create the original files"
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
nr=64
filesize=$((blksz * nr))
$XFS_IO_PROG -c "cowextsize $((blksz * 16))" $testdir >> $seqres.full
# - Check that the files are now different where we say they're different.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
# Import common functions.
. ./common/filter
echo "Create the original files"
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
nr=64
filesize=$((blksz * nr))
$XFS_IO_PROG -c "cowextsize $((blksz * 16))" $testdir >> $seqres.full
# executable and libraries!) to see what happens.
#
. ./common/preamble
-_begin_fstest dangerous_fuzzers dangerous_scrub dangerous_online_repair
+_begin_fstest fuzzers scrub online_repair
_register_cleanup "_cleanup" BUS
echo "Format and populate"
_scratch_mkfs > "$seqres.full" 2>&1
_scratch_mount
+
+_require_scratch_xfs_scrub
+
cp $XFS_SCRUB_PROG $SCRATCH_MNT/xfs_scrub
$LDD_PROG $XFS_SCRUB_PROG | sed -e '/\//!d;/linux-gate/d;/=>/ {s/.*=>[[:blank:]]*\([^[:blank:]]*\).*/\1/};s/[[:blank:]]*\([^[:blank:]]*\) (.*)/\1/' | while read lib; do
cp $lib $SCRATCH_MNT/
}
echo "==== NO CRC ===="
-# Control size to control inode numbers
-_scratch_mkfs_xfs "-m crc=0 -n ftype=0 -d size=512m" >> $seqres.full
+_scratch_mkfs_xfs "-m crc=0 -n ftype=0" >> $seqres.full
test_all_state
echo "==== CRC ===="
-_scratch_mkfs_xfs "-m crc=1 -d size=512m" >>$seqres.full
+_scratch_mkfs_xfs "-m crc=1" >>$seqres.full
test_all_state
status=0
=== Test EIO/max_retries ===
error/fail_at_unmount=1
error/metadata/default/max_retries=-1
-error/metadata/default/retry_timeout_seconds=0
+error/metadata/default/retry_timeout_seconds=-1
error/metadata/EIO/max_retries=-1
-error/metadata/EIO/retry_timeout_seconds=0
+error/metadata/EIO/retry_timeout_seconds=-1
error/metadata/ENOSPC/max_retries=-1
-error/metadata/ENOSPC/retry_timeout_seconds=0
+error/metadata/ENOSPC/retry_timeout_seconds=-1
error/fail_at_unmount=0
error/metadata/EIO/max_retries=1
=== Test EIO/retry_timeout_seconds ===
error/fail_at_unmount=1
error/metadata/default/max_retries=-1
-error/metadata/default/retry_timeout_seconds=0
+error/metadata/default/retry_timeout_seconds=-1
error/metadata/EIO/max_retries=-1
-error/metadata/EIO/retry_timeout_seconds=0
+error/metadata/EIO/retry_timeout_seconds=-1
error/metadata/ENOSPC/max_retries=-1
-error/metadata/ENOSPC/retry_timeout_seconds=0
+error/metadata/ENOSPC/retry_timeout_seconds=-1
error/fail_at_unmount=0
error/metadata/EIO/retry_timeout_seconds=1
# real QA test starts here
_supported_fs xfs
+_fixed_by_kernel_commit 74ad4693b647 \
+ "xfs: fix log recovery when unknown rocompat bits are set"
# skip fs check because superblock contains unknown ro-compat features
_require_scratch_nocheck
# Only V5 XFS disallow rw mount/remount with unknown ro-compat features
_require_scratch_xfs_crc
-
-_scratch_mkfs_xfs >>$seqres.full 2>&1
+_require_scratch_shutdown
# set the highest bit of features_ro_compat, use it as an unknown
# feature bit. If one day this bit become known feature, please
# change this case.
-_scratch_xfs_set_metadata_field "features_ro_compat" "$((2**31))" "sb 0" | \
- grep 'features_ro_compat'
+set_bad_rocompat() {
+ ro_compat=$(_scratch_xfs_get_metadata_field "features_ro_compat" "sb 0")
+ echo $ro_compat | grep -q -E '^0x[[:xdigit:]]+$'
+ if [[ $? != 0 ]]; then
+ echo ":$ro_compat:"
+ echo "features_ro_compat has an invalid value."
+ return 1
+ fi
+
+ ro_compat=$(echo $ro_compat | \
+ awk '/^0x[[:xdigit:]]+/ {
+ printf("0x%x\n", or(strtonum($1), 0x80000000))
+ }')
+
+ # write the new ro compat field to the superblock
+ _scratch_xfs_set_metadata_field "features_ro_compat" "$ro_compat" "sb 0" \
+ > $seqres.full 2>&1
+
+ # read the newly set ro compat filed for verification
+ new_ro_compat=$(_scratch_xfs_get_metadata_field "features_ro_compat" "sb 0" \
+ 2>/dev/null)
+
+ # verify the new ro_compat field is correct. Without xfsprogs commit
+ # f4afdcb0ad ("xfs_db: clean up the salvage read callsites in set_cur()"),
+ # we can't get new_ro_compat value.
+ if [ "$new_ro_compat" != "$ro_compat" ]; then
+ echo "Unable to set new features_ro_compat. Wanted $ro_compat, got $new_ro_compat"
+ return 1
+ fi
+ return 0
+}
+
+# Once with a clean filesystem...
+_scratch_mkfs_xfs >>$seqres.full 2>&1
+_scratch_mount
+echo moo > $SCRATCH_MNT/testfile
+_scratch_unmount
+set_bad_rocompat
# rw mount with unknown ro-compat feature should fail
echo "rw mount test"
_fail "ro mount test failed"
else
# no hang/panic is fine
+ cat $SCRATCH_MNT/testfile > /dev/null
$FSSTRESS_PROG -d $SCRATCH_MNT -p 4 -n 400 >>$seqres.full 2>&1
fi
_scratch_unmount
+# And again with a dirty filesystem...
+_scratch_mkfs_xfs >>$seqres.full 2>&1
+_scratch_mount
+echo moo > $SCRATCH_MNT/testfile
+$XFS_IO_PROG -x -c 'shutdown -f' "${SCRATCH_MNT}"
+_scratch_unmount
+set_bad_rocompat
+
+# rw mount with unknown ro-compat feature should fail
+echo "rw mount test"
+_try_scratch_mount 2>>$seqres.full
+if [ $? -eq 0 ]; then
+ _fail "rw mount test failed"
+fi
+
+# ro mount should succeed even with log recovery
+echo "ro mount test"
+_try_scratch_mount -o ro 2>>$seqres.full
+if [ $? -ne 0 ]; then
+ _fail "ro mount test failed"
+fi
+cat $SCRATCH_MNT/testfile > /dev/null
+
# success, all done
status=0
exit
QA output created by 270
-features_ro_compat = 0x80000000
rw mount test
ro mount test
rw remount test
+rw mount test
+ro mount test
# same owner (per-AG metadata) for rmap btree blocks and blocks on the AGFL and
# the reverse mapping index merges records, the number of per-AG extents
# reported will vary depending on whether the refcount btree is enabled.
-$XFS_INFO_PROG $SCRATCH_MNT | grep -q reflink=1
+_require_xfs_has_feature "$SCRATCH_MNT" reflink
has_reflink=$(( 1 - $? ))
perag_metadata_exts=2
test $has_reflink -gt 0 && perag_metadata_exts=$((perag_metadata_exts + 1))
echo "Check bmap and fsmap" | tee -a $seqres.full
cat $TEST_DIR/bmap | while read ext offrange colon blockrange ag agrange total crap; do
- qstr="^[[:space:]]*[0-9]*:[[:space:]]*[0-9]*:[0-9]*[[:space:]]*${blockrange} :[[:space:]]*${ino}[[:space:]]*${offrange}[[:space:]]*${ag}[[:space:]]*${agrange}[[:space:]]*${total}$"
+ qstr="^[[:space:]]*[0-9]*:[[:space:]]*[0-9]*:[0-9]*[[:space:]]*${blockrange} :[[:space:]]*${ino}[[:space:]]*${offrange}[[:space:]]*${ag}[[:space:]]*${agrange}[[:space:]]*${total}(| [01]*)$"
echo "${qstr}" >> $seqres.full
grep "${qstr}" $TEST_DIR/fsmap >> $seqres.full
- found=$(grep -c "${qstr}" $TEST_DIR/fsmap)
+ found=$(grep -E -c "${qstr}" $TEST_DIR/fsmap)
test $found -eq 1 || echo "Unexpected output for offset ${offrange}."
done
echo "Check f1 bmap and fsmap" | tee -a $seqres.full
cat $TEST_DIR/bmap | while read ext offrange colon blockrange ag agrange total crap; do
- qstr="^[[:space:]]*[0-9]*:[[:space:]]*[0-9]*:[0-9]*[[:space:]]*${blockrange} :[[:space:]]*${ino}[[:space:]]*${offrange}[[:space:]]*${ag}[[:space:]]*${agrange}[[:space:]]*${total} 0100000$"
+ qstr="^[[:space:]]*[0-9]*:[[:space:]]*[0-9]*:[0-9]*[[:space:]]*${blockrange} :[[:space:]]*${ino}[[:space:]]*${offrange}[[:space:]]*${ag}[[:space:]]*${agrange}[[:space:]]*${total} 010[01]{4}$"
echo "${qstr}" >> $seqres.full
grep "${qstr}" $TEST_DIR/fsmap >> $seqres.full
- found=$(grep -c "${qstr}" $TEST_DIR/fsmap)
+ found=$(grep -E -c "${qstr}" $TEST_DIR/fsmap)
test $found -eq 1 || echo "Unexpected output for offset ${offrange}."
done
echo "Check f2 bmap and fsmap" | tee -a $seqres.full
cat $TEST_DIR/bmap | while read ext offrange colon blockrange ag agrange total crap; do
- qstr="^[[:space:]]*[0-9]*:[[:space:]]*[0-9]*:[0-9]*[[:space:]]*${blockrange} :[[:space:]]*${ino}[[:space:]]*${offrange}[[:space:]]*${ag}[[:space:]]*${agrange}[[:space:]]*${total} 0100000$"
+ qstr="^[[:space:]]*[0-9]*:[[:space:]]*[0-9]*:[0-9]*[[:space:]]*${blockrange} :[[:space:]]*${ino}[[:space:]]*${offrange}[[:space:]]*${ag}[[:space:]]*${agrange}[[:space:]]*${total} 010[01]{4}$"
echo "${qstr}" >> $seqres.full
grep "${qstr}" $TEST_DIR/fsmap >> $seqres.full
- found=$(grep -c "${qstr}" $TEST_DIR/fsmap)
+ found=$(grep -E -c "${qstr}" $TEST_DIR/fsmap)
test $found -eq 1 || echo "Unexpected output for offset ${offrange}."
done
_supported_fs xfs
_require_scsi_debug
+size=$(_small_fs_size_mb 128)
# Remove xfs signature so -f isn't needed to re-mkfs
_wipe_device()
(
echo "==================="
echo "4k physical 512b logical aligned"
-SCSI_DEBUG_DEV=`_get_scsi_debug_dev 4096 512 0 128`
+SCSI_DEBUG_DEV=`_get_scsi_debug_dev 4096 512 0 $size`
test -b "$SCSI_DEBUG_DEV" || _notrun "Could not get scsi_debug device"
# sector size should default to 4k
_check_mkfs $SCSI_DEBUG_DEV
(
echo "==================="
echo "4k physical 512b logical unaligned"
-SCSI_DEBUG_DEV=`_get_scsi_debug_dev 4096 512 1 128`
+SCSI_DEBUG_DEV=`_get_scsi_debug_dev 4096 512 1 $size`
test -b "$SCSI_DEBUG_DEV" || _notrun "Could not get scsi_debug device"
# should fail on misalignment
_check_mkfs $SCSI_DEBUG_DEV
(
echo "==================="
echo "hard 4k physical / 4k logical"
-SCSI_DEBUG_DEV=`_get_scsi_debug_dev 4096 4096 0 128`
+SCSI_DEBUG_DEV=`_get_scsi_debug_dev 4096 4096 0 $size`
test -b "$SCSI_DEBUG_DEV" || _notrun "Could not get scsi_debug device"
# block size smaller than sector size should fail
_check_mkfs -b size=2048 $SCSI_DEBUG_DEV
# Check that GETBMAPX accurately report shared extents.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
_register_cleanup "_cleanup" BUS
blocks=5
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
sz=$((blocks * blksz))
echo "Create the original files"
# real QA test starts here
_supported_fs xfs
+_require_command "$XFS_MDRESTORE_PROG" "xfs_mdrestore"
_require_xfs_copy
_require_test
_require_scratch
# xfs_metadump should refuse to dump a mounted device
_scratch_mkfs >> $seqres.full 2>&1
_scratch_mount
-_scratch_xfs_metadump $METADUMP_FILE 2>&1 | filter_mounted
+_scratch_xfs_metadump $METADUMP_FILE -a -o 2>&1 | filter_mounted
_scratch_unmount
# Test restore to a mounted device
# xfs_mdrestore should refuse to restore to a mounted device
-_scratch_xfs_metadump $METADUMP_FILE
+_scratch_xfs_metadump $METADUMP_FILE -a -o
_scratch_mount
-xfs_mdrestore $METADUMP_FILE $SCRATCH_DEV 2>&1 | filter_mounted
+_scratch_xfs_mdrestore $METADUMP_FILE 2>&1 | filter_mounted
_scratch_unmount
# Test xfs_copy to a mounted device
#
# FS QA Test No. 285
#
-# Race fio and xfs_scrub for a while to see if we crash or livelock.
+# Race fsstress and xfs_scrub in read-only mode for a while to see if we crash
+# or livelock.
#
. ./common/preamble
-_begin_fstest dangerous_fuzzers dangerous_scrub
+_begin_fstest scrub dangerous_fsstress_scrub
+_cleanup() {
+ cd /
+ _scratch_xfs_stress_scrub_cleanup &> /dev/null
+ rm -r -f $tmp.*
+}
_register_cleanup "_cleanup" BUS
# Import common functions.
. ./common/filter
. ./common/fuzzy
. ./common/inject
+. ./common/xfs
# real QA test starts here
_supported_fs xfs
-_require_test_program "feature"
-_require_command "$KILLALL_PROG" killall
-_require_command "$TIMEOUT_PROG" timeout
-_require_scrub
_require_scratch
+_require_xfs_stress_scrub
-echo "Format and populate"
_scratch_mkfs > "$seqres.full" 2>&1
_scratch_mount
-
-STRESS_DIR="$SCRATCH_MNT/testdir"
-mkdir -p $STRESS_DIR
-
-cpus=$(( $($here/src/feature -o) * 4 * LOAD_FACTOR))
-$FSSTRESS_PROG -d $STRESS_DIR -p $cpus -n $((cpus * 100000)) $FSSTRESS_AVOID >/dev/null 2>&1 &
-$XFS_SCRUB_PROG -d -T -v -n $SCRATCH_MNT >> $seqres.full
-
-killstress() {
- sleep $(( 60 * TIME_FACTOR ))
- $KILLALL_PROG -q $FSSTRESS_PROG
-}
-
-echo "Concurrent scrub"
-start=$(date +%s)
-end=$((start + (60 * TIME_FACTOR) ))
-killstress &
-echo "Scrub started at $(date --date="@${start}"), ending at $(date --date="@${end}")" >> $seqres.full
-while [ "$(date +%s)" -lt "$end" ]; do
- $TIMEOUT_PROG -s TERM $(( end - $(date +%s) + 2 )) $XFS_SCRUB_PROG -d -T -v -n $SCRATCH_MNT >> $seqres.full 2>&1
-done
-
-echo "Test done"
-echo "Scrub finished at $(date)" >> $seqres.full
-$KILLALL_PROG -q $FSSTRESS_PROG
+_scratch_xfs_stress_scrub -S '-n'
# success, all done
+echo Silence is golden
status=0
exit
QA output created by 285
-Format and populate
-Concurrent scrub
-Test done
+Silence is golden
#
# FS QA Test No. 286
#
-# Race fio and xfs_scrub for a while to see if we crash or livelock.
+# Race fsstress and xfs_scrub in force-repair mode for a while to see if we
+# crash or livelock.
#
. ./common/preamble
-_begin_fstest dangerous_fuzzers dangerous_scrub dangerous_online_repair
+_begin_fstest online_repair dangerous_fsstress_repair
+_cleanup() {
+ cd /
+ _scratch_xfs_stress_scrub_cleanup &> /dev/null
+ rm -r -f $tmp.*
+}
_register_cleanup "_cleanup" BUS
# Import common functions.
. ./common/filter
. ./common/fuzzy
. ./common/inject
+. ./common/xfs
# real QA test starts here
_supported_fs xfs
-_require_test_program "feature"
-_require_command "$KILLALL_PROG" killall
-_require_command "$TIMEOUT_PROG" timeout
-_require_scrub
_require_scratch
-# xfs_scrub will turn on error injection itself
-_require_xfs_io_error_injection "force_repair"
+_require_xfs_stress_online_repair
-echo "Format and populate"
_scratch_mkfs > "$seqres.full" 2>&1
_scratch_mount
-
-STRESS_DIR="$SCRATCH_MNT/testdir"
-mkdir -p $STRESS_DIR
-
-cpus=$(( $($here/src/feature -o) * 4 * LOAD_FACTOR))
-$FSSTRESS_PROG -d $STRESS_DIR -p $cpus -n $((cpus * 100000)) $FSSTRESS_AVOID >/dev/null 2>&1 &
-$XFS_SCRUB_PROG -d -T -v -n $SCRATCH_MNT >> $seqres.full
-
-killstress() {
- sleep $(( 60 * TIME_FACTOR ))
- $KILLALL_PROG -q $FSSTRESS_PROG
-}
-
-echo "Concurrent repair"
-start=$(date +%s)
-end=$((start + (60 * TIME_FACTOR) ))
-killstress &
-echo "Repair started at $(date --date="@${start}"), ending at $(date --date="@${end}")" >> $seqres.full
-while [ "$(date +%s)" -lt "$end" ]; do
- XFS_SCRUB_FORCE_REPAIR=1 $TIMEOUT_PROG -s TERM $(( end - $(date +%s) + 2 )) $XFS_SCRUB_PROG -d -T -v $SCRATCH_MNT >> $seqres.full
-done
-
-echo "Test done"
-echo "Repair finished at $(date)" >> $seqres.full
-$KILLALL_PROG -q $FSSTRESS_PROG
+_scratch_xfs_stress_online_repair -S '-k'
# success, all done
+echo Silence is golden
status=0
exit
QA output created by 286
-Format and populate
-Concurrent repair
-Test done
+Silence is golden
# that leaf directly (as xfsprogs commit f714016).
#
. ./common/preamble
-_begin_fstest auto quick repair fuzzers
+_begin_fstest auto quick repair fuzzers attr
# Import common functions.
. ./common/filter
_notrun "xfs_db can't set attr hdr.count to 0"
fi
-# make sure xfs_repair can find above corruption. If it can't, that
-# means we need to fix this bug on current xfs_repair
-_scratch_xfs_repair -n >> $seqres.full 2>&1
-if [ $? -eq 0 ];then
- _fail "xfs_repair can't find the corruption"
-else
- # If xfs_repair can find this corruption, then this repair
- # should junk above leaf attribute and fix this XFS.
- _scratch_xfs_repair >> $seqres.full 2>&1
+# Check that xfs_repair discards the attr fork if block 0 is an empty leaf
+# block. Empty leaf blocks at the start of the xattr data can be a byproduct
+# of a shutdown race, and hence are not a corruption.
+_scratch_xfs_repair >> $seqres.full 2>&1
- # Old xfs_repair maybe find and fix this corruption by
- # reset the first used heap value and the usedbytes cnt
- # in ablock 0. That's not what we want. So check if
- # xfs_repair has junked the whole ablock 0 by xfs_db.
- _scratch_xfs_db -x -c "inode $inum" -c "ablock 0" | \
- grep -q "no attribute data"
- if [ $? -ne 0 ]; then
- _fail "xfs_repair didn't junk the empty attr leaf"
- fi
+# Old xfs_repair maybe find and fix this corruption by
+# reset the first used heap value and the usedbytes cnt
+# in ablock 0. That's not what we want. So check if
+# xfs_repair has junked the whole ablock 0 by xfs_db.
+_scratch_xfs_db -x -c "inode $inum" -c "ablock 0" | \
+ grep -q "no attribute data"
+if [ $? -ne 0 ]; then
+ _fail "xfs_repair didn't junk the empty attr leaf"
fi
echo "Silence is golden"
. ./common/preamble
_begin_fstest auto repair metadump
+# Override the default cleanup function.
+_cleanup()
+{
+ cd /
+ rm -r -f $tmp.*
+ _xfs_cleanup_verify_metadump
+}
+
# Import common functions.
. ./common/filter
+. ./common/metadump
_supported_fs xfs
+_require_command "$XFS_MDRESTORE_PROG" "xfs_mdrestore"
+_xfs_setup_verify_metadump
# real QA test starts here
_require_scratch
# Yes they can! Now...
# Can xfs_metadump cope with this monster?
-_scratch_xfs_metadump $tmp.metadump || _fail "xfs_metadump failed"
-xfs_mdrestore $tmp.metadump $tmp.img || _fail "xfs_mdrestore failed"
-[ "$USE_EXTERNAL" = yes ] && [ -n "$SCRATCH_RTDEV" ] && \
- rt_repair_opts="-r $SCRATCH_RTDEV"
-[ "$USE_EXTERNAL" = yes ] && [ -n "$SCRATCH_LOGDEV" ] && \
- log_repair_opts="-l $SCRATCH_LOGDEV"
-$XFS_REPAIR_PROG $rt_repair_opts $log_repair_opts -f $tmp.img >> $seqres.full 2>&1 || \
- _fail "xfs_repair of metadump failed"
+_xfs_verify_metadumps '-a -o'
# Yes it can; success, all done
status=0
_require_command "$(type -P $CAT)" $CAT
for COMMAND in `$XFS_IO_PROG -c help | awk '{print $1}' | grep -v "^Use"`; do
- $CAT "$MANPAGE" | egrep -q "^\.B.*$COMMAND" || \
+ $CAT "$MANPAGE" | grep -E -q "^\.B.*$COMMAND" || \
echo "$COMMAND not documented in the xfs_io manpage"
done
# Failure is a hang; KASAN should also catch this.
#
. ./common/preamble
-_begin_fstest auto dir metadata
+_begin_fstest auto dir metadata prealloc punch
# Import common functions.
. ./common/filter
. ./common/preamble
_begin_fstest auto freeze
+# Override the default cleanup function.
+_cleanup()
+{
+ # Make sure $SCRATCH_MNT is unfreezed
+ xfs_freeze -u $SCRATCH_MNT 2>/dev/null
+ $KILLALL_PROG -q -9 $FSSTRESS_PROG
+ wait
+ cd /
+ rm -f $tmp.*
+}
+
# Import common functions.
. ./common/filter
STRESS_DIR="$SCRATCH_MNT/testdir"
mkdir -p $STRESS_DIR
-$FSSTRESS_PROG -d $STRESS_DIR -n 100 -p 1000 $FSSTRESS_AVOID >/dev/null 2>&1 &
+$FSSTRESS_PROG -d $STRESS_DIR -n 100 -p 1000 $FSSTRESS_AVOID >>$seqres.full &
# Freeze/unfreeze file system randomly
echo "Start freeze/unfreeze randomly" | tee -a $seqres.full
_qmount
mkdir -p $QUOTA_DIR
- $FSSTRESS_PROG -d $QUOTA_DIR -n 1000000 -p 100 $FSSTRESS_AVOID >/dev/null 2>&1 &
+ $FSSTRESS_PROG -d $QUOTA_DIR -n 1000000 -p 100 $FSSTRESS_AVOID >>$seqres.full &
sleep 10
$XFS_QUOTA_PROG -x -c "disable -$type" $SCRATCH_DEV
sleep 5
echo "Format"
_scratch_mkfs > $seqres.full 2>&1
_scratch_mount >> $seqres.full
-is_rmap=$($XFS_INFO_PROG $SCRATCH_MNT | grep -c "rmapbt=1")
+is_rmap=$(_xfs_has_feature $SCRATCH_MNT rmapbt -v)
_scratch_unmount
_get_agf_data() {
echo "Format"
_scratch_mkfs > $seqres.full 2>&1
_scratch_mount >> $seqres.full
-is_rmap=$($XFS_INFO_PROG $SCRATCH_MNT | grep -c "rmapbt=1")
+is_rmap=$(_xfs_has_feature $SCRATCH_MNT rmapbt -v)
_scratch_xfs_unmount_dirty
_get_agf_data() {
# Create a file with more than 2^21 blocks (the max length of a bmbt record).
#
. ./common/preamble
-_begin_fstest auto clone rmap
+_begin_fstest auto clone rmap prealloc
# Override the default cleanup function.
_cleanup()
# Inject an error during block remap to test log recovery.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone punch
# Override the default cleanup function.
_cleanup()
echo "Format filesystem"
_scratch_mkfs >/dev/null 2>&1
_scratch_mount >> $seqres.full
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
echo "Create files"
_pwrite_byte 0x66 0 $sz $SCRATCH_MNT/file1 >> $seqres.full
# Inject an error during refcount updates to test log recovery.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone punch
# Override the default cleanup function.
_cleanup()
echo "Format filesystem"
_scratch_mkfs >/dev/null 2>&1
_scratch_mount >> $seqres.full
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
$XFS_IO_PROG -c "cowextsize $sz" $SCRATCH_MNT
# Force XFS into "two refcount updates per transaction" mode.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone punch
# Override the default cleanup function.
_cleanup()
# Simulate free extent errors with a file write and a file remove.
#
. ./common/preamble
-_begin_fstest auto quick rw
+_begin_fstest auto quick rw freeze
# Override the default cleanup function.
_cleanup()
{
+ # Make sure $SCRATCH_MNT is unfreezed
+ xfs_freeze -u $SCRATCH_MNT 2>/dev/null
cd /
- _scratch_unmount > /dev/null 2>&1
rm -rf $tmp.*
}
_require_scratch
_require_error_injection
_require_xfs_io_error_injection "rmap_finish_one"
+_require_freeze
blksz=65536
blks=64
echo "Format filesystem"
_scratch_mkfs >/dev/null 2>&1
_scratch_mount >> $seqres.full
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
echo "Create files"
_pwrite_byte 0x66 0 $sz $SCRATCH_MNT/file1 >> $seqres.full
# Force XFS into "two refcount updates per transaction" mode.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone punch
# Override the default cleanup function.
_cleanup()
# Inject an error during extent freeing to test log recovery.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone freeze
# Override the default cleanup function.
_cleanup()
{
+ # Make sure $SCRATCH_MNT is unfreezed
+ xfs_freeze -u $SCRATCH_MNT 2>/dev/null
cd /
- _scratch_unmount > /dev/null 2>&1
rm -rf $tmp.*
}
_require_scratch_reflink
_require_error_injection
_require_xfs_io_error_injection "free_extent"
+_require_freeze
blksz=65536
blks=30
# instead of when we're stashing the CoW orphan record.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone punch
# Override the default cleanup function.
_cleanup()
_scratch_mkfs >/dev/null 2>&1
_scratch_mount >> $seqres.full
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
$XFS_IO_PROG -c "cowextsize $sz" $SCRATCH_MNT
+# This test uses a very large cowextszhint to manipulate the COW fork to
+# contain a large unwritten extent before injecting the error. The goal is
+# that the write() will succeed, writeback will flush the dirty data to disk,
+# and writeback completion will shut down the filesystem when it tries to
+# remove the staging extent record from the refcount btree. In other words,
+# this test ensures that XFS always finishes a COW completion it has started.
+#
+# This test isn't compatible with always_cow mode because the hole in the COW
+# fork left by the first write means that writeback tries to allocate a COW
+# staging extent for an unshared extent and trips over the injected error.
+_require_no_xfs_always_cow
+
echo "Create files"
_pwrite_byte 0x66 0 $sz $SCRATCH_MNT/file1 >> $seqres.full
_cp_reflink $SCRATCH_MNT/file1 $SCRATCH_MNT/file2
# See how well xfs_fsr handles "defragging" a file with a hojillion extents.
#
. ./common/preamble
-_begin_fstest auto quick clone fsr
+_begin_fstest auto quick clone fsr prealloc
# Import common functions.
. ./common/filter
_require_command "$XFS_FSR_PROG" "xfs_fsr"
_require_xfs_io_error_injection "bmap_finish_one"
_require_xfs_scratch_rmapbt
+_require_xfs_io_command falloc # fsr requires support for preallocation
rm -f "$seqres.full"
testdir="$SCRATCH_MNT/test-$seq"
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
blks=3
mkdir "$testdir"
echo "FS should be shut down, touch will fail"
touch $SCRATCH_MNT/badfs 2>&1 | _filter_scratch
-echo "Remount to replay log" | tee /dev/ttyprintk
+echo "Remount to replay log" | _tee_kernlog
_scratch_remount_dump_log >> $seqres.full
new_nextents=$(_count_extents $testdir/file1)
-echo "Check extent count" | tee /dev/ttyprintk
+echo "Check extent count" | _tee_kernlog
$XFS_IO_PROG -c 'stat -v' $testdir/file1 >> $seqres.full
$XFS_IO_PROG -c 'stat -v' $testdir/file2 >> $seqres.full
echo "extents: $old_nextents -> $new_nextents" >> $seqres.full
# Ensure that xfs_fsr handles quota correctly while defragging files.
#
. ./common/preamble
-_begin_fstest auto quick clone fsr quota
+_begin_fstest auto quick clone fsr quota prealloc
# Import common functions.
. ./common/filter
do_repquota()
{
- repquota $SCRATCH_MNT | egrep '^(fsgqa|root|nobody)' | sort -r
+ repquota $SCRATCH_MNT | grep -E '^(fsgqa|root|nobody)' | sort -r
}
rm -f "$seqres.full"
# Create a big enough rmapbt that we tickle a fdblocks accounting bug.
#
. ./common/preamble
-_begin_fstest auto quick rmap clone
+_begin_fstest auto quick rmap clone prealloc
# Import common functions.
. ./common/filter
# Make sure query_range returns -EINVAL if lowkey > highkey.
#
. ./common/preamble
-_begin_fstest auto quick rmap clone collapse punch insert zero
+_begin_fstest auto quick rmap clone collapse punch insert zero prealloc
# Import common functions.
. ./common/filter
# Exercise expanding and shrinking the realtime rmap btree.
#
. ./common/preamble
-_begin_fstest auto rmap realtime
+_begin_fstest auto rmap realtime prealloc
# Import common functions.
. ./common/filter
echo "Create a three-level rtrmapbt"
# inode core size is at least 176 bytes; btree header is 56 bytes;
# rtrmap record is 32 bytes; and rtrmap key/pointer are 56 bytes.
-i_ptrs=$(( (isize - 176) / 56 ))
+i_core_size="$(_xfs_get_inode_core_bytes $SCRATCH_MNT)"
+i_ptrs=$(( (isize - i_core_size) / 56 ))
bt_ptrs=$(( (blksz - 56) / 56 ))
bt_recs=$(( (blksz - 56) / 32 ))
# Exercise metadump on realtime rmapbt preservation.
#
. ./common/preamble
-_begin_fstest auto rmap realtime metadump
+_begin_fstest auto rmap realtime metadump prealloc
# Override the default cleanup function.
_cleanup()
# real QA test starts here
_supported_fs xfs
+_require_command "$XFS_MDRESTORE_PROG" "xfs_mdrestore"
_require_realtime
_require_xfs_scratch_rmapbt
_require_test_program "punch-alternating"
echo "Create a three-level rtrmapbt"
# inode core size is at least 176 bytes; btree header is 56 bytes;
# rtrmap record is 32 bytes; and rtrmap key/pointer are 56 bytes.
-i_ptrs=$(( (isize - 176) / 56 ))
+i_core_size="$(_xfs_get_inode_core_bytes $SCRATCH_MNT)"
+i_ptrs=$(( (isize - i_core_size) / 56 ))
bt_ptrs=$(( (blksz - 56) / 56 ))
bt_recs=$(( (blksz - 56) / 32 ))
echo "Create metadump file"
_scratch_unmount
-_scratch_xfs_metadump $metadump_file
+_scratch_xfs_metadump $metadump_file -a -o
# Now restore the obfuscated one back and take a look around
echo "Restore metadump"
-xfs_mdrestore $metadump_file $TEST_DIR/image
+SCRATCH_DEV=$TEST_DIR/image _scratch_xfs_mdrestore $metadump_file
SCRATCH_DEV=$TEST_DIR/image _scratch_mount
SCRATCH_DEV=$TEST_DIR/image _scratch_unmount
# Corrupt the realtime rmapbt and see how the kernel and xfs_repair deal.
#
. ./common/preamble
-_begin_fstest fuzzers rmap realtime
+_begin_fstest fuzzers rmap realtime prealloc
# Import common functions.
. ./common/filter
# inode core size is at least 176 bytes; btree header is 56 bytes;
# rtrmap record is 32 bytes; and rtrmap key/pointer are 56 bytes.
-i_ptrs=$(( (isize - 176) / 56 ))
+i_core_size="$(_xfs_get_inode_core_bytes $SCRATCH_MNT)"
+i_ptrs=$(( (isize - i_core_size) / 56 ))
bt_ptrs=$(( (blksz - 56) / 56 ))
bt_recs=$(( (blksz - 56) / 32 ))
# Cross-link file block into rtrmapbt and see if repair fixes it.
#
. ./common/preamble
-_begin_fstest auto quick rmap realtime
+_begin_fstest auto quick rmap realtime prealloc
# Import common functions.
. ./common/filter
# inode core size is at least 176 bytes; btree header is 56 bytes;
# rtrmap record is 32 bytes; and rtrmap key/pointer are 56 bytes.
-i_ptrs=$(( (isize - 176) / 56 ))
+i_core_size="$(_xfs_get_inode_core_bytes $SCRATCH_MNT)"
+i_ptrs=$(( (isize - i_core_size) / 56 ))
bt_recs=$(( (blksz - 56) / 32 ))
blocks=$((i_ptrs * bt_recs + 1))
# Cross-link rtrmapbt block into a file and see if repair fixes it.
#
. ./common/preamble
-_begin_fstest auto quick rmap realtime
+_begin_fstest auto quick rmap realtime prealloc
# Import common functions.
. ./common/filter
# inode core size is at least 176 bytes; btree header is 56 bytes;
# rtrmap record is 32 bytes; and rtrmap key/pointer are 56 bytes.
-i_ptrs=$(( (isize - 176) / 56 ))
+i_core_size="$(_xfs_get_inode_core_bytes $SCRATCH_MNT)"
+i_ptrs=$(( (isize - i_core_size) / 56 ))
bt_recs=$(( (blksz - 56) / 32 ))
blocks=$((i_ptrs * bt_recs + 1))
# Basic rmap manipulation tests for realtime files.
#
. ./common/preamble
-_begin_fstest auto quick rmap collapse punch insert zero realtime
+_begin_fstest auto quick rmap collapse punch insert zero realtime prealloc
# Import common functions.
. ./common/filter
# - Check the number of extents.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap unshare
# Import common functions.
. ./common/filter
# - Check the number of extents.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap unshare
# Import common functions.
. ./common/filter
# - Check the number of extents.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap unshare
# Import common functions.
. ./common/filter
mkdir $testdir
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
nr=128
filesize=$((blksz * nr))
bufnr=8
# - Check the number of extents.
#
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap unshare
# Import common functions.
. ./common/filter
mkdir $testdir
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
nr=128
filesize=$((blksz * nr))
bufnr=8
mknod $testdir/BLKDEV b 1 1
mknod $testdir/FIFO p
-$XFS_INFO_PROG $SCRATCH_MNT | grep -q "ftype=1" && FTYPE_FEATURE=1
+_xfs_has_feature $SCRATCH_MNT ftype && FTYPE_FEATURE=1
# Record test dir inode for xfs_repair filter
inode_filter=$tmp.sed
_scratch_xfs_fuzz_metadata '' 'offline' 'agfl 0' >> $seqres.full
echo "Done fuzzing AGFL"
-echo "Fuzz AGFL flfirst"
+# Restore a correct copy of the filesystem before we start the second round of
+# fuzzing. This avoids corruption errors from xfs_db when we probe for flfirst
+# in the AGF and later when _scratch_xfs_fuzz_metadata probes the AGFL fields.
+__scratch_xfs_fuzz_mdrestore
flfirst=$(_scratch_xfs_db -c 'agf 0' -c 'p flfirst' | sed -e 's/flfirst = //g')
+
+echo "Fuzz AGFL flfirst"
SCRATCH_XFS_LIST_METADATA_FIELDS="bno[${flfirst}]" _scratch_xfs_fuzz_metadata '' 'offline' 'agfl 0' >> $seqres.full
echo "Done fuzzing AGFL flfirst"
_scratch_xfs_fuzz_metadata '' 'online' 'agfl 0' >> $seqres.full
echo "Done fuzzing AGFL"
-echo "Fuzz AGFL flfirst"
+# Restore a correct copy of the filesystem before we start the second round of
+# fuzzing. This avoids corruption errors from xfs_db when we probe for flfirst
+# in the AGF and later when _scratch_xfs_fuzz_metadata probes the AGFL fields.
+__scratch_xfs_fuzz_mdrestore
flfirst=$(_scratch_xfs_db -c 'agf 0' -c 'p flfirst' | sed -e 's/flfirst = //g')
+
+echo "Fuzz AGFL flfirst"
SCRATCH_XFS_LIST_METADATA_FIELDS="bno[${flfirst}]" _scratch_xfs_fuzz_metadata '' 'online' 'agfl 0' >> $seqres.full
echo "Done fuzzing AGFL flfirst"
_scratch_populate_cached nofill > $seqres.full 2>&1
echo "Fuzz AGI"
-_scratch_xfs_fuzz_metadata '' 'online' 'agi 0' >> $seqres.full
+_scratch_xfs_fuzz_metadata '' 'online' 'agi 1' >> $seqres.full
echo "Done fuzzing AGI"
# success, all done
echo "Format and populate"
_scratch_populate_cached nofill > $seqres.full 2>&1
+path="$(_scratch_xfs_find_agbtree_height 'bno' 2)" || \
+ _fail "could not find two-level bnobt"
+
echo "Fuzz bnobt recs"
-_scratch_xfs_fuzz_metadata '' 'offline' 'agf 0' 'addr bnoroot' 'addr ptrs[1]' >> $seqres.full
+_scratch_xfs_fuzz_metadata '' 'offline' "$path" 'addr bnoroot' 'addr ptrs[1]' >> $seqres.full
echo "Done fuzzing bnobt recs"
# success, all done
echo "Format and populate"
_scratch_populate_cached nofill > $seqres.full 2>&1
+path="$(_scratch_xfs_find_agbtree_height 'bno' 2)" || \
+ _fail "could not find two-level bnobt"
+
echo "Fuzz bnobt recs"
-_scratch_xfs_fuzz_metadata '' 'online' 'agf 0' 'addr bnoroot' 'addr ptrs[1]' >> $seqres.full
+_scratch_xfs_fuzz_metadata '' 'online' "$path" 'addr bnoroot' 'addr ptrs[1]' >> $seqres.full
echo "Done fuzzing bnobt recs"
# success, all done
echo "Format and populate"
_scratch_populate_cached nofill > $seqres.full 2>&1
+path="$(_scratch_xfs_find_agbtree_height 'bno' 2)" || \
+ _fail "could not find two-level bnobt"
+
echo "Fuzz bnobt keyptr"
-_scratch_xfs_fuzz_metadata '' 'offline' 'agf 0' 'addr bnoroot' >> $seqres.full
+_scratch_xfs_fuzz_metadata '' 'offline' "$path" 'addr bnoroot' >> $seqres.full
echo "Done fuzzing bnobt keyptr"
# success, all done
echo "Format and populate"
_scratch_populate_cached nofill > $seqres.full 2>&1
+path="$(_scratch_xfs_find_agbtree_height 'bno' 2)" || \
+ _fail "could not find two-level bnobt"
+
echo "Fuzz bnobt keyptr"
-_scratch_xfs_fuzz_metadata '' 'online' 'agf 0' 'addr bnoroot' >> $seqres.full
+_scratch_xfs_fuzz_metadata '' 'online' "$path" 'addr bnoroot' >> $seqres.full
echo "Done fuzzing bnobt keyptr"
# success, all done
echo "Format and populate"
_scratch_populate_cached nofill > $seqres.full 2>&1
+path="$(_scratch_xfs_find_agbtree_height 'cnt' 2)" || \
+ _fail "could not find two-level cntbt"
+
echo "Fuzz cntbt"
-_scratch_xfs_fuzz_metadata '' 'offline' 'agf 0' 'addr cntroot' 'addr ptrs[1]' >> $seqres.full
+_scratch_xfs_fuzz_metadata '' 'offline' "$path" 'addr cntroot' 'addr ptrs[1]' >> $seqres.full
echo "Done fuzzing cntbt"
# success, all done
echo "Format and populate"
_scratch_populate_cached nofill > $seqres.full 2>&1
+path="$(_scratch_xfs_find_agbtree_height 'cnt' 2)" || \
+ _fail "could not find two-level cntbt"
+
echo "Fuzz cntbt"
-_scratch_xfs_fuzz_metadata '' 'online' 'agf 0' 'addr cntroot' 'addr ptrs[1]' >> $seqres.full
+_scratch_xfs_fuzz_metadata '' 'online' "$path" 'addr cntroot' 'addr ptrs[1]' >> $seqres.full
echo "Done fuzzing cntbt"
# success, all done
echo "Format and populate"
_scratch_populate_cached nofill > $seqres.full 2>&1
+path="$(_scratch_xfs_find_agbtree_height 'ino' 2)" || \
+ _fail "could not find two-level inobt"
+
echo "Fuzz inobt"
-_scratch_xfs_fuzz_metadata '' 'offline' 'agi 0' 'addr root' >> $seqres.full
+_scratch_xfs_fuzz_metadata '' 'offline' "$path" 'addr root' 'addr ptrs[1]' >> $seqres.full
echo "Done fuzzing inobt"
# success, all done
echo "Format and populate"
_scratch_populate_cached nofill > $seqres.full 2>&1
+path="$(_scratch_xfs_find_agbtree_height 'ino' 2)" || \
+ _fail "could not find two-level inobt"
+
echo "Fuzz inobt"
-_scratch_xfs_fuzz_metadata '' 'online' 'agi 1' 'addr root' >> $seqres.full
+_scratch_xfs_fuzz_metadata '' 'online' "$path" 'addr root' 'addr ptrs[1]' >> $seqres.full
echo "Done fuzzing inobt"
# success, all done
echo "Format and populate"
_scratch_populate_cached nofill > $seqres.full 2>&1
+path="$(_scratch_xfs_find_agbtree_height 'fino' 2)" || \
+ _fail "could not find two-level finobt"
+
echo "Fuzz finobt"
-_scratch_xfs_fuzz_metadata '' 'offline' 'agi 0' 'addr free_root' >> $seqres.full
+_scratch_xfs_fuzz_metadata '' 'offline' "$path" 'addr free_root' 'addr ptrs[1]' >> $seqres.full
echo "Done fuzzing finobt"
# success, all done
echo "Format and populate"
_scratch_populate_cached nofill > $seqres.full 2>&1
+path="$(_scratch_xfs_find_agbtree_height 'fino' 2)" || \
+ _fail "could not find two-level finobt"
+
echo "Fuzz finobt"
-_scratch_xfs_fuzz_metadata '' 'online' 'agi 0' 'addr free_root' >> $seqres.full
+_scratch_xfs_fuzz_metadata '' 'online' "$path" 'addr free_root' 'addr ptrs[1]' >> $seqres.full
echo "Done fuzzing finobt"
# success, all done
echo "Format and populate"
_scratch_populate_cached nofill > $seqres.full 2>&1
+path="$(_scratch_xfs_find_agbtree_height 'rmap' 2)" || \
+ _fail "could not find two-level rmapbt"
+
echo "Fuzz rmapbt recs"
-_scratch_xfs_fuzz_metadata '' 'offline' 'agf 0' 'addr rmaproot' 'addr ptrs[1]' >> $seqres.full
+_scratch_xfs_fuzz_metadata '' 'offline' "$path" 'addr rmaproot' 'addr ptrs[1]' >> $seqres.full
echo "Done fuzzing rmapbt recs"
# success, all done
echo "Format and populate"
_scratch_populate_cached nofill > $seqres.full 2>&1
+path="$(_scratch_xfs_find_agbtree_height 'rmap' 2)" || \
+ _fail "could not find two-level rmapbt"
+
echo "Fuzz rmapbt recs"
-_scratch_xfs_fuzz_metadata '' 'online' 'agf 0' 'addr rmaproot' 'addr ptrs[1]' >> $seqres.full
+_scratch_xfs_fuzz_metadata '' 'online' "$path" 'addr rmaproot' 'addr ptrs[1]' >> $seqres.full
echo "Done fuzzing rmapbt recs"
# success, all done
echo "Format and populate"
_scratch_populate_cached nofill > $seqres.full 2>&1
+path="$(_scratch_xfs_find_agbtree_height 'rmap' 2)" || \
+ _fail "could not find two-level rmapbt"
+
echo "Fuzz rmapbt keyptr"
-_scratch_xfs_fuzz_metadata '' 'offline' 'agf 0' 'addr rmaproot' >> $seqres.full
+_scratch_xfs_fuzz_metadata '' 'offline' "$path" 'addr rmaproot' >> $seqres.full
echo "Done fuzzing rmapbt keyptr"
# success, all done
echo "Format and populate"
_scratch_populate_cached nofill > $seqres.full 2>&1
+path="$(_scratch_xfs_find_agbtree_height 'rmap' 2)" || \
+ _fail "could not find two-level rmapbt"
+
echo "Fuzz rmapbt keyptr"
-_scratch_xfs_fuzz_metadata '' 'online' 'agf 0' 'addr rmaproot' >> $seqres.full
+_scratch_xfs_fuzz_metadata '' 'online' "$path" 'addr rmaproot' >> $seqres.full
echo "Done fuzzing rmapbt keyptr"
# success, all done
echo "Format and populate"
_scratch_populate_cached nofill > $seqres.full 2>&1
+path="$(_scratch_xfs_find_agbtree_height 'refcnt' 2)" || \
+ _fail "could not find two-level refcountbt"
+
echo "Fuzz refcountbt"
-_scratch_xfs_fuzz_metadata '' 'offline' 'agf 0' 'addr refcntroot' >> $seqres.full
+_scratch_xfs_fuzz_metadata '' 'offline' "$path" 'addr refcntroot' >> $seqres.full
echo "Done fuzzing refcountbt"
# success, all done
#
# FS QA Test No. 373
#
-# Populate a XFS filesystem and fuzz every refcountbt field.
+# Populate a XFS filesystem and fuzz every refcountbt key/pointer field.
# Use xfs_scrub to fix the corruption.
#
. ./common/preamble
echo "Format and populate"
_scratch_populate_cached nofill > $seqres.full 2>&1
+path="$(_scratch_xfs_find_agbtree_height 'refcnt' 2)" || \
+ _fail "could not find two-level refcountbt"
+
echo "Fuzz refcountbt"
-_scratch_xfs_fuzz_metadata '' 'online' 'agf 0' 'addr refcntroot' >> $seqres.full
+_scratch_xfs_fuzz_metadata '' 'online' "$path" 'addr refcntroot' >> $seqres.full
echo "Done fuzzing refcountbt"
# success, all done
echo "Format and populate"
_scratch_populate_cached nofill > $seqres.full 2>&1
+path="$(_scratch_xfs_find_agbtree_height 'refcnt' 2)" || \
+ _fail "could not find two-level refcountbt"
+
echo "Fuzz refcountbt"
-_scratch_xfs_fuzz_metadata '' 'offline' 'agf 0' 'addr refcntroot' 'addr ptrs[1]' >> $seqres.full
+_scratch_xfs_fuzz_metadata '' 'offline' "$path" 'addr refcntroot' 'addr ptrs[1]' >> $seqres.full
echo "Done fuzzing refcountbt"
# success, all done
echo "Format and populate"
_scratch_populate_cached nofill > $seqres.full 2>&1
+path="$(_scratch_xfs_find_agbtree_height 'refcnt' 2)" || \
+ _fail "could not find two-level refcountbt"
+
echo "Fuzz refcountbt"
-_scratch_xfs_fuzz_metadata '' 'online' 'agf 0' 'addr refcntroot' 'addr ptrs[1]' >> $seqres.full
+_scratch_xfs_fuzz_metadata '' 'online' "$path" 'addr refcntroot' 'addr ptrs[1]' >> $seqres.full
echo "Done fuzzing refcountbt"
# success, all done
#
# FS QA Test No. 422
#
-# Race freeze and rmapbt repair for a while to see if we crash or livelock.
-# rmapbt repair requires us to freeze the filesystem to stop all filesystem
-# activity, so we can't have userspace wandering in and thawing it.
+# Race fsstress and rmapbt repair for a while to see if we crash or livelock.
#
. ./common/preamble
-_begin_fstest dangerous_scrub dangerous_online_repair
+_begin_fstest online_repair dangerous_fsstress_repair freeze
+_cleanup() {
+ _scratch_xfs_stress_scrub_cleanup &> /dev/null
+ cd /
+ rm -r -f $tmp.*
+}
_register_cleanup "_cleanup" BUS
# Import common functions.
. ./common/filter
. ./common/fuzzy
. ./common/inject
-. ./common/xfs
# real QA test starts here
_supported_fs xfs
-_require_xfs_scratch_rmapbt
-_require_xfs_io_command "scrub"
-_require_xfs_io_error_injection "force_repair"
-_require_command "$KILLALL_PROG" killall
+_require_scratch
+_require_xfs_stress_online_repair
-echo "Format and populate"
_scratch_mkfs > "$seqres.full" 2>&1
_scratch_mount
-
-STRESS_DIR="$SCRATCH_MNT/testdir"
-mkdir -p $STRESS_DIR
-
-for i in $(seq 0 9); do
- mkdir -p $STRESS_DIR/$i
- for j in $(seq 0 9); do
- mkdir -p $STRESS_DIR/$i/$j
- for k in $(seq 0 9); do
- echo x > $STRESS_DIR/$i/$j/$k
- done
- done
-done
-
-cpus=$(( $($here/src/feature -o) * 4 * LOAD_FACTOR))
-
-echo "Concurrent repair"
-filter_output() {
- egrep -v '(Device or resource busy|Invalid argument)'
-}
-freeze_loop() {
- end="$1"
-
- while [ "$(date +%s)" -lt $end ]; do
- $XFS_IO_PROG -x -c 'freeze' -c 'thaw' $SCRATCH_MNT 2>&1 | filter_output
- done
-}
-repair_loop() {
- end="$1"
-
- while [ "$(date +%s)" -lt $end ]; do
- $XFS_IO_PROG -x -c 'repair rmapbt 0' -c 'repair rmapbt 1' $SCRATCH_MNT 2>&1 | filter_output
- done
-}
-stress_loop() {
- end="$1"
-
- FSSTRESS_ARGS=$(_scale_fsstress_args -p 4 -d $SCRATCH_MNT -n 2000 $FSSTRESS_AVOID)
- while [ "$(date +%s)" -lt $end ]; do
- $FSSTRESS_PROG $FSSTRESS_ARGS >> $seqres.full
- done
-}
-$XFS_IO_PROG -x -c 'inject force_repair' $SCRATCH_MNT
-
-start=$(date +%s)
-end=$((start + (30 * TIME_FACTOR) ))
-
-echo "Loop started at $(date --date="@${start}"), ending at $(date --date="@${end}")" >> $seqres.full
-stress_loop $end &
-freeze_loop $end &
-repair_loop $end &
-
-# Wait until 2 seconds after the loops should have finished...
-while [ "$(date +%s)" -lt $((end + 2)) ]; do
- sleep 1
-done
-
-# ...and clean up after the loops in case they didn't do it themselves.
-$KILLALL_PROG -TERM xfs_io fsstress >> $seqres.full 2>&1
-$XFS_IO_PROG -x -c 'thaw' $SCRATCH_MNT >> $seqres.full 2>&1
-
-echo "Loop finished at $(date)" >> $seqres.full
-echo "Test done"
+_require_xfs_has_feature "$SCRATCH_MNT" rmapbt
+_scratch_xfs_stress_online_repair -s "repair rmapbt %agno%"
# success, all done
+echo Silence is golden
status=0
exit
QA output created by 422
-Format and populate
-Concurrent repair
-Test done
+Silence is golden
# count them if the fork is in btree format.
#
. ./common/preamble
-_begin_fstest dangerous_scrub
+_begin_fstest dangerous_scrub prealloc
_register_cleanup "_cleanup" BUS
. ./common/filter
. ./common/fuzzy
. ./common/inject
-. ./common/xfs
# real QA test starts here
_supported_fs xfs
_cleanup()
{
cd /
- rm -f "$tmp".* $metadump_file $metadump_img
+ rm -f "$tmp".*
+ _xfs_cleanup_verify_metadump
}
# Import common functions.
. ./common/filter
+. ./common/metadump
# real QA test starts here
_supported_fs xfs
+_require_command "$XFS_MDRESTORE_PROG" "xfs_mdrestore"
_require_scratch
+_xfs_setup_verify_metadump
rm -f "$seqres.full"
# block. 8187 hashes/dablk / 248 dirents/dirblock = ~33 dirblocks per
# dablock. 33 dirblocks * 64k mean that we can expand a directory by
# 2112k before we have to allocate another da btree block.
+
_scratch_mkfs -b size=1k -n size=64k > "$seqres.full" 2>&1
_scratch_mount >> "$seqres.full" 2>&1
-metadump_file="$TEST_DIR/meta-$seq"
-metadump_img="$TEST_DIR/img-$seq"
-rm -f $metadump_file $metadump_img
testdir="$SCRATCH_MNT/test-$seq"
max_fname_len=255
blksz=$(_get_block_size $SCRATCH_MNT)
echo "qualifying extent: $extlen blocks" >> $seqres.full
test -n "$extlen" || _notrun "could not create dir extent > 1000 blocks"
-echo "Try to metadump"
-_scratch_xfs_metadump $metadump_file -w
-xfs_mdrestore $metadump_file $metadump_img
-
-echo "Check restored metadump image"
-$XFS_REPAIR_PROG -n $metadump_img >> $seqres.full 2>&1
+echo "Try to metadump, restore and check restored metadump image"
+_xfs_verify_metadumps '-a -o -w'
# success, all done
status=0
Format and mount
Create huge dir
Check for > 1000 block extent?
-Try to metadump
-Check restored metadump image
+Try to metadump, restore and check restored metadump image
# real QA test starts here
_supported_fs xfs
-_require_loadable_fs_module "xfs"
_require_quota
_require_scratch_reflink
_require_cp_reflink
_require_command "$XFS_FSR_PROG" "xfs_fsr"
+_require_xfs_io_command falloc # fsr requires support for preallocation
_require_xfs_io_error_injection "bmap_finish_one"
_require_xfs_scratch_rmapbt
echo "FS should be shut down, touch will fail"
touch $SCRATCH_MNT/badfs 2>&1 | _filter_scratch
-echo "Remount to replay log" | tee /dev/ttyprintk
+echo "Remount to replay log" | _tee_kernlog
_scratch_unmount
_scratch_dump_log >> $seqres.full
_scratch_xfs_db -x -c 'agf 0' -c 'addr refcntroot' -c 'fuzz -d recs[1].startblock ones' >> $seqres.full
rm -f ${RESULT_DIR}/require_scratch
echo "See if we leak"
-_reload_fs_module "xfs"
+_test_loadable_fs_module "xfs"
# success, all done
status=0
# real QA test starts here
_supported_fs xfs
-_require_loadable_fs_module "xfs"
_require_quota
_require_scratch_reflink
_require_cp_reflink
_pwrite_byte 0x63 0 $blksz $testdir/file2 >> $seqres.full
_reflink_range $testdir/file2 0 $testdir/file1 $blksz $blksz >> $seqres.full
-echo "Remount to check recovery" | tee /dev/ttyprintk
+echo "Remount to check recovery" | _tee_kernlog
_scratch_unmount
_scratch_xfs_db -x -c 'agf 0' -c 'addr refcntroot' -c 'fuzz -d recs[1].startblock ones' >> $seqres.full
_scratch_xfs_db -x -c 'agf 0' -c 'addr refcntroot' -c p >> $seqres.full
rm -f ${RESULT_DIR}/require_scratch
echo "See if we leak"
-_reload_fs_module "xfs"
+_test_loadable_fs_module "xfs"
# success, all done
status=0
# real QA test starts here
_supported_fs xfs
-_require_loadable_fs_module "xfs"
_require_scratch_reflink
_require_cp_reflink
+_require_xfs_io_command falloc # fsr requires support for preallocation
_require_command "$XFS_FSR_PROG" "xfs_fsr"
_require_xfs_io_error_injection "bmap_finish_one"
_require_xfs_scratch_rmapbt
testdir="$SCRATCH_MNT/test-$seq"
blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
blks=3
mkdir "$testdir"
echo "FS should be shut down, touch will fail"
touch $SCRATCH_MNT/badfs 2>&1 | _filter_scratch
-echo "Remount to replay log" | tee /dev/ttyprintk
+echo "Remount to replay log" | _tee_kernlog
_scratch_unmount
_scratch_dump_log >> $seqres.full
_scratch_xfs_db -x -c 'agf 0' -c 'addr refcntroot' -c 'fuzz -d recs[1].startblock ones' >> $seqres.full
rm -f ${RESULT_DIR}/require_scratch
echo "See if we leak"
-_reload_fs_module "xfs"
+_test_loadable_fs_module "xfs"
# success, all done
status=0
# Fixed by upstream commit 373b058 ("xfs: Properly retry failed dquot
# items in case of error during buffer writeback")
. ./common/preamble
-_begin_fstest auto quick quota
+_begin_fstest auto quick quota freeze
# Override the default cleanup function.
_cleanup()
{
+ # Make sure $SCRATCH_MNT is unfreezed
+ xfs_freeze -u $SCRATCH_MNT 2>/dev/null
[ -z "${interval}" ] || \
sysctl -w fs.xfs.xfssyncd_centisecs=${interval} >/dev/null 2>&1
cd /
# real QA test starts here
_supported_fs xfs
_require_scratch_nocheck
-# We corrupt XFS on purpose, and check if assert failures would crash system.
-_require_no_xfs_bug_on_assert
+
+# We corrupt XFS on purpose, and check if assert failures would crash the
+# system when trying to xfs_log_mount. Hence this is a regression test for:
+_fixed_by_kernel_commit 9c92ee208b1f \
+ "xfs: validate sb_logsunit is a multiple of the fs blocksize"
+
+# This used to be _require_no_xfs_bug_on_assert, but now we've fixed the sb
+# verifier to reject this before xfs_log_mount gets to it:
+_fixed_by_kernel_commit f1e1765aad7d \
+ "xfs: journal geometry is not properly bounds checked"
rm -f "$seqres.full"
_scratch_xfs_set_sb_field logsunit $((blksz - 1)) >> $seqres.full 2>&1
# Check if logsunit is set correctly
-lsunit=$(_scratch_xfs_get_sb_field logsunit)
+lsunit=$(_scratch_xfs_get_sb_field logsunit 2>/dev/null)
[ $lsunit -ne $((blksz - 1)) ] && _notrun "failed to set sb_logsunit"
# Mount and writing log may trigger a crash on buggy kernel
_supported_fs xfs
_require_quota
+_require_scratch_delalloc
_require_scratch_reflink
_require_cp_reflink
_require_user
# accounting inconsistency.
#
. ./common/preamble
-_begin_fstest auto quick ioctl fsr punch
+_begin_fstest auto quick ioctl fsr punch fiemap prealloc
# Import common functions.
. ./common/filter
_require_xfs_io_command "falloc"
_require_xfs_io_command "fpunch"
_require_xfs_io_command "swapext"
+_require_xfs_io_command "fiemap"
_scratch_mkfs | _filter_mkfs >> $seqres.full 2> $tmp.mkfs
_scratch_mount
# about the fix.
#
. ./common/preamble
-_begin_fstest auto quick
+_begin_fstest auto quick prealloc
_register_cleanup "_cleanup; rm -f $tmp.*"
cmd="$1"
# Format filesystem
- echo "TEST $cmd" | tee /dev/ttyprintk
+ echo "TEST $cmd" | _tee_kernlog
echo "TEST $cmd" >> $seqres.full
_scratch_mkfs >> $seqres.full
# freed inodes in a partially initialized state.
#
. ./common/preamble
-_begin_fstest auto quick filestreams
+_begin_fstest auto quick filestreams prealloc
# Import common functions.
. ./common/filter
# after the rmapbt has grown in size.
#
. ./common/preamble
-_begin_fstest auto quick rmap
+_begin_fstest auto quick rmap prealloc
# Import common functions.
. ./common/filter
_scratch_xfs_fuzz_metadata '' 'none' 'agfl 0' >> $seqres.full
echo "Done fuzzing AGFL"
-echo "Fuzz AGFL flfirst"
+# Restore a correct copy of the filesystem before we start the second round of
+# fuzzing. This avoids corruption errors from xfs_db when we probe for flfirst
+# in the AGF and later when _scratch_xfs_fuzz_metadata probes the AGFL fields.
+__scratch_xfs_fuzz_mdrestore
flfirst=$(_scratch_xfs_db -c 'agf 0' -c 'p flfirst' | sed -e 's/flfirst = //g')
+
+echo "Fuzz AGFL flfirst"
SCRATCH_XFS_LIST_METADATA_FIELDS="bno[${flfirst}]" _scratch_xfs_fuzz_metadata '' 'none' 'agfl 0' >> $seqres.full
echo "Done fuzzing AGFL flfirst"
echo "Format and populate"
_scratch_populate_cached nofill > $seqres.full 2>&1
+path="$(_scratch_xfs_find_agbtree_height 'bno' 2)" || \
+ _fail "could not find two-level bnobt"
+
echo "Fuzz bnobt recs"
-_scratch_xfs_fuzz_metadata '' 'none' 'agf 0' 'addr bnoroot' 'addr ptrs[1]' >> $seqres.full
+_scratch_xfs_fuzz_metadata '' 'none' "$path" 'addr bnoroot' 'addr ptrs[1]' >> $seqres.full
echo "Done fuzzing bnobt recs"
# success, all done
echo "Format and populate"
_scratch_populate_cached nofill > $seqres.full 2>&1
+path="$(_scratch_xfs_find_agbtree_height 'bno' 2)" || \
+ _fail "could not find two-level bnobt"
+
echo "Fuzz bnobt keyptr"
-_scratch_xfs_fuzz_metadata '' 'none' 'agf 0' 'addr bnoroot' >> $seqres.full
+_scratch_xfs_fuzz_metadata '' 'none' "$path" 'addr bnoroot' >> $seqres.full
echo "Done fuzzing bnobt keyptr"
# success, all done
echo "Format and populate"
_scratch_populate_cached nofill > $seqres.full 2>&1
+path="$(_scratch_xfs_find_agbtree_height 'cnt' 2)" || \
+ _fail "could not find two-level cntbt"
+
echo "Fuzz cntbt"
-_scratch_xfs_fuzz_metadata '' 'none' 'agf 0' 'addr cntroot' 'addr ptrs[1]' >> $seqres.full
+_scratch_xfs_fuzz_metadata '' 'none' "$path" 'addr cntroot' 'addr ptrs[1]' >> $seqres.full
echo "Done fuzzing cntbt"
# success, all done
echo "Format and populate"
_scratch_populate_cached nofill > $seqres.full 2>&1
+path="$(_scratch_xfs_find_agbtree_height 'ino' 2)" || \
+ _fail "could not find two-level inobt"
+
echo "Fuzz inobt"
-_scratch_xfs_fuzz_metadata '' 'none' 'agi 1' 'addr root' >> $seqres.full
+_scratch_xfs_fuzz_metadata '' 'none' "$path" 'addr root' 'addr ptrs[1]' >> $seqres.full
echo "Done fuzzing inobt"
# success, all done
echo "Format and populate"
_scratch_populate_cached nofill > $seqres.full 2>&1
+path="$(_scratch_xfs_find_agbtree_height 'fino' 2)" || \
+ _fail "could not find two-level finobt"
+
echo "Fuzz finobt"
-_scratch_xfs_fuzz_metadata '' 'none' 'agi 0' 'addr free_root' >> $seqres.full
+_scratch_xfs_fuzz_metadata '' 'none' "$path" 'addr free_root' 'addr ptrs[1]' >> $seqres.full
echo "Done fuzzing finobt"
# success, all done
echo "Format and populate"
_scratch_populate_cached nofill > $seqres.full 2>&1
+path="$(_scratch_xfs_find_agbtree_height 'rmap' 2)" || \
+ _fail "could not find two-level rmapbt"
+
echo "Fuzz rmapbt recs"
-_scratch_xfs_fuzz_metadata '' 'none' 'agf 0' 'addr rmaproot' 'addr ptrs[1]' >> $seqres.full
+_scratch_xfs_fuzz_metadata '' 'none' "$path" 'addr rmaproot' 'addr ptrs[1]' >> $seqres.full
echo "Done fuzzing rmapbt recs"
# success, all done
echo "Format and populate"
_scratch_populate_cached nofill > $seqres.full 2>&1
+path="$(_scratch_xfs_find_agbtree_height 'rmap' 2)" || \
+ _fail "could not find two-level rmapbt"
+
echo "Fuzz rmapbt keyptr"
-_scratch_xfs_fuzz_metadata '' 'none' 'agf 0' 'addr rmaproot' >> $seqres.full
+_scratch_xfs_fuzz_metadata '' 'none' "$path" 'addr rmaproot' >> $seqres.full
echo "Done fuzzing rmapbt keyptr"
# success, all done
#
# FS QA Test No. 464
#
-# Populate a XFS filesystem and fuzz every refcountbt field.
+# Populate a XFS filesystem and fuzz every refcountbt key/pointer field.
# Do not fix the filesystem, to test metadata verifiers.
. ./common/preamble
echo "Format and populate"
_scratch_populate_cached nofill > $seqres.full 2>&1
+path="$(_scratch_xfs_find_agbtree_height 'refcnt' 2)" || \
+ _fail "could not find two-level refcountbt"
+
echo "Fuzz refcountbt"
-_scratch_xfs_fuzz_metadata '' 'none' 'agf 0' 'addr refcntroot' >> $seqres.full
+_scratch_xfs_fuzz_metadata '' 'none' "$path" 'addr refcntroot' >> $seqres.full
echo "Done fuzzing refcountbt"
# success, all done
echo "Format and populate"
_scratch_populate_cached nofill > $seqres.full 2>&1
+path="$(_scratch_xfs_find_agbtree_height 'refcnt' 2)" || \
+ _fail "could not find two-level refcountbt"
+
echo "Fuzz refcountbt"
-_scratch_xfs_fuzz_metadata '' 'none' 'agf 0' 'addr refcntroot' 'addr ptrs[1]' >> $seqres.full
+_scratch_xfs_fuzz_metadata '' 'none' "$path" 'addr refcntroot' 'addr ptrs[1]' >> $seqres.full
echo "Done fuzzing refcountbt"
# success, all done
char *str;
} syms[] = {
ENDL
-egrep '(__print_flags|__print_symbolic)' $ftrace_dir*/*/format | \
+grep -E '(__print_flags|__print_symbolic)' $ftrace_dir*/*/format | \
sed -f $sedprog | grep '^{' | sort | uniq >> $cprog
cat >> $cprog << ENDL
};
#
# FS QA Test No. 503
#
-# Populate a XFS filesystem and ensure that metadump, mdrestore, and copy
-# all work properly.
+# Populate a XFS filesystem and ensure that metadump and mdrestore all work
+# properly.
#
. ./common/preamble
-_begin_fstest auto copy metadump
+_begin_fstest auto metadump
_register_cleanup "_cleanup" BUS
{
cd /
rm -rf $tmp.* $testdir
+ _xfs_cleanup_verify_metadump
}
# Import common functions.
. ./common/filter
. ./common/populate
+. ./common/metadump
testdir=$TEST_DIR/test-$seq
# real QA test starts here
_supported_fs xfs
-_require_xfs_copy
+_require_command "$XFS_MDRESTORE_PROG" "xfs_mdrestore"
+_require_loop
_require_scratch_nocheck
_require_populate_commands
+_xfs_skip_online_rebuild
+_xfs_skip_offline_rebuild
+_xfs_setup_verify_metadump
echo "Format and populate"
_scratch_populate_cached nofill > $seqres.full 2>&1
mkdir -p $testdir
metadump_file=$testdir/scratch.md
-metadump_file_a=${metadump_file}.a
-metadump_file_g=${metadump_file}.g
-metadump_file_ag=${metadump_file}.ag
copy_file=$testdir/copy.img
-echo metadump
-_scratch_xfs_metadump $metadump_file >> $seqres.full
+echo "metadump and mdrestore"
+_xfs_verify_metadumps
-echo metadump a
-_scratch_xfs_metadump $metadump_file_a -a >> $seqres.full
+echo "metadump a and mdrestore"
+_xfs_verify_metadumps '-a'
-echo metadump g
-_scratch_xfs_metadump $metadump_file_g -g >> $seqres.full
+echo "metadump o and mdrestore"
+_xfs_verify_metadumps '-o'
-echo metadump ag
-_scratch_xfs_metadump $metadump_file_ag -a -g >> $seqres.full
-
-echo copy
-$XFS_COPY_PROG $SCRATCH_DEV $copy_file >> $seqres.full
-_check_scratch_fs $copy_file
-
-echo recopy
-$XFS_COPY_PROG $copy_file $SCRATCH_DEV >> $seqres.full
-_scratch_mount
-_check_scratch_fs
-_scratch_unmount
-
-echo mdrestore
-xfs_mdrestore $metadump_file $SCRATCH_DEV
-_scratch_mount
-_check_scratch_fs
-_scratch_unmount
-
-echo mdrestore a
-xfs_mdrestore $metadump_file_a $SCRATCH_DEV
-_scratch_mount
-_check_scratch_fs
-_scratch_unmount
-
-echo mdrestore g
-xfs_mdrestore $metadump_file_g $SCRATCH_DEV
-_scratch_mount
-_check_scratch_fs
-_scratch_unmount
-
-echo mdrestore ag
-xfs_mdrestore $metadump_file_ag $SCRATCH_DEV
-_scratch_mount
-_check_scratch_fs
-_scratch_unmount
+echo "metadump ao and mdrestore"
+_xfs_verify_metadumps '-a -o'
# success, all done
status=0
QA output created by 503
Format and populate
-metadump
-metadump a
-metadump g
-metadump ag
-copy
-recopy
-mdrestore
-mdrestore a
-mdrestore g
-mdrestore ag
+metadump and mdrestore
+metadump a and mdrestore
+metadump o and mdrestore
+metadump ao and mdrestore
_require_command "$(type -P $CAT)" $CAT
for COMMAND in `$XFS_SPACEMAN_PROG -c help $TEST_DIR | awk '{print $1}' | grep -v "^Use"`; do
- $CAT "$MANPAGE" | egrep -q "^\.B.*$COMMAND" || \
+ $CAT "$MANPAGE" | grep -E -q "^\.B.*$COMMAND" || \
echo "$COMMAND not documented in the xfs_spaceman manpage"
done
_scratch_mkfs > $seqres.full 2>&1
_scratch_mount
+
+_require_scratch_xfs_scrub
+
_scratch_cycle_mount # make sure we haven't run quotacheck on this mount
# Haven't checked anything, it should tell us to run scrub
_scratch_mkfs > "$seqres.full" 2>&1
_scratch_mount
+fs_blksz=$(_get_block_size $SCRATCH_MNT)
+_require_congruent_file_oplen $SCRATCH_MNT $((MAXEXTLEN * fs_blksz))
+
# Create a huge sparse filesystem on the scratch device because that's what
# we're going to need to guarantee that we have enough blocks to overflow in
# the first place. We need to have at least enough free space on that huge fs
# XFS mount options sanity check, refer to 'man 5 xfs'.
#
. ./common/preamble
-_begin_fstest auto mount
+_begin_fstest auto mount prealloc
# Override the default cleanup function.
_cleanup()
# real QA test starts here
_supported_fs xfs
+_fixed_by_kernel_commit 237d7887ae72 \
+ "xfs: show the proper user quota options"
+
_require_test
_require_loop
_require_xfs_io_command "falloc"
# Test allocsize=size
# Valid values for this option are page size (typically 4KiB) through to 1GiB
do_mkfs
-pagesz=$(get_page_size)
+pagesz=$(_get_page_size)
if [ $pagesz -ge 1024 ];then
pagesz="$((pagesz / 1024))k"
fi
$MKFS_XFS_PROG $file >> /dev/null
for COMMAND in `$XFS_DB_PROG -x -c help $file | awk '{print $1}' | grep -v "^Use"`; do
- $CAT "$MANPAGE" | egrep -q "^\.B.*$COMMAND" || \
+ $CAT "$MANPAGE" | grep -E -q "^\.B.*$COMMAND" || \
echo "$COMMAND not documented in the xfs_db manpage"
done
_require_command "$(type -P $CAT)" $CAT
for COMMAND in `$XFS_QUOTA_PROG -x -c help $file | awk '{print $1}' | grep -v "^Use"`; do
- $CAT "$MANPAGE" | egrep -q "^\.B.*$COMMAND" || \
+ $CAT "$MANPAGE" | grep -E -q "^\.B.*$COMMAND" || \
echo "$COMMAND not documented in the xfs_quota manpage"
done
log()
{
- echo "$@" | tee -a $seqres.full /dev/ttyprintk
+ echo "$*" | _tee_kernlog $seqres.full
}
__test_mount_opts()
_cleanup()
{
cd /
- $XFS_IO_PROG -x -c 'thaw' $SCRATCH_MNT > /dev/null 2>&1
+ _scratch_xfs_stress_scrub_cleanup
rm -rf $tmp.*
}
. ./common/filter
. ./common/fuzzy
. ./common/inject
-. ./common/xfs
# real QA test starts here
_supported_fs xfs
_require_xfs_scratch_rmapbt
_require_xfs_io_command "fsmap"
-_require_command "$KILLALL_PROG" killall
+_require_xfs_stress_scrub
-echo "Format and populate"
_scratch_mkfs > "$seqres.full" 2>&1
_scratch_mount
-
-STRESS_DIR="$SCRATCH_MNT/testdir"
-mkdir -p $STRESS_DIR
-
-for i in $(seq 0 9); do
- mkdir -p $STRESS_DIR/$i
- for j in $(seq 0 9); do
- mkdir -p $STRESS_DIR/$i/$j
- for k in $(seq 0 9); do
- echo x > $STRESS_DIR/$i/$j/$k
- done
- done
-done
-
-cpus=$(( $(src/feature -o) * 4 * LOAD_FACTOR))
-
-echo "Concurrent fsmap and freeze"
-filter_output() {
- egrep -v '(Device or resource busy|Invalid argument)'
-}
-freeze_loop() {
- end="$1"
-
- while [ "$(date +%s)" -lt $end ]; do
- $XFS_IO_PROG -x -c 'freeze' $SCRATCH_MNT 2>&1 | filter_output
- $XFS_IO_PROG -x -c 'thaw' $SCRATCH_MNT 2>&1 | filter_output
- done
-}
-fsmap_loop() {
- end="$1"
-
- while [ "$(date +%s)" -lt $end ]; do
- $XFS_IO_PROG -c 'fsmap -v' $SCRATCH_MNT > /dev/null
- done
-}
-stress_loop() {
- end="$1"
-
- FSSTRESS_ARGS=$(_scale_fsstress_args -p 4 -d $SCRATCH_MNT -n 2000 $FSSTRESS_AVOID)
- while [ "$(date +%s)" -lt $end ]; do
- $FSSTRESS_PROG $FSSTRESS_ARGS >> $seqres.full
- done
-}
-
-start=$(date +%s)
-end=$((start + (30 * TIME_FACTOR) ))
-
-echo "Loop started at $(date --date="@${start}"), ending at $(date --date="@${end}")" >> $seqres.full
-stress_loop $end &
-freeze_loop $end &
-fsmap_loop $end &
-
-# Wait until 2 seconds after the loops should have finished...
-while [ "$(date +%s)" -lt $((end + 2)) ]; do
- sleep 1
-done
-
-# ...and clean up after the loops in case they didn't do it themselves.
-$KILLALL_PROG -TERM xfs_io fsstress >> $seqres.full 2>&1
-$XFS_IO_PROG -x -c 'thaw' $SCRATCH_MNT >> $seqres.full 2>&1
-
-echo "Loop finished at $(date)" >> $seqres.full
-echo "Test done"
+_scratch_xfs_stress_scrub -f -i 'fsmap -v'
# success, all done
+echo "Silence is golden"
status=0
exit
QA output created by 517
-Format and populate
-Concurrent fsmap and freeze
-Test done
+Silence is golden
fi
_dmesg_since_test_start | tac | sed -ne "0,\#${kmsg}#p" | tac | \
- egrep -q 'Metadata corruption detected at' && hasmsg=1
+ grep -E -q 'Metadata corruption detected at' && hasmsg=1
_scratch_unmount > /dev/null 2>&1
[ $mounted -eq 0 -o $hasmsg -eq 1 ] || \
# size is and isn't a power of 2.
#
. ./common/preamble
-_begin_fstest auto quick rw realtime
+_begin_fstest auto quick insert zero collapse punch rw realtime
# Override the default cleanup function.
_cleanup()
# mapping.
. ./common/preamble
-_begin_fstest auto quick quota
+_begin_fstest auto quick quota prealloc
# Import common functions.
. ./common/filter
_scratch_mkfs_sized $((512 * 1024 * 1024)) >> $seqres.full
_scratch_mount -o uquota >> $seqres.full
+# bmap_alloc_minlen_extent only applies to the datadev space allocator, so
+# we force the filesystem not to use the realtime volume.
+_xfs_force_bdev data $SCRATCH_MNT
+
bsize=$(_get_file_block_size $SCRATCH_MNT)
echo "* Delalloc to written extent conversion"
formatted_blksz="$(_get_block_size $SCRATCH_MNT)"
test "$formatted_blksz" -ne "$dbsize" && \
_notrun "Tried to format with $dbsize blocksize, got $formatted_blksz."
-$XFS_INFO_PROG $SCRATCH_MNT | egrep -q 'realtime.*blocks=0' && \
- _notrun "Filesystem should have a realtime volume"
+_require_xfs_has_feature "$SCRATCH_MNT" realtime
echo "Consume free space"
fillerdir=$SCRATCH_MNT/fillerdir
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 FUJITSU LIMITED. All rights reserved.
+#
+# FS QA Test 533
+#
+# Regression test for xfsprogs commit
+# f4afdcb0ad11 ("xfs_db: clean up the salvage read callsites in set_cur()")
+#
+# This case test xfs_db whether can get the new magicnum field value even we
+# just have corrupted this field value.
+#
+
+. ./common/preamble
+_begin_fstest auto quick db
+
+# real QA test starts here
+_supported_fs xfs
+_fixed_by_git_commit xfsprogs f4afdcb0ad11 \
+ "xfs_db: clean up the salvage read callsites in set_cur()"
+#skip fs check because invalid superblock 1
+_require_scratch_nocheck
+
+# The error messages in the golden output come from the V5 superblock verifier
+# routines, so ignore V4 filesystems.
+_require_scratch_xfs_crc
+
+_scratch_mkfs_xfs >>$seqres.full 2>&1
+
+# write the bad magicnum field value(0) to the superblock 1
+_scratch_xfs_set_metadata_field "magicnum" "0" "sb 1"
+
+# Even magicnum field has been corrupted, we still can read this field value.
+# The error message changed in xfsprogs 5.19.
+_scratch_xfs_get_metadata_field "magicnum" "sb 1" 2>&1 | \
+ sed -e 's/Superblock has bad magic number 0x0. Not an XFS filesystem?/bad magic number/g'
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 533
+Allowing write of corrupted data with good CRC
+magicnum = 0
+bad magic number
+0
# Verify that XFS does not cause inode fork's extent count to overflow when
# writing to an unwritten extent.
. ./common/preamble
-_begin_fstest auto quick
+_begin_fstest auto quick prealloc
# Import common functions.
. ./common/filter
# Verify that XFS does not cause inode fork's extent count to overflow when
# writing to a shared extent.
. ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone unshare
# Import common functions.
. ./common/filter
# Verify that XFS does not cause inode fork's extent count to overflow when
# swapping forks between files
. ./common/preamble
-_begin_fstest auto quick
+_begin_fstest auto quick collapse swapext
# Import common functions.
. ./common/filter
_scratch_mkfs >> $seqres.full
_scratch_mount >> $seqres.full
-bsize=$(_get_block_size $SCRATCH_MNT)
+bsize=$(_get_file_block_size $SCRATCH_MNT)
srcfile=${SCRATCH_MNT}/srcfile
donorfile=${SCRATCH_MNT}/donorfile
echo "Inject bmap_alloc_minlen_extent error tag"
_scratch_inject_error bmap_alloc_minlen_extent 1
-echo "Scale fsstress args"
-args=$(_scale_fsstress_args -p $((LOAD_FACTOR * 75)) -n $((TIME_FACTOR * 1000)))
-
-echo "Execute fsstress in background"
-$FSSTRESS_PROG -d $SCRATCH_MNT $args \
- -f bulkstat=0 \
- -f bulkstat1=0 \
- -f fiemap=0 \
- -f getattr=0 \
- -f getdents=0 \
- -f getfattr=0 \
- -f listfattr=0 \
- -f mread=0 \
- -f read=0 \
- -f readlink=0 \
- -f readv=0 \
- -f stat=0 \
- -f aread=0 \
- -f dread=0 > /dev/null 2>&1
+echo "Execute fsstress"
+$FSSTRESS_PROG -d $SCRATCH_MNT \
+ $(_scale_fsstress_args -p 75 -n 1000) \
+ -f bulkstat=0 \
+ -f bulkstat1=0 \
+ -f fiemap=0 \
+ -f getattr=0 \
+ -f getdents=0 \
+ -f getfattr=0 \
+ -f listfattr=0 \
+ -f mread=0 \
+ -f read=0 \
+ -f readlink=0 \
+ -f readv=0 \
+ -f stat=0 \
+ -f aread=0 \
+ -f dread=0 >> $seqres.full
# success, all done
status=0
Consume free space
Create fragmented filesystem
Inject bmap_alloc_minlen_extent error tag
-Scale fsstress args
-Execute fsstress in background
+Execute fsstress
# the same value as during the mount
#
# Regression test for commit:
-# xfs: Skip repetitive warnings about mount options
+# 92cf7d36384b xfs: Skip repetitive warnings about mount options
. ./common/preamble
_begin_fstest auto quick mount
# Import common functions.
-_require_check_dmesg
_supported_fs xfs
+_fixed_by_kernel_commit 92cf7d36384b \
+ "xfs: Skip repetitive warnings about mount options"
+
+_require_check_dmesg
_require_scratch
log_tag()
check_dmesg_for_since_tag()
{
- dmesg_since_test_tag | egrep -q "$1"
+ dmesg_since_test_tag | grep -E -q "$1"
}
echo "Silence is golden."
# real QA test starts here
_supported_fs xfs
+_fixed_by_kernel_commit 5ca5916b6bc9 \
+ "xfs: punch out data fork delalloc blocks on COW writeback failure"
+
_require_scratch_reflink
_require_cp_reflink
_require_xfs_io_command "cowextsize"
# than the root inode. Ensure that xfsdump/xfsrestore handles this.
#
. ./common/preamble
-_begin_fstest auto quick dump
+_begin_fstest auto quick dump prealloc
# Import common functions.
. ./common/dump
_supported_fs xfs
+_require_xfs_io_command "falloc"
_require_scratch
-# A large stripe unit will put the root inode out quite far
-# due to alignment, leaving free blocks ahead of it.
-_scratch_mkfs_xfs -d sunit=1024,swidth=1024 > $seqres.full 2>&1
-
-# Mounting /without/ a stripe should allow inodes to be allocated
-# in lower free blocks, without the stripe alignment.
-_scratch_mount -o sunit=0,swidth=0
-
-root_inum=$(stat -c %i $SCRATCH_MNT)
-
-# Consume space after the root inode so that the blocks before
-# root look "close" for the next inode chunk allocation
-$XFS_IO_PROG -f -c "falloc 0 16m" $SCRATCH_MNT/fillfile
-
-# And make a bunch of inodes until we (hopefully) get one lower
-# than root, in a new inode chunk.
-echo "root_inum: $root_inum" >> $seqres.full
-for i in $(seq 0 4096) ; do
- fname=$SCRATCH_MNT/$(printf "FILE_%03d" $i)
- touch $fname
- inum=$(stat -c "%i" $fname)
- [[ $inum -lt $root_inum ]] && break
-done
-
-echo "created: $inum" >> $seqres.full
-
-[[ $inum -lt $root_inum ]] || _notrun "Could not set up test"
+# Create a filesystem which contains a fake root inode
+inums=($(_scratch_xfs_create_fake_root))
+root_inum=${inums[0]}
+fake_inum=${inums[1]}
# Now try a dump and restore. Cribbed from xfs/068
_create_dumpdir_stress
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test 547
+#
+# Verify that correct inode extent count fields are populated with and without
+# nrext64 feature.
+#
+. ./common/preamble
+_begin_fstest auto quick metadata
+
+# Import common functions.
+. ./common/filter
+. ./common/attr
+. ./common/inject
+. ./common/populate
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_nrext64
+_require_attrs
+_require_xfs_debug
+_require_xfs_db_command path
+_require_test_program "punch-alternating"
+_require_xfs_io_error_injection "bmap_alloc_minlen_extent"
+
+for nrext64 in 0 1; do
+ echo "* Verify extent counter fields with nrext64=${nrext64} option"
+
+ _scratch_mkfs -i nrext64=${nrext64} -d size=$((512 * 1024 * 1024)) \
+ >> $seqres.full
+ _scratch_mount >> $seqres.full
+
+ # Force data device extents so that we can fragment the free space
+ # and force attr fork allocations to be non-contiguous
+ _xfs_force_bdev data $SCRATCH_MNT
+
+ bsize=$(_get_file_block_size $SCRATCH_MNT)
+
+ testfile=$SCRATCH_MNT/testfile
+
+ nr_blks=20
+
+ echo "Add blocks to test file's data fork"
+ $XFS_IO_PROG -f -c "pwrite 0 $((nr_blks * bsize))" $testfile \
+ >> $seqres.full
+ $here/src/punch-alternating $testfile
+
+ echo "Consume free space"
+ fillerdir=$SCRATCH_MNT/fillerdir
+ nr_free_blks=$(stat -f -c '%f' $SCRATCH_MNT)
+ nr_free_blks=$((nr_free_blks * 90 / 100))
+
+ _fill_fs $((bsize * nr_free_blks)) $fillerdir $bsize 0 \
+ >> $seqres.full 2>&1
+
+ echo "Create fragmented filesystem"
+ for dentry in $(ls -1 $fillerdir/); do
+ $here/src/punch-alternating $fillerdir/$dentry >> $seqres.full
+ done
+
+ echo "Inject bmap_alloc_minlen_extent error tag"
+ _scratch_inject_error bmap_alloc_minlen_extent 1
+
+ echo "Add blocks to test file's attr fork"
+ attr_len=255
+ nr_attrs=$((nr_blks * bsize / attr_len))
+ for i in $(seq 1 $nr_attrs); do
+ attr="$(printf "trusted.%0247d" $i)"
+ $SETFATTR_PROG -n "$attr" $testfile >> $seqres.full 2>&1
+ [[ $? != 0 ]] && break
+ done
+
+ _scratch_unmount >> $seqres.full
+
+ dcnt=$(_scratch_xfs_get_metadata_field core.nextents \
+ "path /$(basename $testfile)")
+ acnt=$(_scratch_xfs_get_metadata_field core.naextents \
+ "path /$(basename $testfile)")
+
+ echo "nrext64: $nrext64 dcnt: $dcnt acnt: $acnt" >> $seqres.full
+
+ if [ -z "$dcnt" ] || (( $dcnt != 10 )); then
+ echo "Invalid data fork extent count: $dcnt"
+ exit 1
+ fi
+
+ if [ -z "$acnt" ] || (( $acnt < 10 )); then
+ echo "Invalid attr fork extent count: $acnt"
+ exit 1
+ fi
+done
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 547
+* Verify extent counter fields with nrext64=0 option
+Add blocks to test file's data fork
+Consume free space
+Create fragmented filesystem
+Inject bmap_alloc_minlen_extent error tag
+Add blocks to test file's attr fork
+* Verify extent counter fields with nrext64=1 option
+Add blocks to test file's data fork
+Consume free space
+Create fragmented filesystem
+Inject bmap_alloc_minlen_extent error tag
+Add blocks to test file's attr fork
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test 548
+#
+# Test to verify upgrade of an existing V5 filesystem to support large extent
+# counters.
+#
+. ./common/preamble
+_begin_fstest auto quick metadata
+
+# Import common functions.
+. ./common/filter
+. ./common/attr
+. ./common/inject
+. ./common/populate
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_nrext64
+_require_attrs
+_require_xfs_debug
+_require_xfs_db_command path
+_require_test_program "punch-alternating"
+_require_xfs_io_error_injection "bmap_alloc_minlen_extent"
+
+_scratch_mkfs -d size=$((512 * 1024 * 1024)) >> $seqres.full
+_scratch_mount >> $seqres.full
+
+bsize=$(_get_file_block_size $SCRATCH_MNT)
+
+testfile=$SCRATCH_MNT/testfile
+
+nr_blks=20
+
+echo "Add blocks to file's data fork"
+$XFS_IO_PROG -f -c "pwrite 0 $((nr_blks * bsize))" $testfile \
+ >> $seqres.full
+$here/src/punch-alternating $testfile
+
+echo "Consume free space"
+fillerdir=$SCRATCH_MNT/fillerdir
+nr_free_blks=$(stat -f -c '%f' $SCRATCH_MNT)
+nr_free_blks=$((nr_free_blks * 90 / 100))
+
+_fill_fs $((bsize * nr_free_blks)) $fillerdir $bsize 0 \
+ >> $seqres.full 2>&1
+
+echo "Create fragmented filesystem"
+for dentry in $(ls -1 $fillerdir/); do
+ $here/src/punch-alternating $fillerdir/$dentry >> $seqres.full
+done
+
+echo "Inject bmap_alloc_minlen_extent error tag"
+_scratch_inject_error bmap_alloc_minlen_extent 1
+
+echo "Add blocks to file's attr fork"
+nr_blks=10
+attr_len=255
+nr_attrs=$((nr_blks * bsize / attr_len))
+for i in $(seq 1 $nr_attrs); do
+ attr="$(printf "trusted.%0247d" $i)"
+ $SETFATTR_PROG -n "$attr" $testfile >> $seqres.full 2>&1
+ [[ $? != 0 ]] && break
+done
+
+echo "Unmount filesystem"
+_scratch_unmount >> $seqres.full
+
+orig_dcnt=$(_scratch_xfs_get_metadata_field core.nextents \
+ "path /$(basename $testfile)")
+orig_acnt=$(_scratch_xfs_get_metadata_field core.naextents \
+ "path /$(basename $testfile)")
+
+echo "Upgrade filesystem to support large extent counters"
+_scratch_xfs_admin -O nrext64=1 >> $seqres.full 2>&1
+if [[ $? != 0 ]]; then
+ _notrun "Filesystem geometry is not suitable for upgrading"
+fi
+
+
+echo "Mount filesystem"
+_scratch_mount >> $seqres.full
+
+echo "Modify inode core"
+touch $testfile
+
+echo "Unmount filesystem"
+_scratch_unmount >> $seqres.full
+
+dcnt=$(_scratch_xfs_get_metadata_field core.nextents \
+ "path /$(basename $testfile)")
+acnt=$(_scratch_xfs_get_metadata_field core.naextents \
+ "path /$(basename $testfile)")
+
+echo "Verify inode extent counter values after fs upgrade"
+
+if [[ $orig_dcnt != $dcnt ]]; then
+ echo "Corrupt data extent counter"
+ exit 1
+fi
+
+if [[ $orig_acnt != $acnt ]]; then
+ echo "Corrupt attr extent counter"
+ exit 1
+fi
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 548
+Add blocks to file's data fork
+Consume free space
+Create fragmented filesystem
+Inject bmap_alloc_minlen_extent error tag
+Add blocks to file's attr fork
+Unmount filesystem
+Upgrade filesystem to support large extent counters
+Mount filesystem
+Modify inode core
+Unmount filesystem
+Verify inode extent counter values after fs upgrade
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 FUJITSU LIMITED. All rights reserved.
+#
+# FS QA Test No. 549
+#
+# Regression test for xfsprogs commit
+# 50dba8189b1f ("mkfs: terminate getsubopt arrays properly")
+#
+# This case test mkfs.xfs whether can terminate getsubopt arrays properly.
+# If not, it will trigger segmentation fault.
+#
+
+. ./common/preamble
+_begin_fstest auto quick mkfs
+
+# real QA test starts here
+_supported_fs xfs
+_fixed_by_git_commit xfsprogs 50dba8189b1f \
+ "mkfs: terminate getsubopt arrays properly"
+_require_test
+
+$MKFS_XFS_PROG -f -d agcount=4 -d garbage=0 >> $seqres.full 2>&1
+
+echo "Silence is golden"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 549
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Fujitsu Limited. All Rights Reserved.
+#
+# FS QA Test No. 550
+#
+# Test memory failure mechanism when dax enabled
+#
+. ./common/preamble
+_begin_fstest auto quick dax
+
+# Import common functions.
+. ./common/filter
+. ./common/reflink
+
+# real QA test starts here
+_require_check_dmesg
+_require_scratch_reflink
+_require_cp_reflink
+_require_xfs_scratch_rmapbt
+_require_scratch_dax_mountopt "dax"
+_require_test_program "t_mmap_cow_memory_failure"
+
+echo "Format and mount"
+_scratch_mkfs > $seqres.full 2>&1
+_scratch_mount "-o dax" >> $seqres.full 2>&1
+
+testdir=$SCRATCH_MNT/test-$seq
+mkdir $testdir
+
+echo "Create the original files"
+filesize=65536
+_pwrite_byte 0x61 0 $filesize $testdir/testfile >> $seqres.full
+_scratch_cycle_mount "dax"
+
+echo "Inject memory failure (1 page)"
+# create two processes:
+# process1: mread 1 page to cause page fault, and wait
+# process2: mread 1 page to cause page fault, then inject poison on this page
+$here/src/t_mmap_cow_memory_failure -s1 -S1 -R $testdir/testfile -P $testdir/testfile
+
+echo "Inject memory failure (2 pages)"
+$here/src/t_mmap_cow_memory_failure -s2 -S2 -R $testdir/testfile -P $testdir/testfile
+
+_check_dmesg_for "Sending SIGBUS to t_mmap_cow_memo" || echo "Memory failure didn't kill the process"
+_check_dmesg_for "recovery action for dax page: Recovered" || echo "Failured page didn't recovered"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 550
+Format and mount
+Create the original files
+Inject memory failure (1 page)
+Inject poison...
+Process is killed by signal: 7
+Inject memory failure (2 pages)
+Inject poison...
+Process is killed by signal: 7
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Fujitsu Limited. All Rights Reserved.
+#
+# FS QA Test No. 551
+#
+# Test memory failure mechanism when dax and reflink working together
+#
+. ./common/preamble
+_begin_fstest auto quick clone dax
+
+# Import common functions.
+. ./common/filter
+. ./common/reflink
+
+# real QA test starts here
+_require_check_dmesg
+_require_scratch_reflink
+_require_cp_reflink
+_require_xfs_scratch_rmapbt
+_require_scratch_dax_mountopt "dax"
+_require_test_program "t_mmap_cow_memory_failure"
+
+echo "Format and mount"
+_scratch_mkfs > $seqres.full 2>&1
+_scratch_mount "-o dax" >> $seqres.full 2>&1
+
+testdir=$SCRATCH_MNT/test-$seq
+mkdir $testdir
+
+echo "Create the original files"
+filesize=65536
+_pwrite_byte 0x61 0 $filesize $testdir/testfile >> $seqres.full
+_cp_reflink $testdir/testfile $testdir/poisonfile >> $seqres.full
+_scratch_cycle_mount "dax"
+
+echo "Inject memory failure (1 page)"
+# create two processes:
+# process1: mread 1 page to cause page fault, and wait
+# process2: mread 1 page to cause page fault, then inject poison on this page
+$here/src/t_mmap_cow_memory_failure -s1 -S1 -R $testdir/testfile -P $testdir/poisonfile
+
+echo "Inject memory failure (2 pages)"
+$here/src/t_mmap_cow_memory_failure -s2 -S2 -R $testdir/testfile -P $testdir/poisonfile
+
+_check_dmesg_for "Sending SIGBUS to t_mmap_cow_memo" || echo "Memory failure didn't kill the process"
+_check_dmesg_for "recovery action for dax page: Recovered" || echo "Failured page didn't recovered"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 551
+Format and mount
+Create the original files
+Inject memory failure (1 page)
+Inject poison...
+Process is killed by signal: 7
+Inject memory failure (2 pages)
+Inject poison...
+Process is killed by signal: 7
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Fujitsu Limited. All Rights Reserved.
+#
+# FS QA Test No. 552
+#
+# Test memory failure mechanism when dax and reflink working together
+# test for partly reflinked file
+#
+. ./common/preamble
+_begin_fstest auto quick clone dax
+
+# Import common functions.
+. ./common/filter
+. ./common/reflink
+
+# real QA test starts here
+_require_check_dmesg
+_require_scratch_reflink
+_require_xfs_scratch_rmapbt
+_require_scratch_dax_mountopt "dax"
+_require_test_program "t_mmap_cow_memory_failure"
+
+echo "Format and mount"
+_scratch_mkfs > $seqres.full 2>&1
+_scratch_mount "-o dax" >> $seqres.full 2>&1
+
+testdir=$SCRATCH_MNT/test-$seq
+mkdir $testdir
+
+echo "Create the original files"
+nr=16
+blksz=$(_get_page_size)
+_pwrite_byte 0x61 0 $((blksz * nr)) $testdir/testfile >> $seqres.full
+_pwrite_byte 0x62 0 $((blksz * nr)) $testdir/poisonfile >> $seqres.full
+seq 0 2 $((nr - 1)) | while read i; do
+ _reflink_range $testdir/testfile $((blksz * i)) \
+ $testdir/poisonfile $((blksz * i)) $blksz >> $seqres.full
+done
+_scratch_cycle_mount "dax"
+
+echo "Inject memory failure (1 page)"
+$here/src/t_mmap_cow_memory_failure -s1 -S1 -R $testdir/testfile -P $testdir/poisonfile
+
+echo "Inject memory failure (2 pages)"
+# poison on reflinked page and not reflinked page
+$here/src/t_mmap_cow_memory_failure -s2 -S2 -R $testdir/testfile -P $testdir/poisonfile
+
+_check_dmesg_for "Sending SIGBUS to t_mmap_cow_memo" || echo "Memory failure didn't kill the process"
+_check_dmesg_for "recovery action for dax page: Recovered" || echo "Failured page didn't recovered"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 552
+Format and mount
+Create the original files
+Inject memory failure (1 page)
+Inject poison...
+Process is killed by signal: 7
+Inject memory failure (2 pages)
+Inject poison...
+Process is killed by signal: 7
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test 553
+#
+# Test to check if a direct write on a delalloc extent present in CoW fork can
+# result in an ENOSPC error.
+#
+. ./common/preamble
+_begin_fstest auto quick clone
+
+# Import common functions.
+. ./common/reflink
+. ./common/inject
+
+# real QA test starts here
+_supported_fs xfs
+_fixed_by_kernel_commit d62113303d691 \
+ "xfs: Fix false ENOSPC when performing direct write on a delalloc extent in cow fork"
+_require_scratch_reflink
+_require_xfs_debug
+_require_test_program "punch-alternating"
+_require_xfs_io_error_injection "bmap_alloc_minlen_extent"
+_require_xfs_io_command "reflink"
+_require_xfs_io_command "cowextsize"
+
+source=${SCRATCH_MNT}/source
+destination=${SCRATCH_MNT}/destination
+fragmented_file=${SCRATCH_MNT}/fragmented_file
+
+echo "Format and mount fs"
+_scratch_mkfs >> $seqres.full
+_scratch_mount >> $seqres.full
+
+blksz=$(_get_file_block_size $SCRATCH_MNT)
+
+echo "Create source file"
+$XFS_IO_PROG -f -c "pwrite 0 $((blksz * 8192))" $source >> $seqres.full
+
+echo "Reflink destination file with source file"
+$XFS_IO_PROG -f -c "reflink $source" $destination >> $seqres.full
+
+echo "Set destination file's cowextsize to 4096 blocks"
+$XFS_IO_PROG -c "cowextsize $((blksz * 4096))" $destination >> $seqres.full
+
+echo "Fragment FS"
+$XFS_IO_PROG -f -c "pwrite 0 $((blksz * 16384))" $fragmented_file \
+ >> $seqres.full
+sync
+$here/src/punch-alternating $fragmented_file >> $seqres.full
+
+echo "Inject bmap_alloc_minlen_extent error tag"
+_scratch_inject_error bmap_alloc_minlen_extent 1
+
+echo "Create delalloc extent of length 4096 blocks in destination file's CoW fork"
+$XFS_IO_PROG -c "pwrite 0 $blksz" $destination >> $seqres.full
+
+sync
+
+echo "Direct I/O write at 3rd block in destination file"
+$XFS_IO_PROG -d -c "pwrite $((blksz * 3)) $((blksz * 2))" $destination \
+ >> $seqres.full
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 553
+Format and mount fs
+Create source file
+Reflink destination file with source file
+Set destination file's cowextsize to 4096 blocks
+Fragment FS
+Inject bmap_alloc_minlen_extent error tag
+Create delalloc extent of length 4096 blocks in destination file's CoW fork
+Direct I/O write at 3rd block in destination file
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Fujitsu Limited. All Rights Reserved.
+#
+# FS QA Test No. 554
+#
+# Create a filesystem which contains an inode with a lower number
+# than the root inode. Set the lower number to a dump file as the root inode
+# and ensure that 'xfsrestore -x' handles this wrong inode.
+#
+. ./common/preamble
+_begin_fstest auto quick dump prealloc
+
+# Import common functions.
+. ./common/dump
+
+_supported_fs xfs
+_fixed_by_git_commit xfsdump \
+ "XXXXXXXXXXXX xfsrestore: fix rootdir due to xfsdump bulkstat misuse"
+_require_xfs_io_command "falloc"
+_require_scratch
+_require_xfsrestore_xflag
+
+# Create a filesystem which contains a fake root inode
+inums=($(_scratch_xfs_create_fake_root))
+root_inum=${inums[0]}
+fake_inum=${inums[1]}
+
+# Now try a dump and restore. Cribbed from xfs/068
+_create_dumpdir_stress
+
+echo -n "Before: " >> $seqres.full
+_count_dumpdir_files | tee $tmp.before >> $seqres.full
+
+_do_dump_file
+
+# Set the wrong root inode number to the dump file
+# as problematic xfsdump used to do.
+$here/src/fake-dump-rootino $dump_file $fake_inum
+
+_do_restore_file -x | \
+sed -e "s/rootino #${fake_inum}/rootino #FAKENO/g" \
+ -e "s/# to ${root_inum}/# to ROOTNO/g" \
+ -e "/entries processed$/s/[0-9][0-9]*/NUM/g"
+
+echo -n "After: " >> $seqres.full
+_count_restoredir_files | tee $tmp.after >> $seqres.full
+diff -u $tmp.before $tmp.after
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 554
+Creating directory system to dump using fsstress.
+
+-----------------------------------------------
+fsstress : -f link=10 -f creat=10 -f mkdir=10 -f truncate=5 -f symlink=10
+-----------------------------------------------
+Dumping to file...
+xfsdump -f DUMP_FILE -M stress_tape_media -L stress_554 SCRATCH_MNT
+xfsdump: using file dump (drive_simple) strategy
+xfsdump: level 0 dump of HOSTNAME:SCRATCH_MNT
+xfsdump: dump date: DATE
+xfsdump: session id: ID
+xfsdump: session label: "stress_554"
+xfsdump: ino map <PHASES>
+xfsdump: ino map construction complete
+xfsdump: estimated dump size: NUM bytes
+xfsdump: /var/xfsdump/inventory created
+xfsdump: creating dump session media file 0 (media 0, file 0)
+xfsdump: dumping ino map
+xfsdump: dumping directories
+xfsdump: dumping non-directory files
+xfsdump: ending media file
+xfsdump: media file size NUM bytes
+xfsdump: dump size (non-dir files) : NUM bytes
+xfsdump: dump complete: SECS seconds elapsed
+xfsdump: Dump Status: SUCCESS
+Restoring from file...
+xfsrestore -x -f DUMP_FILE -L stress_554 RESTORE_DIR
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: examining media file 0
+xfsrestore: reading directories
+xfsrestore: found fake rootino #FAKENO, will fix.
+xfsrestore: fix root # to ROOTNO (bind mount?)
+xfsrestore: NUM directories and NUM entries processed
+xfsrestore: directory post-processing
+xfsrestore: restoring non-directory files
+xfsrestore: restore complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Red Hat, Inc. All Rights Reserved.
+#
+# FS QA Test 555
+#
+# Corrupt xfs sb_inopblock, make sure no crash. This's a test coverage of
+# 392c6de98af1 ("xfs: sanitize sb_inopblock in xfs_mount_validate_sb")
+#
+. ./common/preamble
+_begin_fstest auto quick
+
+# real QA test starts here
+_supported_fs xfs
+_fixed_by_kernel_commit 392c6de98af1 \
+ "xfs: sanitize sb_inopblock in xfs_mount_validate_sb"
+_require_scratch
+
+_scratch_mkfs >>$seqres.full
+echo "corrupt inopblock of sb 0"
+_scratch_xfs_set_metadata_field "inopblock" "500" "sb 0" >> $seqres.full
+echo "try to mount ..."
+_try_scratch_mount 2>> $seqres.full && _fail "mount should not succeed!"
+echo "no crash or hang"
+echo "repair corrupted sb 0"
+_scratch_xfs_repair >> $seqres.full 2>&1
+echo "check fs"
+_scratch_xfs_repair -n >> $seqres.full 2>&1 || echo "fs isn't fixed!"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 555
+corrupt inopblock of sb 0
+try to mount ...
+no crash or hang
+repair corrupted sb 0
+check fs
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 556
+#
+# Check xfs_scrub's media scan can actually return diagnostic information for
+# media errors in file data extents.
+
+. ./common/preamble
+_begin_fstest auto quick scrub eio
+
+# Override the default cleanup function.
+_cleanup()
+{
+ cd /
+ rm -f $tmp.*
+ _dmerror_cleanup
+}
+
+# Import common functions.
+. ./common/fuzzy
+. ./common/filter
+. ./common/dmerror
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_scratch_xfs_crc
+_require_scrub
+_require_dm_target error
+
+filter_scrub_errors() {
+ _filter_scratch | sed \
+ -e "s/offset $((fs_blksz * 2)) /offset 2FSB /g" \
+ -e "s/length $fs_blksz.*/length 1FSB./g"
+}
+
+_scratch_mkfs >> $seqres.full
+_dmerror_init
+_dmerror_mount >> $seqres.full 2>&1
+
+_require_scratch_xfs_scrub
+
+# Write a file with 4 file blocks worth of data
+victim=$SCRATCH_MNT/a
+file_blksz=$(_get_file_block_size $SCRATCH_MNT)
+$XFS_IO_PROG -f -c "pwrite -S 0x58 0 $((4 * file_blksz))" -c "fsync" $victim >> $seqres.full
+unset errordev
+_xfs_is_realtime_file $victim && errordev="RT"
+bmap_str="$($XFS_IO_PROG -c "bmap -elpv" $victim | grep "^[[:space:]]*0:")"
+echo "$errordev:$bmap_str" >> $seqres.full
+
+phys="$(echo "$bmap_str" | $AWK_PROG '{print $3}')"
+if [ "$errordev" = "RT" ]; then
+ len="$(echo "$bmap_str" | $AWK_PROG '{print $4}')"
+else
+ len="$(echo "$bmap_str" | $AWK_PROG '{print $6}')"
+fi
+fs_blksz=$(_get_block_size $SCRATCH_MNT)
+echo "file_blksz:$file_blksz:fs_blksz:$fs_blksz" >> $seqres.full
+kernel_sectors_per_fs_block=$((fs_blksz / 512))
+
+# Did we get at least 4 fs blocks worth of extent?
+min_len_sectors=$(( 4 * kernel_sectors_per_fs_block ))
+test "$len" -lt $min_len_sectors && \
+ _fail "could not format a long enough extent on an empty fs??"
+
+phys_start=$(echo "$phys" | sed -e 's/\.\..*//g')
+
+echo "$errordev:$phys:$len:$fs_blksz:$phys_start" >> $seqres.full
+echo "victim file:" >> $seqres.full
+od -tx1 -Ad -c $victim >> $seqres.full
+
+# Set the dmerror table so that all IO will pass through.
+_dmerror_reset_table
+
+cat >> $seqres.full << ENDL
+dmerror before:
+$DMERROR_TABLE
+$DMERROR_RTTABLE
+<end table>
+ENDL
+
+# All sector numbers that we feed to the kernel must be in units of 512b, but
+# they also must be aligned to the device's logical block size.
+logical_block_size=$(_min_dio_alignment $SCRATCH_DEV)
+kernel_sectors_per_device_lba=$((logical_block_size / 512))
+
+# Mark as bad one of the device LBAs in the middle of the extent. Target the
+# second LBA of the third block of the four-block file extent that we allocated
+# earlier, but without overflowing into the fourth file block.
+bad_sector=$(( phys_start + (2 * kernel_sectors_per_fs_block) ))
+bad_len=$kernel_sectors_per_device_lba
+if (( kernel_sectors_per_device_lba < kernel_sectors_per_fs_block )); then
+ bad_sector=$((bad_sector + kernel_sectors_per_device_lba))
+fi
+if (( (bad_sector % kernel_sectors_per_device_lba) != 0)); then
+ echo "bad_sector $bad_sector not congruent with device logical block size $logical_block_size"
+fi
+_dmerror_mark_range_bad $bad_sector $bad_len $errordev
+
+cat >> $seqres.full << ENDL
+dmerror after marking bad:
+$DMERROR_TABLE
+$DMERROR_RTTABLE
+<end table>
+ENDL
+
+_dmerror_load_error_table
+
+# See if the media scan picks it up.
+echo "Scrub for injected media error (single threaded)"
+
+# Once in single-threaded mode
+_scratch_scrub -b -x >> $seqres.full 2> $tmp.error
+cat $tmp.error | filter_scrub_errors
+
+# Once in parallel mode
+echo "Scrub for injected media error (multi threaded)"
+_scratch_scrub -x >> $seqres.full 2> $tmp.error
+cat $tmp.error | filter_scrub_errors
+
+# Remount to flush the page cache and reread to see the IO error
+_dmerror_unmount
+_dmerror_mount
+echo "victim file:" >> $seqres.full
+od -tx1 -Ad -c $victim >> $seqres.full 2> $tmp.error
+cat $tmp.error | sed -e 's/read error: //g' | _filter_scratch
+
+# Scrub again to re-confirm the media error across a remount
+echo "Scrub for injected media error (after remount)"
+_scratch_scrub -x >> $seqres.full 2> $tmp.error
+cat $tmp.error | filter_scrub_errors
+
+# Now mark the bad range good so that a retest shows no media failure.
+_dmerror_mark_range_good $bad_sector $bad_len $errordev
+_dmerror_load_error_table
+
+cat >> $seqres.full << ENDL
+dmerror after marking good:
+$DMERROR_TABLE
+$DMERROR_RTTABLE
+<end table>
+ENDL
+
+echo "Scrub after removing injected media error"
+
+# Scrub one last time to make sure the error's gone.
+_scratch_scrub -x >> $seqres.full 2> $tmp.error
+cat $tmp.error | filter_scrub_errors
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 556
+Scrub for injected media error (single threaded)
+Unfixable Error: SCRATCH_MNT/a: media error at data offset 2FSB length 1FSB.
+SCRATCH_MNT: unfixable errors found: 1
+Scrub for injected media error (multi threaded)
+Unfixable Error: SCRATCH_MNT/a: media error at data offset 2FSB length 1FSB.
+SCRATCH_MNT: unfixable errors found: 1
+od: SCRATCH_MNT/a: Input/output error
+Scrub for injected media error (after remount)
+Unfixable Error: SCRATCH_MNT/a: media error at data offset 2FSB length 1FSB.
+SCRATCH_MNT: unfixable errors found: 1
+Scrub after removing injected media error
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Fujitsu Limited. All Rights Reserved.
+#
+# FS QA Test No. 557
+#
+# This is a test for:
+# bf3cb3944792 (xfs: allow single bulkstat of special inodes)
+# Create a filesystem which contains an inode with a lower number
+# than the root inode. Then verify that XFS_BULK_IREQ_SPECIAL_ROOT gets
+# the correct root inode number.
+#
+. ./common/preamble
+_begin_fstest auto quick prealloc
+
+_supported_fs xfs
+_require_xfs_io_command "falloc"
+_require_xfs_io_command "bulkstat_single"
+_require_scratch
+
+_fixed_by_kernel_commit 817644fa4525 \
+ "xfs: get root inode correctly at bulkstat"
+
+# Create a filesystem which contains a fake root inode
+inums=($(_scratch_xfs_create_fake_root))
+root_inum=${inums[0]}
+fake_inum=${inums[1]}
+
+# Get root ino with XFS_BULK_IREQ_SPECIAL_ROOT
+bulkstat_root_inum=$($XFS_IO_PROG -c 'bulkstat_single root' $SCRATCH_MNT | grep bs_ino | awk '{print $3;}')
+echo "bulkstat_root_inum: $bulkstat_root_inum" >> $seqres.full
+if [ $root_inum -ne $bulkstat_root_inum ]; then
+ echo "root ino mismatch: expected:${root_inum}, actual:${bulkstat_root_inum}"
+fi
+
+echo "Silence is golden"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 557
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 558
+#
+# This is a regression test for a data corruption bug that existed in XFS' copy
+# on write code between 4.9 and 4.19. The root cause is a concurrency bug
+# wherein we would drop ILOCK_SHARED after querying the CoW fork in xfs_map_cow
+# and retake it before querying the data fork in xfs_map_blocks. If a second
+# thread changes the CoW fork mappings between the two calls, it's possible for
+# xfs_map_blocks to return a zero-block mapping, which results in writeback
+# being elided for that block. Elided writeback of dirty data results in
+# silent loss of writes.
+#
+# Worse yet, kernels from that era still used buffer heads, which means that an
+# elided writeback leaves the page clean but the bufferheads dirty. Due to a
+# naïve optimization in mark_buffer_dirty, the SetPageDirty call is elided if
+# the bufferhead is dirty, which means that a subsequent rewrite of the data
+# block will never result in the page being marked dirty, and all subsequent
+# writes are lost.
+#
+# It turns out that Christoph Hellwig unwittingly fixed the race in commit
+# 5c665e5b5af6 ("xfs: remove xfs_map_cow"), and no testcase was ever written.
+# Four years later, we hit it on a production 4.14 kernel. This testcase
+# relies on a debugging knob that introduces artificial delays into writeback.
+#
+# Before the race, the file blocks 0-1 are not shared and blocks 2-5 are
+# shared. There are no extents in CoW fork.
+#
+# Two threads race like this:
+#
+# Thread 1 (writeback block 0) | Thread 2 (write to block 2)
+# ---------------------------------|--------------------------------
+# |
+# 1. Check if block 0 in CoW fork |
+# from xfs_map_cow. |
+# |
+# 2. Block 0 not found in CoW |
+# fork; the block is considered |
+# not shared. |
+# |
+# 3. xfs_map_blocks looks up data |
+# fork to get a map covering |
+# block 0. |
+# |
+# 4. It gets a data fork mapping |
+# for block 0 with length 2. |
+# |
+# | 1. A buffered write to block 2 sees
+# | that it is a shared block and no
+# | extent covers block 2 in CoW fork.
+# |
+# | It creates a new CoW fork mapping.
+# | Due to the cowextsize, the new
+# | extent starts at block 0 with
+# | length 128.
+# |
+# |
+# 5. It lookup CoW fork again to |
+# trim the map (0, 2) to a |
+# shared block boundary. |
+# |
+# 5a. It finds (0, 128) in CoW fork|
+# 5b. It trims the data fork map |
+# from (0, 1) to (0, 0) (!!!) |
+# |
+# 6. The xfs_imap_valid call after |
+# the xfs_map_blocks call checks|
+# if the mapping (0, 0) covers |
+# block 0. The result is "NO". |
+# |
+# 7. Since block 0 has no physical |
+# block mapped, it's not added |
+# to the ioend. This is the |
+# first problem. |
+# |
+# 8. xfs_add_to_ioend usually |
+# clears the bufferhead dirty |
+# flag Because this is skipped,|
+# we leave the page clean with |
+# the associated buffer head(s) |
+# dirty (the second problem). |
+# Now the dirty state is |
+# inconsistent.
+#
+# On newer kernels, this is also a functionality test for the ifork sequence
+# counter because the writeback completions will change the data fork and force
+# revalidations of the wb mapping.
+#
+. ./common/preamble
+_begin_fstest auto quick clone
+
+# Import common functions.
+. ./common/reflink
+. ./common/inject
+. ./common/tracing
+
+# real QA test starts here
+_cleanup()
+{
+ test -n "$sentryfile" && rm -f $sentryfile
+ wait
+ _ftrace_cleanup
+ cd /
+ rm -r -f $tmp.* $sentryfile $tracefile
+}
+
+# Modify as appropriate.
+_supported_fs xfs
+_fixed_by_kernel_commit 5c665e5b5af6 "xfs: remove xfs_map_cow"
+_require_ftrace
+_require_xfs_io_error_injection "wb_delay_ms"
+_require_scratch_reflink
+_require_cp_reflink
+
+# This test races writeback of a pure overwrite of a data fork extent against
+# the creation of a speculative COW preallocation. In alwayscow mode, there
+# are no pure overwrites, which means that a precondition of the test is not
+# satisfied, and this test should be skipped.
+_require_no_xfs_always_cow
+
+_scratch_mkfs >> $seqres.full
+_scratch_mount >> $seqres.full
+
+# This is a pagecache test, so try to disable fsdax mode.
+$XFS_IO_PROG -c 'chattr -x' $SCRATCH_MNT &> $seqres.full
+_require_pagecache_access $SCRATCH_MNT
+
+min_blksz=65536
+file_blksz=$(_get_file_block_size "$SCRATCH_MNT")
+blksz=$(( 8 * $file_blksz ))
+
+blksz=$(( blksz > min_blksz ? blksz : min_blksz ))
+
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
+
+# Make sure we have sufficient extent size to create speculative CoW
+# preallocations.
+$XFS_IO_PROG -c 'cowextsize 1m' $SCRATCH_MNT
+
+# Write out a file with the first two blocks unshared and the rest shared.
+_pwrite_byte 0x59 0 $((160 * blksz)) $SCRATCH_MNT/file >> $seqres.full
+_pwrite_byte 0x59 0 $((160 * blksz)) $SCRATCH_MNT/file.compare >> $seqres.full
+sync
+
+_cp_reflink $SCRATCH_MNT/file $SCRATCH_MNT/file.reflink
+
+_pwrite_byte 0x58 0 $((2 * blksz)) $SCRATCH_MNT/file >> $seqres.full
+_pwrite_byte 0x58 0 $((2 * blksz)) $SCRATCH_MNT/file.compare >> $seqres.full
+sync
+
+# Avoid creation of large folios on newer kernels by cycling the mount and
+# immediately writing to the page cache.
+_scratch_cycle_mount
+
+# Write the same data to file.compare as we're about to do to file. Do this
+# before slowing down writeback to avoid unnecessary delay.
+_pwrite_byte 0x57 0 $((2 * blksz)) $SCRATCH_MNT/file.compare >> $seqres.full
+_pwrite_byte 0x56 $((2 * blksz)) $((2 * blksz)) $SCRATCH_MNT/file.compare >> $seqres.full
+sync
+
+# Introduce a half-second wait to each writeback block mapping call. This
+# gives us a chance to race speculative cow prealloc with writeback.
+_scratch_inject_error "wb_delay_ms" 500
+
+_ftrace_setup
+_ftrace_record_events 'xfs_wb*iomap_invalid'
+
+# Start thread 1 + writeback above
+$XFS_IO_PROG -c "pwrite -S 0x57 0 $((2 * blksz))" \
+ -c 'fsync' $SCRATCH_MNT/file >> $seqres.full &
+sleep 1
+
+# Start a sentry to look for evidence of invalidation tracepoint tripping. If
+# we see that, we know we've forced writeback to revalidate a mapping. The
+# test has been successful, so turn off the delay.
+sentryfile=$TEST_DIR/$seq.sentry
+tracefile=$TEST_DIR/$seq.ftrace
+wait_for_errortag() {
+ while [ -e "$sentryfile" ]; do
+ _ftrace_dump | grep iomap_invalid >> "$tracefile"
+ if grep -q iomap_invalid "$tracefile"; then
+ _scratch_inject_error "wb_delay_ms" 0
+ _ftrace_ignore_events
+ break;
+ fi
+ sleep 0.5
+ done
+}
+touch $sentryfile
+wait_for_errortag &
+
+# Start thread 2 to create the cowextsize reservation
+$XFS_IO_PROG -c "pwrite -S 0x56 $((2 * blksz)) $((2 * blksz))" \
+ -c 'fsync' $SCRATCH_MNT/file >> $seqres.full
+rm -f $sentryfile
+
+cat "$tracefile" >> $seqres.full
+grep -q iomap_invalid "$tracefile"
+saw_invalidation=$?
+
+# Flush everything to disk. If the bug manifests, then after the cycle,
+# file should have stale 0x58 in block 0 because we silently dropped a write.
+_scratch_cycle_mount
+
+if ! cmp -s $SCRATCH_MNT/file $SCRATCH_MNT/file.compare; then
+ echo file and file.compare do not match
+ $XFS_IO_PROG -c 'bmap -celpv' -c 'bmap -elpv' $SCRATCH_MNT/file &>> $seqres.full
+ echo file.compare
+ od -tx1 -Ad -c $SCRATCH_MNT/file.compare
+ echo file
+ od -tx1 -Ad -c $SCRATCH_MNT/file
+elif [ $saw_invalidation -ne 0 ]; then
+ # The files matched, but nothing got logged about the revalidation?
+ echo "Expected to hear about writeback iomap invalidations?"
+fi
+
+echo Silence is golden
+status=0
+exit
--- /dev/null
+QA output created by 558
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 559
+#
+# This is a regression test for a data corruption bug that existed in iomap's
+# buffered write routines.
+#
+. ./common/preamble
+_begin_fstest auto quick rw
+
+# Import common functions.
+. ./common/inject
+. ./common/tracing
+
+# real QA test starts here
+_cleanup()
+{
+ test -n "$sentryfile" && rm -f $sentryfile
+ wait
+ _ftrace_cleanup
+ cd /
+ rm -r -f $tmp.* $sentryfile $tracefile
+}
+
+# Modify as appropriate.
+_supported_fs xfs
+_require_ftrace
+_require_xfs_io_command "falloc"
+_require_xfs_io_error_injection "write_delay_ms"
+_require_scratch
+
+_fixed_by_kernel_commit 304a68b9c63b \
+ "xfs: use iomap_valid method to detect stale cached iomaps"
+
+_scratch_mkfs >> $seqres.full
+_scratch_mount >> $seqres.full
+
+# This is a pagecache test, so try to disable fsdax mode.
+$XFS_IO_PROG -c 'chattr -x' $SCRATCH_MNT &> $seqres.full
+_require_pagecache_access $SCRATCH_MNT
+
+blocks=10
+
+# If this kernel advertises huge page support, it's possible that it could be
+# using large folios for the page cache writes. It is necessary to write
+# multiple folios (large or regular) to triggering the write invalidation,
+# so we'll scale the test write size accordingly.
+blksz=$(_get_hugepagesize)
+base_pagesize=$(_get_page_size)
+test -z "$blksz" && blksz=${base_pagesize}
+filesz=$((blocks * blksz))
+dirty_offset=$(( filesz - 1 ))
+write_len=$(( ( (blocks - 1) * blksz) + 1 ))
+
+# The write invalidation that we're testing below can only occur as part of
+# a single large write. The kernel limits writes to one base page less than
+# 2GiB to prevent lengthy IOs and integer overflows. If the block size is so
+# huge (e.g. 512M huge pages on arm64) that we'd exceed that, reduce the number
+# of blocks to get us under the limit.
+max_writesize=$((2147483647 - base_pagesize))
+if ((write_len > max_writesize)); then
+ blocks=$(( ( (max_writesize - 1) / blksz) + 1))
+ # We need at least three blocks in the file to test invalidation
+ # between writes to multiple folios. If we drop below that,
+ # reconfigure ourselves with base pages and hope for the best.
+ if ((blocks < 3)); then
+ blksz=$base_pagesize
+ blocks=10
+ fi
+ filesz=$((blocks * blksz))
+ dirty_offset=$(( filesz - 1 ))
+ write_len=$(( ( (blocks - 1) * blksz) + 1 ))
+fi
+
+# Create a large file with a large unwritten range.
+$XFS_IO_PROG -f -c "falloc 0 $filesz" $SCRATCH_MNT/file >> $seqres.full
+
+# Write the same data to file.compare as we're about to do to file. Do this
+# before slowing down writeback to avoid unnecessary delay.
+_pwrite_byte 0x58 $dirty_offset 1 $SCRATCH_MNT/file.compare >> $seqres.full
+_pwrite_byte 0x57 0 $write_len $SCRATCH_MNT/file.compare >> $seqres.full
+
+# Reinitialize the page cache for this file.
+_scratch_cycle_mount
+
+# Dirty the last page in the range and immediately set the write delay so that
+# any subsequent writes have to wait.
+$XFS_IO_PROG -c "pwrite -S 0x58 $dirty_offset 1" $SCRATCH_MNT/file >> $seqres.full
+_scratch_inject_error "write_delay_ms" 500
+
+_ftrace_setup
+_ftrace_record_events 'xfs_iomap_invalid'
+
+# Start a sentry to look for evidence of invalidation tracepoint tripping. If
+# we see that, we know we've forced writeback to revalidate a mapping. The
+# test has been successful, so turn off the delay.
+sentryfile=$TEST_DIR/$seq.sentry
+tracefile=$TEST_DIR/$seq.ftrace
+wait_for_errortag() {
+ while [ -e "$sentryfile" ]; do
+ _ftrace_dump | grep iomap_invalid >> "$tracefile"
+ if grep -q iomap_invalid "$tracefile"; then
+ _scratch_inject_error "write_delay_ms" 0
+ _ftrace_ignore_events
+ break;
+ fi
+ sleep 0.5
+ done
+}
+touch $sentryfile
+wait_for_errortag &
+
+# Start thread 1 + writeback above
+($XFS_IO_PROG -c "pwrite -S 0x57 -b $write_len 0 $write_len" \
+ $SCRATCH_MNT/file >> $seqres.full; rm -f $sentryfile) &
+sleep 1
+
+# Start thread 2 to simulate reclaim writeback via sync_file_range and fadvise
+# to drop the page cache.
+# -c "fadvise -d $dirty_offset 1" \
+dirty_pageoff=$((filesz - blksz))
+$XFS_IO_PROG -c "sync_range -a -w $dirty_pageoff $blksz" \
+ -c "mmap -r 0 $filesz" \
+ -c "madvise -d 0 $filesz" \
+ $SCRATCH_MNT/file >> $seqres.full
+wait
+rm -f $sentryfile
+
+cat "$tracefile" >> $seqres.full
+grep -q iomap_invalid "$tracefile"
+saw_invalidation=$?
+
+# Flush everything to disk. If the bug manifests, then after the cycle,
+# file should have stale 0x58 in block 0 because we silently dropped a write.
+_scratch_cycle_mount
+
+if ! cmp -s $SCRATCH_MNT/file $SCRATCH_MNT/file.compare; then
+ echo file and file.compare do not match
+ $XFS_IO_PROG -c 'bmap -celpv' -c 'bmap -elpv' $SCRATCH_MNT/file &>> $seqres.full
+ echo file.compare
+ od -tx1 -Ad -c $SCRATCH_MNT/file.compare
+ echo file
+ od -tx1 -Ad -c $SCRATCH_MNT/file
+elif [ $saw_invalidation -ne 0 ]; then
+ # The files matched, but nothing got logged about the revalidation?
+ echo "Expected to hear about write iomap invalidation?"
+fi
+
+echo Silence is golden
+status=0
+exit
--- /dev/null
+QA output created by 559
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 560
+#
+# Race GETFSMAP and ro remount for a while to see if we crash or livelock.
+#
+. ./common/preamble
+_begin_fstest auto quick fsmap remount
+
+# Override the default cleanup function.
+_cleanup()
+{
+ cd /
+ _scratch_xfs_stress_scrub_cleanup
+ rm -rf $tmp.*
+}
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_xfs_scratch_rmapbt
+_require_xfs_io_command "fsmap"
+_require_xfs_stress_scrub
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_scrub -r 5 -i 'fsmap -v'
+
+# success, all done
+echo "Silence is golden"
+status=0
+exit
--- /dev/null
+QA output created by 560
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 561
+#
+# Race xfs_scrub in check-only mode and ro remount for a while to see if we
+# crash or livelock.
+#
+. ./common/preamble
+_begin_fstest scrub dangerous_fsstress_scrub
+
+# Override the default cleanup function.
+_cleanup()
+{
+ cd /
+ _scratch_xfs_stress_scrub_cleanup &> /dev/null
+ _scratch_remount rw
+ rm -rf $tmp.*
+}
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_scrub
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_scrub -r 5 -S '-n'
+
+# success, all done
+echo "Silence is golden"
+status=0
+exit
--- /dev/null
+QA output created by 561
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 562
+#
+# Race xfs_scrub in check-only mode and freeze for a while to see if we crash
+# or livelock.
+#
+. ./common/preamble
+_begin_fstest scrub dangerous_fsstress_scrub
+
+# Override the default cleanup function.
+_cleanup()
+{
+ cd /
+ _scratch_xfs_stress_scrub_cleanup &> /dev/null
+ _scratch_remount rw
+ rm -rf $tmp.*
+}
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_scrub
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_scrub -f -S '-n'
+
+# success, all done
+echo "Silence is golden"
+status=0
+exit
--- /dev/null
+QA output created by 562
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 563
+#
+# Race xfs_scrub in force-repair mdoe and freeze for a while to see if we crash
+# or livelock.
+#
+. ./common/preamble
+_begin_fstest online_repair dangerous_fsstress_repair
+
+# Override the default cleanup function.
+_cleanup()
+{
+ cd /
+ _scratch_xfs_stress_scrub_cleanup &> /dev/null
+ _scratch_remount rw
+ rm -rf $tmp.*
+}
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/xfs
+. ./common/inject
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_online_repair
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_online_repair -f -S '-k'
+
+# success, all done
+echo "Silence is golden"
+status=0
+exit
--- /dev/null
+QA output created by 563
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 564
+#
+# Race xfs_scrub in force-repair mode and ro remount for a while to see if we
+# crash or livelock.
+#
+. ./common/preamble
+_begin_fstest online_repair dangerous_fsstress_repair
+
+# Override the default cleanup function.
+_cleanup()
+{
+ cd /
+ _scratch_xfs_stress_scrub_cleanup &> /dev/null
+ _scratch_remount rw
+ rm -rf $tmp.*
+}
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/xfs
+. ./common/inject
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_online_repair
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_online_repair -r 5 -S '-k'
+
+# success, all done
+echo "Silence is golden"
+status=0
+exit
--- /dev/null
+QA output created by 564
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle, Inc. All Rights Reserved.
+#
+# FS QA Test No. 565
+#
+# Race fsx and xfs_scrub in read-only mode for a while to see if we crash
+# or livelock.
+#
+. ./common/preamble
+_begin_fstest scrub dangerous_fsstress_scrub
+
+_cleanup() {
+ cd /
+ _scratch_xfs_stress_scrub_cleanup &> /dev/null
+ rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_scrub
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_scrub -S '-n' -X 'fsx'
+
+# success, all done
+echo Silence is golden
+status=0
+exit
--- /dev/null
+QA output created by 565
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle, Inc. All Rights Reserved.
+#
+# FS QA Test No. 566
+#
+# Race fsx and xfs_scrub in force-repair mode for a while to see if we
+# crash or livelock.
+#
+. ./common/preamble
+_begin_fstest online_repair dangerous_fsstress_repair
+
+_cleanup() {
+ cd /
+ _scratch_xfs_stress_scrub_cleanup &> /dev/null
+ rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_online_repair
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_online_repair -S '-k' -X 'fsx'
+
+# success, all done
+echo Silence is golden
+status=0
+exit
--- /dev/null
+QA output created by 566
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023 Fujitsu Limited. All Rights Reserved.
+#
+# FS QA Test No. 567
+#
+# Tests `xfsrestore -x` which handles an wrong inode in a dump, with the
+# multi-level dumps where we hit an issue during development.
+# This procedure is cribbed from:
+# xfs/064: test multilevel dump and restores with hardlinks
+
+. ./common/preamble
+_begin_fstest auto dump
+
+# Import common functions.
+. ./common/dump
+
+_supported_fs xfs
+_fixed_by_git_commit xfsdump \
+ "XXXXXXXXXXXX xfsrestore: fix rootdir due to xfsdump bulkstat misuse"
+_require_xfs_io_command "falloc"
+_require_scratch
+_require_xfsrestore_xflag
+
+# Create a filesystem which contains a fake root inode
+inums=($(_scratch_xfs_create_fake_root))
+root_inum=${inums[0]}
+fake_inum=${inums[1]}
+
+# Remove unnecessary files
+find $SCRATCH_MNT -not -inum $fake_inum -type f -delete
+# Rename a file root file to the static filename
+find $SCRATCH_MNT -inum $fake_inum -exec mv {} $SCRATCH_MNT/fakeroot \;
+
+# Override the default cleanup function.
+_cleanup()
+{
+ _cleanup_dump
+ cd /
+ rm -f $tmp.*
+}
+
+_ls_size_filter()
+{
+ #
+ # Print size ($5) and fname ($9).
+ # The size is significant since we add to the file as part
+ # of a file change for the incremental.
+ #
+ # Filter out the housekeeping files of xfsrestore
+ #
+ $AWK_PROG 'NF == 9 { print $5, $9 }' |\
+ grep -E -v 'dumpdir|housekeeping|dirattr|dirextattr|namreg|state|tree'
+}
+
+
+_create_dumpdir_hardlinks 9
+
+echo "Do the incremental dumps"
+i=0
+while [ $i -le 9 ]; do
+ if [ $i -gt 0 ]; then
+ sleep 2
+ _modify_level $i
+ fi
+
+ _stable_fs
+ sleep 2
+
+ echo "********* level $i ***********" >>$seqres.full
+ date >>$seqres.full
+ find $SCRATCH_MNT -exec $here/src/lstat64 {} \; | sed 's/(00.*)//' >$tmp.dates.$i
+ if [ $i -gt 0 ]; then
+ let level_1=$i-1
+ diff -c $tmp.dates.$level_1 $tmp.dates.$i >>$seqres.full
+ else
+ cat $tmp.dates.$i >>$seqres.full
+ fi
+
+ dumpfile=$tmp.df.level$i
+ _do_dump_file -f $dumpfile -l $i
+ # Set the wrong root inode number to the dump file
+ # as problematic xfsdump used to do.
+ $here/src/fake-dump-rootino $dumpfile $fake_inum
+
+ let i=$i+1
+done
+
+echo "Listing of what files we start with:"
+ls -l $dump_dir | _ls_size_filter
+
+echo "Look at what files are contained in the inc. dump"
+i=0
+while [ $i -le 9 ]; do
+ echo ""
+ echo "restoring from df.level$i"
+ _do_restore_toc -x -f $tmp.df.level$i | \
+ sed -e "s/rootino #${fake_inum}/rootino #FAKENO/g" \
+ -e "s/# to ${root_inum}/# to ROOTNO/g"
+ let i=$i+1
+done
+
+echo "Do the cumulative restores"
+_prepare_restore_dir
+i=0
+while [ $i -le 9 ]; do
+ if [ $i -eq 0 ]; then
+ # The root inode is fixed at the first restore
+ opt='-x'
+ else
+ opt=
+ fi
+ echo ""
+ echo "restoring from df.level$i"
+ _do_restore_file_cum $opt -f $tmp.df.level$i | \
+ sed -e "s/rootino #${fake_inum}/rootino #FAKENO/g" \
+ -e "s/# to ${root_inum}/# to ROOTNO/g"
+ echo "ls -l restore_dir"
+ ls -lR $restore_dir | _ls_size_filter | _check_quota_file
+ let i=$i+1
+done
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 567
+Creating directory system of hardlinks to incrementally dump.
+creating hardlink file1_h1 to file1
+creating hardlink file1_h2 to file1
+creating hardlink file1_h3 to file1
+creating hardlink file1_h4 to file1
+creating hardlink file1_h5 to file1
+creating hardlink file2_h1 to file2
+creating hardlink file2_h2 to file2
+creating hardlink file2_h3 to file2
+creating hardlink file2_h4 to file2
+creating hardlink file2_h5 to file2
+creating hardlink file3_h1 to file3
+creating hardlink file3_h2 to file3
+creating hardlink file3_h3 to file3
+creating hardlink file3_h4 to file3
+creating hardlink file3_h5 to file3
+creating hardlink file4_h1 to file4
+creating hardlink file4_h2 to file4
+creating hardlink file4_h3 to file4
+creating hardlink file4_h4 to file4
+creating hardlink file4_h5 to file4
+creating hardlink file5_h1 to file5
+creating hardlink file5_h2 to file5
+creating hardlink file5_h3 to file5
+creating hardlink file5_h4 to file5
+creating hardlink file5_h5 to file5
+creating hardlink file6_h1 to file6
+creating hardlink file6_h2 to file6
+creating hardlink file6_h3 to file6
+creating hardlink file6_h4 to file6
+creating hardlink file6_h5 to file6
+creating hardlink file7_h1 to file7
+creating hardlink file7_h2 to file7
+creating hardlink file7_h3 to file7
+creating hardlink file7_h4 to file7
+creating hardlink file7_h5 to file7
+creating hardlink file8_h1 to file8
+creating hardlink file8_h2 to file8
+creating hardlink file8_h3 to file8
+creating hardlink file8_h4 to file8
+creating hardlink file8_h5 to file8
+creating hardlink file9_h1 to file9
+creating hardlink file9_h2 to file9
+creating hardlink file9_h3 to file9
+creating hardlink file9_h4 to file9
+creating hardlink file9_h5 to file9
+Do the incremental dumps
+Dumping to file...
+xfsdump -l0 -f DUMP_FILE -M stress_tape_media -L stress_567 SCRATCH_MNT
+xfsdump: using file dump (drive_simple) strategy
+xfsdump: level 0 dump of HOSTNAME:SCRATCH_MNT
+xfsdump: dump date: DATE
+xfsdump: session id: ID
+xfsdump: session label: "stress_567"
+xfsdump: ino map <PHASES>
+xfsdump: ino map construction complete
+xfsdump: estimated dump size: NUM bytes
+xfsdump: /var/xfsdump/inventory created
+xfsdump: creating dump session media file 0 (media 0, file 0)
+xfsdump: dumping ino map
+xfsdump: dumping directories
+xfsdump: dumping non-directory files
+xfsdump: ending media file
+xfsdump: media file size NUM bytes
+xfsdump: dump size (non-dir files) : NUM bytes
+xfsdump: dump complete: SECS seconds elapsed
+xfsdump: Dump Status: SUCCESS
+Dumping to file...
+xfsdump -l1 -f DUMP_FILE -M stress_tape_media -L stress_567 SCRATCH_MNT
+xfsdump: using file dump (drive_simple) strategy
+xfsdump: level 1 incremental dump of HOSTNAME:SCRATCH_MNT based on level 0 dump begun DATE
+xfsdump: dump date: DATE
+xfsdump: session id: ID
+xfsdump: session label: "stress_567"
+xfsdump: ino map <PHASES>
+xfsdump: ino map construction complete
+xfsdump: estimated dump size: NUM bytes
+xfsdump: creating dump session media file 0 (media 0, file 0)
+xfsdump: dumping ino map
+xfsdump: dumping directories
+xfsdump: dumping non-directory files
+xfsdump: ending media file
+xfsdump: media file size NUM bytes
+xfsdump: dump size (non-dir files) : NUM bytes
+xfsdump: dump complete: SECS seconds elapsed
+xfsdump: Dump Status: SUCCESS
+Dumping to file...
+xfsdump -l2 -f DUMP_FILE -M stress_tape_media -L stress_567 SCRATCH_MNT
+xfsdump: using file dump (drive_simple) strategy
+xfsdump: level 2 incremental dump of HOSTNAME:SCRATCH_MNT based on level 1 dump begun DATE
+xfsdump: dump date: DATE
+xfsdump: session id: ID
+xfsdump: session label: "stress_567"
+xfsdump: ino map <PHASES>
+xfsdump: ino map construction complete
+xfsdump: estimated dump size: NUM bytes
+xfsdump: creating dump session media file 0 (media 0, file 0)
+xfsdump: dumping ino map
+xfsdump: dumping directories
+xfsdump: dumping non-directory files
+xfsdump: ending media file
+xfsdump: media file size NUM bytes
+xfsdump: dump size (non-dir files) : NUM bytes
+xfsdump: dump complete: SECS seconds elapsed
+xfsdump: Dump Status: SUCCESS
+Dumping to file...
+xfsdump -l3 -f DUMP_FILE -M stress_tape_media -L stress_567 SCRATCH_MNT
+xfsdump: using file dump (drive_simple) strategy
+xfsdump: level 3 incremental dump of HOSTNAME:SCRATCH_MNT based on level 2 dump begun DATE
+xfsdump: dump date: DATE
+xfsdump: session id: ID
+xfsdump: session label: "stress_567"
+xfsdump: ino map <PHASES>
+xfsdump: ino map construction complete
+xfsdump: estimated dump size: NUM bytes
+xfsdump: creating dump session media file 0 (media 0, file 0)
+xfsdump: dumping ino map
+xfsdump: dumping directories
+xfsdump: dumping non-directory files
+xfsdump: ending media file
+xfsdump: media file size NUM bytes
+xfsdump: dump size (non-dir files) : NUM bytes
+xfsdump: dump complete: SECS seconds elapsed
+xfsdump: Dump Status: SUCCESS
+Dumping to file...
+xfsdump -l4 -f DUMP_FILE -M stress_tape_media -L stress_567 SCRATCH_MNT
+xfsdump: using file dump (drive_simple) strategy
+xfsdump: level 4 incremental dump of HOSTNAME:SCRATCH_MNT based on level 3 dump begun DATE
+xfsdump: dump date: DATE
+xfsdump: session id: ID
+xfsdump: session label: "stress_567"
+xfsdump: ino map <PHASES>
+xfsdump: ino map construction complete
+xfsdump: estimated dump size: NUM bytes
+xfsdump: creating dump session media file 0 (media 0, file 0)
+xfsdump: dumping ino map
+xfsdump: dumping directories
+xfsdump: dumping non-directory files
+xfsdump: ending media file
+xfsdump: media file size NUM bytes
+xfsdump: dump size (non-dir files) : NUM bytes
+xfsdump: dump complete: SECS seconds elapsed
+xfsdump: Dump Status: SUCCESS
+Dumping to file...
+xfsdump -l5 -f DUMP_FILE -M stress_tape_media -L stress_567 SCRATCH_MNT
+xfsdump: using file dump (drive_simple) strategy
+xfsdump: level 5 incremental dump of HOSTNAME:SCRATCH_MNT based on level 4 dump begun DATE
+xfsdump: dump date: DATE
+xfsdump: session id: ID
+xfsdump: session label: "stress_567"
+xfsdump: ino map <PHASES>
+xfsdump: ino map construction complete
+xfsdump: estimated dump size: NUM bytes
+xfsdump: creating dump session media file 0 (media 0, file 0)
+xfsdump: dumping ino map
+xfsdump: dumping directories
+xfsdump: dumping non-directory files
+xfsdump: ending media file
+xfsdump: media file size NUM bytes
+xfsdump: dump size (non-dir files) : NUM bytes
+xfsdump: dump complete: SECS seconds elapsed
+xfsdump: Dump Status: SUCCESS
+Dumping to file...
+xfsdump -l6 -f DUMP_FILE -M stress_tape_media -L stress_567 SCRATCH_MNT
+xfsdump: using file dump (drive_simple) strategy
+xfsdump: level 6 incremental dump of HOSTNAME:SCRATCH_MNT based on level 5 dump begun DATE
+xfsdump: dump date: DATE
+xfsdump: session id: ID
+xfsdump: session label: "stress_567"
+xfsdump: ino map <PHASES>
+xfsdump: ino map construction complete
+xfsdump: estimated dump size: NUM bytes
+xfsdump: creating dump session media file 0 (media 0, file 0)
+xfsdump: dumping ino map
+xfsdump: dumping directories
+xfsdump: dumping non-directory files
+xfsdump: ending media file
+xfsdump: media file size NUM bytes
+xfsdump: dump size (non-dir files) : NUM bytes
+xfsdump: dump complete: SECS seconds elapsed
+xfsdump: Dump Status: SUCCESS
+Dumping to file...
+xfsdump -l7 -f DUMP_FILE -M stress_tape_media -L stress_567 SCRATCH_MNT
+xfsdump: using file dump (drive_simple) strategy
+xfsdump: level 7 incremental dump of HOSTNAME:SCRATCH_MNT based on level 6 dump begun DATE
+xfsdump: dump date: DATE
+xfsdump: session id: ID
+xfsdump: session label: "stress_567"
+xfsdump: ino map <PHASES>
+xfsdump: ino map construction complete
+xfsdump: estimated dump size: NUM bytes
+xfsdump: creating dump session media file 0 (media 0, file 0)
+xfsdump: dumping ino map
+xfsdump: dumping directories
+xfsdump: dumping non-directory files
+xfsdump: ending media file
+xfsdump: media file size NUM bytes
+xfsdump: dump size (non-dir files) : NUM bytes
+xfsdump: dump complete: SECS seconds elapsed
+xfsdump: Dump Status: SUCCESS
+Dumping to file...
+xfsdump -l8 -f DUMP_FILE -M stress_tape_media -L stress_567 SCRATCH_MNT
+xfsdump: using file dump (drive_simple) strategy
+xfsdump: level 8 incremental dump of HOSTNAME:SCRATCH_MNT based on level 7 dump begun DATE
+xfsdump: dump date: DATE
+xfsdump: session id: ID
+xfsdump: session label: "stress_567"
+xfsdump: ino map <PHASES>
+xfsdump: ino map construction complete
+xfsdump: estimated dump size: NUM bytes
+xfsdump: creating dump session media file 0 (media 0, file 0)
+xfsdump: dumping ino map
+xfsdump: dumping directories
+xfsdump: dumping non-directory files
+xfsdump: ending media file
+xfsdump: media file size NUM bytes
+xfsdump: dump size (non-dir files) : NUM bytes
+xfsdump: dump complete: SECS seconds elapsed
+xfsdump: Dump Status: SUCCESS
+Dumping to file...
+xfsdump -l9 -f DUMP_FILE -M stress_tape_media -L stress_567 SCRATCH_MNT
+xfsdump: using file dump (drive_simple) strategy
+xfsdump: level 9 incremental dump of HOSTNAME:SCRATCH_MNT based on level 8 dump begun DATE
+xfsdump: dump date: DATE
+xfsdump: session id: ID
+xfsdump: session label: "stress_567"
+xfsdump: ino map <PHASES>
+xfsdump: ino map construction complete
+xfsdump: estimated dump size: NUM bytes
+xfsdump: creating dump session media file 0 (media 0, file 0)
+xfsdump: dumping ino map
+xfsdump: dumping directories
+xfsdump: dumping non-directory files
+xfsdump: ending media file
+xfsdump: media file size NUM bytes
+xfsdump: dump size (non-dir files) : NUM bytes
+xfsdump: dump complete: SECS seconds elapsed
+xfsdump: Dump Status: SUCCESS
+Listing of what files we start with:
+12 file1
+12 file1_h1
+12 file1_h2
+12 file1_h3
+12 file1_h4
+12 file1_h5
+12 file2
+12 file2_h1
+12 file2_h2
+12 file2_h3
+12 file2_h4
+12 file2_h5
+12 file3
+12 file3_h1
+12 file3_h2
+12 file3_h3
+12 file3_h4
+12 file3_h5
+12 file4
+12 file4_h1
+12 file4_h2
+12 file4_h3
+12 file4_h4
+12 file4_h5
+12 file5
+12 file5_h1
+12 file5_h2
+12 file5_h3
+12 file5_h4
+12 file5_h5
+12 file6
+12 file6_h1
+12 file6_h2
+12 file6_h3
+12 file6_h4
+12 file6_h5
+12 file7
+12 file7_h1
+12 file7_h2
+12 file7_h3
+12 file7_h4
+12 file7_h5
+12 file8
+12 file8_h1
+12 file8_h2
+12 file8_h3
+12 file8_h4
+12 file8_h5
+12 file9
+12 file9_h1
+12 file9_h2
+12 file9_h3
+12 file9_h4
+12 file9_h5
+Look at what files are contained in the inc. dump
+
+restoring from df.level0
+Contents of dump ...
+xfsrestore -x -f DUMP_FILE -t
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description:
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 0
+xfsrestore: session label: "stress_567"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: found fake rootino #FAKENO, will fix.
+xfsrestore: fix root # to ROOTNO (bind mount?)
+xfsrestore: 2 directories and 56 entries processed
+xfsrestore: directory post-processing
+xfsrestore: reading non-directory files
+xfsrestore: table of contents display complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+
+dumpdir/file1
+dumpdir/file1_h1
+dumpdir/file1_h2
+dumpdir/file1_h3
+dumpdir/file1_h4
+dumpdir/file1_h5
+dumpdir/file2
+dumpdir/file2_h1
+dumpdir/file2_h2
+dumpdir/file2_h3
+dumpdir/file2_h4
+dumpdir/file2_h5
+dumpdir/file3
+dumpdir/file3_h1
+dumpdir/file3_h2
+dumpdir/file3_h3
+dumpdir/file3_h4
+dumpdir/file3_h5
+dumpdir/file4
+dumpdir/file4_h1
+dumpdir/file4_h2
+dumpdir/file4_h3
+dumpdir/file4_h4
+dumpdir/file4_h5
+dumpdir/file5
+dumpdir/file5_h1
+dumpdir/file5_h2
+dumpdir/file5_h3
+dumpdir/file5_h4
+dumpdir/file5_h5
+dumpdir/file6
+dumpdir/file6_h1
+dumpdir/file6_h2
+dumpdir/file6_h3
+dumpdir/file6_h4
+dumpdir/file6_h5
+dumpdir/file7
+dumpdir/file7_h1
+dumpdir/file7_h2
+dumpdir/file7_h3
+dumpdir/file7_h4
+dumpdir/file7_h5
+dumpdir/file8
+dumpdir/file8_h1
+dumpdir/file8_h2
+dumpdir/file8_h3
+dumpdir/file8_h4
+dumpdir/file8_h5
+dumpdir/file9
+dumpdir/file9_h1
+dumpdir/file9_h2
+dumpdir/file9_h3
+dumpdir/file9_h4
+dumpdir/file9_h5
+fakeroot
+
+restoring from df.level1
+Contents of dump ...
+xfsrestore -x -f DUMP_FILE -t
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description:
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 1
+xfsrestore: session label: "stress_567"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: found fake rootino #FAKENO, will fix.
+xfsrestore: fix root # to ROOTNO (bind mount?)
+xfsrestore: 2 directories and 56 entries processed
+xfsrestore: directory post-processing
+xfsrestore: reading non-directory files
+xfsrestore: table of contents display complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+
+dumpdir/file1
+dumpdir/file1_h1
+dumpdir/file1_h2
+dumpdir/file1_h3
+dumpdir/file1_h4
+dumpdir/file1_h5
+
+restoring from df.level2
+Contents of dump ...
+xfsrestore -x -f DUMP_FILE -t
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description:
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 2
+xfsrestore: session label: "stress_567"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: found fake rootino #FAKENO, will fix.
+xfsrestore: fix root # to ROOTNO (bind mount?)
+xfsrestore: 2 directories and 56 entries processed
+xfsrestore: directory post-processing
+xfsrestore: reading non-directory files
+xfsrestore: table of contents display complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+
+dumpdir/file2
+dumpdir/file2_h1
+dumpdir/file2_h2
+dumpdir/file2_h3
+dumpdir/file2_h4
+dumpdir/file2_h5
+
+restoring from df.level3
+Contents of dump ...
+xfsrestore -x -f DUMP_FILE -t
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description:
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 3
+xfsrestore: session label: "stress_567"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: found fake rootino #FAKENO, will fix.
+xfsrestore: fix root # to ROOTNO (bind mount?)
+xfsrestore: 2 directories and 56 entries processed
+xfsrestore: directory post-processing
+xfsrestore: reading non-directory files
+xfsrestore: table of contents display complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+
+dumpdir/file3
+dumpdir/file3_h1
+dumpdir/file3_h2
+dumpdir/file3_h3
+dumpdir/file3_h4
+dumpdir/file3_h5
+
+restoring from df.level4
+Contents of dump ...
+xfsrestore -x -f DUMP_FILE -t
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description:
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 4
+xfsrestore: session label: "stress_567"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: found fake rootino #FAKENO, will fix.
+xfsrestore: fix root # to ROOTNO (bind mount?)
+xfsrestore: 2 directories and 56 entries processed
+xfsrestore: directory post-processing
+xfsrestore: reading non-directory files
+xfsrestore: table of contents display complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+
+dumpdir/file4
+dumpdir/file4_h1
+dumpdir/file4_h2
+dumpdir/file4_h3
+dumpdir/file4_h4
+dumpdir/file4_h5
+
+restoring from df.level5
+Contents of dump ...
+xfsrestore -x -f DUMP_FILE -t
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description:
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 5
+xfsrestore: session label: "stress_567"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: found fake rootino #FAKENO, will fix.
+xfsrestore: fix root # to ROOTNO (bind mount?)
+xfsrestore: 2 directories and 56 entries processed
+xfsrestore: directory post-processing
+xfsrestore: reading non-directory files
+xfsrestore: table of contents display complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+
+dumpdir/file5
+dumpdir/file5_h1
+dumpdir/file5_h2
+dumpdir/file5_h3
+dumpdir/file5_h4
+dumpdir/file5_h5
+
+restoring from df.level6
+Contents of dump ...
+xfsrestore -x -f DUMP_FILE -t
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description:
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 6
+xfsrestore: session label: "stress_567"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: found fake rootino #FAKENO, will fix.
+xfsrestore: fix root # to ROOTNO (bind mount?)
+xfsrestore: 2 directories and 56 entries processed
+xfsrestore: directory post-processing
+xfsrestore: reading non-directory files
+xfsrestore: table of contents display complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+
+dumpdir/file6
+dumpdir/file6_h1
+dumpdir/file6_h2
+dumpdir/file6_h3
+dumpdir/file6_h4
+dumpdir/file6_h5
+
+restoring from df.level7
+Contents of dump ...
+xfsrestore -x -f DUMP_FILE -t
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description:
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 7
+xfsrestore: session label: "stress_567"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: found fake rootino #FAKENO, will fix.
+xfsrestore: fix root # to ROOTNO (bind mount?)
+xfsrestore: 2 directories and 56 entries processed
+xfsrestore: directory post-processing
+xfsrestore: reading non-directory files
+xfsrestore: table of contents display complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+
+dumpdir/file7
+dumpdir/file7_h1
+dumpdir/file7_h2
+dumpdir/file7_h3
+dumpdir/file7_h4
+dumpdir/file7_h5
+
+restoring from df.level8
+Contents of dump ...
+xfsrestore -x -f DUMP_FILE -t
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description:
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 8
+xfsrestore: session label: "stress_567"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: found fake rootino #FAKENO, will fix.
+xfsrestore: fix root # to ROOTNO (bind mount?)
+xfsrestore: 2 directories and 56 entries processed
+xfsrestore: directory post-processing
+xfsrestore: reading non-directory files
+xfsrestore: table of contents display complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+
+dumpdir/file8
+dumpdir/file8_h1
+dumpdir/file8_h2
+dumpdir/file8_h3
+dumpdir/file8_h4
+dumpdir/file8_h5
+
+restoring from df.level9
+Contents of dump ...
+xfsrestore -x -f DUMP_FILE -t
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description:
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 9
+xfsrestore: session label: "stress_567"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: found fake rootino #FAKENO, will fix.
+xfsrestore: fix root # to ROOTNO (bind mount?)
+xfsrestore: 2 directories and 56 entries processed
+xfsrestore: directory post-processing
+xfsrestore: reading non-directory files
+xfsrestore: table of contents display complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+
+dumpdir/file9
+dumpdir/file9_h1
+dumpdir/file9_h2
+dumpdir/file9_h3
+dumpdir/file9_h4
+dumpdir/file9_h5
+Do the cumulative restores
+
+restoring from df.level0
+Restoring cumumlative from file...
+xfsrestore -x -f DUMP_FILE -r RESTORE_DIR
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description:
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 0
+xfsrestore: session label: "stress_567"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: found fake rootino #FAKENO, will fix.
+xfsrestore: fix root # to ROOTNO (bind mount?)
+xfsrestore: 2 directories and 56 entries processed
+xfsrestore: directory post-processing
+xfsrestore: restoring non-directory files
+xfsrestore: restore complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+ls -l restore_dir
+0 fakeroot
+0 file1
+0 file1_h1
+0 file1_h2
+0 file1_h3
+0 file1_h4
+0 file1_h5
+0 file2
+0 file2_h1
+0 file2_h2
+0 file2_h3
+0 file2_h4
+0 file2_h5
+0 file3
+0 file3_h1
+0 file3_h2
+0 file3_h3
+0 file3_h4
+0 file3_h5
+0 file4
+0 file4_h1
+0 file4_h2
+0 file4_h3
+0 file4_h4
+0 file4_h5
+0 file5
+0 file5_h1
+0 file5_h2
+0 file5_h3
+0 file5_h4
+0 file5_h5
+0 file6
+0 file6_h1
+0 file6_h2
+0 file6_h3
+0 file6_h4
+0 file6_h5
+0 file7
+0 file7_h1
+0 file7_h2
+0 file7_h3
+0 file7_h4
+0 file7_h5
+0 file8
+0 file8_h1
+0 file8_h2
+0 file8_h3
+0 file8_h4
+0 file8_h5
+0 file9
+0 file9_h1
+0 file9_h2
+0 file9_h3
+0 file9_h4
+0 file9_h5
+
+restoring from df.level1
+Restoring cumumlative from file...
+xfsrestore -f DUMP_FILE -r RESTORE_DIR
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description:
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 1
+xfsrestore: session label: "stress_567"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: 2 directories and 56 entries processed
+xfsrestore: directory post-processing
+xfsrestore: restoring non-directory files
+xfsrestore: restore complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+ls -l restore_dir
+0 fakeroot
+12 file1
+12 file1_h1
+12 file1_h2
+12 file1_h3
+12 file1_h4
+12 file1_h5
+0 file2
+0 file2_h1
+0 file2_h2
+0 file2_h3
+0 file2_h4
+0 file2_h5
+0 file3
+0 file3_h1
+0 file3_h2
+0 file3_h3
+0 file3_h4
+0 file3_h5
+0 file4
+0 file4_h1
+0 file4_h2
+0 file4_h3
+0 file4_h4
+0 file4_h5
+0 file5
+0 file5_h1
+0 file5_h2
+0 file5_h3
+0 file5_h4
+0 file5_h5
+0 file6
+0 file6_h1
+0 file6_h2
+0 file6_h3
+0 file6_h4
+0 file6_h5
+0 file7
+0 file7_h1
+0 file7_h2
+0 file7_h3
+0 file7_h4
+0 file7_h5
+0 file8
+0 file8_h1
+0 file8_h2
+0 file8_h3
+0 file8_h4
+0 file8_h5
+0 file9
+0 file9_h1
+0 file9_h2
+0 file9_h3
+0 file9_h4
+0 file9_h5
+
+restoring from df.level2
+Restoring cumumlative from file...
+xfsrestore -f DUMP_FILE -r RESTORE_DIR
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description:
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 2
+xfsrestore: session label: "stress_567"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: 2 directories and 56 entries processed
+xfsrestore: directory post-processing
+xfsrestore: restoring non-directory files
+xfsrestore: restore complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+ls -l restore_dir
+0 fakeroot
+12 file1
+12 file1_h1
+12 file1_h2
+12 file1_h3
+12 file1_h4
+12 file1_h5
+12 file2
+12 file2_h1
+12 file2_h2
+12 file2_h3
+12 file2_h4
+12 file2_h5
+0 file3
+0 file3_h1
+0 file3_h2
+0 file3_h3
+0 file3_h4
+0 file3_h5
+0 file4
+0 file4_h1
+0 file4_h2
+0 file4_h3
+0 file4_h4
+0 file4_h5
+0 file5
+0 file5_h1
+0 file5_h2
+0 file5_h3
+0 file5_h4
+0 file5_h5
+0 file6
+0 file6_h1
+0 file6_h2
+0 file6_h3
+0 file6_h4
+0 file6_h5
+0 file7
+0 file7_h1
+0 file7_h2
+0 file7_h3
+0 file7_h4
+0 file7_h5
+0 file8
+0 file8_h1
+0 file8_h2
+0 file8_h3
+0 file8_h4
+0 file8_h5
+0 file9
+0 file9_h1
+0 file9_h2
+0 file9_h3
+0 file9_h4
+0 file9_h5
+
+restoring from df.level3
+Restoring cumumlative from file...
+xfsrestore -f DUMP_FILE -r RESTORE_DIR
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description:
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 3
+xfsrestore: session label: "stress_567"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: 2 directories and 56 entries processed
+xfsrestore: directory post-processing
+xfsrestore: restoring non-directory files
+xfsrestore: restore complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+ls -l restore_dir
+0 fakeroot
+12 file1
+12 file1_h1
+12 file1_h2
+12 file1_h3
+12 file1_h4
+12 file1_h5
+12 file2
+12 file2_h1
+12 file2_h2
+12 file2_h3
+12 file2_h4
+12 file2_h5
+12 file3
+12 file3_h1
+12 file3_h2
+12 file3_h3
+12 file3_h4
+12 file3_h5
+0 file4
+0 file4_h1
+0 file4_h2
+0 file4_h3
+0 file4_h4
+0 file4_h5
+0 file5
+0 file5_h1
+0 file5_h2
+0 file5_h3
+0 file5_h4
+0 file5_h5
+0 file6
+0 file6_h1
+0 file6_h2
+0 file6_h3
+0 file6_h4
+0 file6_h5
+0 file7
+0 file7_h1
+0 file7_h2
+0 file7_h3
+0 file7_h4
+0 file7_h5
+0 file8
+0 file8_h1
+0 file8_h2
+0 file8_h3
+0 file8_h4
+0 file8_h5
+0 file9
+0 file9_h1
+0 file9_h2
+0 file9_h3
+0 file9_h4
+0 file9_h5
+
+restoring from df.level4
+Restoring cumumlative from file...
+xfsrestore -f DUMP_FILE -r RESTORE_DIR
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description:
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 4
+xfsrestore: session label: "stress_567"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: 2 directories and 56 entries processed
+xfsrestore: directory post-processing
+xfsrestore: restoring non-directory files
+xfsrestore: restore complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+ls -l restore_dir
+0 fakeroot
+12 file1
+12 file1_h1
+12 file1_h2
+12 file1_h3
+12 file1_h4
+12 file1_h5
+12 file2
+12 file2_h1
+12 file2_h2
+12 file2_h3
+12 file2_h4
+12 file2_h5
+12 file3
+12 file3_h1
+12 file3_h2
+12 file3_h3
+12 file3_h4
+12 file3_h5
+12 file4
+12 file4_h1
+12 file4_h2
+12 file4_h3
+12 file4_h4
+12 file4_h5
+0 file5
+0 file5_h1
+0 file5_h2
+0 file5_h3
+0 file5_h4
+0 file5_h5
+0 file6
+0 file6_h1
+0 file6_h2
+0 file6_h3
+0 file6_h4
+0 file6_h5
+0 file7
+0 file7_h1
+0 file7_h2
+0 file7_h3
+0 file7_h4
+0 file7_h5
+0 file8
+0 file8_h1
+0 file8_h2
+0 file8_h3
+0 file8_h4
+0 file8_h5
+0 file9
+0 file9_h1
+0 file9_h2
+0 file9_h3
+0 file9_h4
+0 file9_h5
+
+restoring from df.level5
+Restoring cumumlative from file...
+xfsrestore -f DUMP_FILE -r RESTORE_DIR
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description:
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 5
+xfsrestore: session label: "stress_567"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: 2 directories and 56 entries processed
+xfsrestore: directory post-processing
+xfsrestore: restoring non-directory files
+xfsrestore: restore complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+ls -l restore_dir
+0 fakeroot
+12 file1
+12 file1_h1
+12 file1_h2
+12 file1_h3
+12 file1_h4
+12 file1_h5
+12 file2
+12 file2_h1
+12 file2_h2
+12 file2_h3
+12 file2_h4
+12 file2_h5
+12 file3
+12 file3_h1
+12 file3_h2
+12 file3_h3
+12 file3_h4
+12 file3_h5
+12 file4
+12 file4_h1
+12 file4_h2
+12 file4_h3
+12 file4_h4
+12 file4_h5
+12 file5
+12 file5_h1
+12 file5_h2
+12 file5_h3
+12 file5_h4
+12 file5_h5
+0 file6
+0 file6_h1
+0 file6_h2
+0 file6_h3
+0 file6_h4
+0 file6_h5
+0 file7
+0 file7_h1
+0 file7_h2
+0 file7_h3
+0 file7_h4
+0 file7_h5
+0 file8
+0 file8_h1
+0 file8_h2
+0 file8_h3
+0 file8_h4
+0 file8_h5
+0 file9
+0 file9_h1
+0 file9_h2
+0 file9_h3
+0 file9_h4
+0 file9_h5
+
+restoring from df.level6
+Restoring cumumlative from file...
+xfsrestore -f DUMP_FILE -r RESTORE_DIR
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description:
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 6
+xfsrestore: session label: "stress_567"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: 2 directories and 56 entries processed
+xfsrestore: directory post-processing
+xfsrestore: restoring non-directory files
+xfsrestore: restore complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+ls -l restore_dir
+0 fakeroot
+12 file1
+12 file1_h1
+12 file1_h2
+12 file1_h3
+12 file1_h4
+12 file1_h5
+12 file2
+12 file2_h1
+12 file2_h2
+12 file2_h3
+12 file2_h4
+12 file2_h5
+12 file3
+12 file3_h1
+12 file3_h2
+12 file3_h3
+12 file3_h4
+12 file3_h5
+12 file4
+12 file4_h1
+12 file4_h2
+12 file4_h3
+12 file4_h4
+12 file4_h5
+12 file5
+12 file5_h1
+12 file5_h2
+12 file5_h3
+12 file5_h4
+12 file5_h5
+12 file6
+12 file6_h1
+12 file6_h2
+12 file6_h3
+12 file6_h4
+12 file6_h5
+0 file7
+0 file7_h1
+0 file7_h2
+0 file7_h3
+0 file7_h4
+0 file7_h5
+0 file8
+0 file8_h1
+0 file8_h2
+0 file8_h3
+0 file8_h4
+0 file8_h5
+0 file9
+0 file9_h1
+0 file9_h2
+0 file9_h3
+0 file9_h4
+0 file9_h5
+
+restoring from df.level7
+Restoring cumumlative from file...
+xfsrestore -f DUMP_FILE -r RESTORE_DIR
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description:
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 7
+xfsrestore: session label: "stress_567"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: 2 directories and 56 entries processed
+xfsrestore: directory post-processing
+xfsrestore: restoring non-directory files
+xfsrestore: restore complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+ls -l restore_dir
+0 fakeroot
+12 file1
+12 file1_h1
+12 file1_h2
+12 file1_h3
+12 file1_h4
+12 file1_h5
+12 file2
+12 file2_h1
+12 file2_h2
+12 file2_h3
+12 file2_h4
+12 file2_h5
+12 file3
+12 file3_h1
+12 file3_h2
+12 file3_h3
+12 file3_h4
+12 file3_h5
+12 file4
+12 file4_h1
+12 file4_h2
+12 file4_h3
+12 file4_h4
+12 file4_h5
+12 file5
+12 file5_h1
+12 file5_h2
+12 file5_h3
+12 file5_h4
+12 file5_h5
+12 file6
+12 file6_h1
+12 file6_h2
+12 file6_h3
+12 file6_h4
+12 file6_h5
+12 file7
+12 file7_h1
+12 file7_h2
+12 file7_h3
+12 file7_h4
+12 file7_h5
+0 file8
+0 file8_h1
+0 file8_h2
+0 file8_h3
+0 file8_h4
+0 file8_h5
+0 file9
+0 file9_h1
+0 file9_h2
+0 file9_h3
+0 file9_h4
+0 file9_h5
+
+restoring from df.level8
+Restoring cumumlative from file...
+xfsrestore -f DUMP_FILE -r RESTORE_DIR
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description:
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 8
+xfsrestore: session label: "stress_567"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: 2 directories and 56 entries processed
+xfsrestore: directory post-processing
+xfsrestore: restoring non-directory files
+xfsrestore: restore complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+ls -l restore_dir
+0 fakeroot
+12 file1
+12 file1_h1
+12 file1_h2
+12 file1_h3
+12 file1_h4
+12 file1_h5
+12 file2
+12 file2_h1
+12 file2_h2
+12 file2_h3
+12 file2_h4
+12 file2_h5
+12 file3
+12 file3_h1
+12 file3_h2
+12 file3_h3
+12 file3_h4
+12 file3_h5
+12 file4
+12 file4_h1
+12 file4_h2
+12 file4_h3
+12 file4_h4
+12 file4_h5
+12 file5
+12 file5_h1
+12 file5_h2
+12 file5_h3
+12 file5_h4
+12 file5_h5
+12 file6
+12 file6_h1
+12 file6_h2
+12 file6_h3
+12 file6_h4
+12 file6_h5
+12 file7
+12 file7_h1
+12 file7_h2
+12 file7_h3
+12 file7_h4
+12 file7_h5
+12 file8
+12 file8_h1
+12 file8_h2
+12 file8_h3
+12 file8_h4
+12 file8_h5
+0 file9
+0 file9_h1
+0 file9_h2
+0 file9_h3
+0 file9_h4
+0 file9_h5
+
+restoring from df.level9
+Restoring cumumlative from file...
+xfsrestore -f DUMP_FILE -r RESTORE_DIR
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description:
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 9
+xfsrestore: session label: "stress_567"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: 2 directories and 56 entries processed
+xfsrestore: directory post-processing
+xfsrestore: restoring non-directory files
+xfsrestore: restore complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+ls -l restore_dir
+0 fakeroot
+12 file1
+12 file1_h1
+12 file1_h2
+12 file1_h3
+12 file1_h4
+12 file1_h5
+12 file2
+12 file2_h1
+12 file2_h2
+12 file2_h3
+12 file2_h4
+12 file2_h5
+12 file3
+12 file3_h1
+12 file3_h2
+12 file3_h3
+12 file3_h4
+12 file3_h5
+12 file4
+12 file4_h1
+12 file4_h2
+12 file4_h3
+12 file4_h4
+12 file4_h5
+12 file5
+12 file5_h1
+12 file5_h2
+12 file5_h3
+12 file5_h4
+12 file5_h5
+12 file6
+12 file6_h1
+12 file6_h2
+12 file6_h3
+12 file6_h4
+12 file6_h5
+12 file7
+12 file7_h1
+12 file7_h2
+12 file7_h3
+12 file7_h4
+12 file7_h5
+12 file8
+12 file8_h1
+12 file8_h2
+12 file8_h3
+12 file8_h4
+12 file8_h5
+12 file9
+12 file9_h1
+12 file9_h2
+12 file9_h3
+12 file9_h4
+12 file9_h5
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023 Fujitsu Limited. All Rights Reserved.
+#
+# FS QA Test No. 568
+#
+# Tests `xfsrestore -x` which handles an wrong inode in a dump, with the
+# multi-level dumps where we hit an issue during development.
+# This procedure is cribbed from:
+# xfs/065: Testing incremental dumps and cumulative restores with
+# different operations for each level
+
+. ./common/preamble
+_begin_fstest auto dump
+
+# Override the default cleanup function.
+_cleanup()
+{
+ _cleanup_dump
+ cd /
+ rm -f $tmp.*
+}
+
+# Import common functions.
+. ./common/filter
+. ./common/dump
+. ./common/quota
+
+#
+# list recursively the directory
+#
+# e.g. lstat output: src/lstat64 31056 -rwxr-xr-x 38403,0
+# Don't print out sizes of directories - which can vary - overwrite with XXX.
+#
+_list_dir()
+{
+ __dir=$1
+ find $__dir -exec $here/src/lstat64 -t {} \; |\
+ sed -e 's/.*dumpdir/dumpdir/' -e '/^dumpdir /d' |\
+ sed -e 's/.*restoredir/restoredir/' -e '/^restoredir /d' |\
+ grep -E -v 'housekeeping|dirattr|dirextattr|namreg|state|tree|fakeroot' |\
+ awk '$3 ~ /^d/ { $2 = "XXX" } {print}' |\
+ LC_COLLATE=POSIX sort
+}
+
+# real QA test starts here
+_supported_fs xfs
+_fixed_by_git_commit xfsdump \
+ "XXXXXXXXXXXX xfsrestore: fix rootdir due to xfsdump bulkstat misuse"
+_require_xfs_io_command "falloc"
+_require_scratch
+_require_xfsrestore_xflag
+
+#
+# too much hassle to get output matching with quotas turned on
+# so don't run it
+#
+_scratch_mkfs_xfs >> $seqres.full
+_qmount_option noquota
+_scratch_mount
+$here/src/feature -U $SCRATCH_DEV && \
+ _notrun "UQuota enabled, test needs controlled xfsdump output"
+$here/src/feature -G $SCRATCH_DEV && \
+ _notrun "GQuota enabled, test needs controlled xfsdump output"
+$here/src/feature -P $SCRATCH_DEV && \
+ _notrun "PQuota enabled, test needs controlled xfsdump output"
+_scratch_unmount
+
+#
+# adding - touch/echo, mkdir
+# deleting - rm, rmdir
+# renaming - mv
+# linking - ln
+# unlinking - rm
+# files and directories
+#
+
+# Create a filesystem which contains a fake root inode
+inums=($(_scratch_xfs_create_fake_root))
+root_inum=${inums[0]}
+fake_inum=${inums[1]}
+
+# Remove unnecessary files
+find $SCRATCH_MNT -not -inum $fake_inum -type f -delete
+# Rename a file root file to the static filename
+find $SCRATCH_MNT -inum $fake_inum -exec mv {} $SCRATCH_MNT/fakeroot \;
+
+mkdir -p $dump_dir || _fail "cannot mkdir \"$dump_dir\""
+cd $dump_dir
+
+echo "Do the incremental dumps"
+i=0
+num_dumps=8 # do some extra to ensure nothing changes
+while [ $i -le $num_dumps ]; do
+ cd $dump_dir
+ case $i in
+ 0)
+ # adding
+ echo 'add0' >addedfile0
+ echo 'add1' >addedfile1
+ echo 'add2' >addedfile2
+ echo 'add3' >addedfile3
+ mkdir addeddir1
+ mkdir addeddir2
+ mkdir addeddir3
+ mkdir addeddir4
+ echo 'add4' >addeddir3/addedfile4
+ echo 'add5' >addeddir4/addedfile5
+ ;;
+ 1)
+ # deleting
+ rm addedfile2
+ rmdir addeddir2
+ rm -rf addeddir3
+ ;;
+ 2)
+ # renaming
+ mv addedfile1 addedfile2 # rename to previous existing file
+ mv addeddir4/addedfile5 addeddir4/addedfile4
+ mv addeddir4 addeddir6
+ mv addeddir1 addeddir2 # rename to previous existing dir
+ ;;
+ 3)
+ # linking
+ ln addedfile0 linkfile0
+ ln addedfile0 linkfile0_1 # have a 2nd link to file
+ ln addedfile2 linkfile2
+ ln addeddir6/addedfile4 linkfile64
+ ;;
+ 4)
+ # unlinking
+ rm linkfile0 # remove a link
+ rm addedfile2 # remove original link
+ rm linkfile64 # remove link
+ rm addeddir6/addedfile4 # remove last link
+ ;;
+ 5) # link first - then onto 6)
+ rm -rf *
+ echo 'add6' >addedfile6
+ ln addedfile6 linkfile6_1
+ ln addedfile6 linkfile6_2
+ ln addedfile6 linkfile6_3
+ ;;
+ 6) # then move the inode that the links point to
+ mv addedfile6 addedfile6_mv
+ rm linkfile6_1
+ rm linkfile6_2
+ rm linkfile6_3
+ ln addedfile6_mv linkfile6_mv_1
+ ln addedfile6_mv linkfile6_mv_2
+ ln addedfile6_mv linkfile6_mv_3
+ ;;
+ esac
+ cd $here
+ sleep 2
+ _stable_fs
+
+ echo "Listing of what files we have at level $i:"
+ _list_dir $dump_dir | tee $tmp.ls.$i
+
+ dumpfile=$tmp.df.level$i
+ _do_dump_file -f $dumpfile -l $i
+ # Set the wrong root inode number to the dump file
+ # as problematic xfsdump used to do.
+ $here/src/fake-dump-rootino $dumpfile $fake_inum
+
+ let i=$i+1
+done
+
+echo "Look at what files are contained in the inc. dump"
+i=0
+while [ $i -le $num_dumps ]; do
+ echo ""
+ echo "restoring from df.level$i"
+ _do_restore_toc -x -f $tmp.df.level$i | \
+ sed -e "s/rootino #${fake_inum}/rootino #FAKENO/g" \
+ -e "s/# to ${root_inum}/# to ROOTNO/g"
+ let i=$i+1
+done
+
+echo "Do the cumulative restores"
+_prepare_restore_dir
+i=0
+while [ $i -le $num_dumps ]; do
+ if [ $i -eq 0 ]; then
+ # The root inode is fixed at the first restore
+ opt='-x'
+ else
+ opt=
+ fi
+ echo ""
+ echo "restoring from df.level$i"
+ _do_restore_file_cum $opt -f $tmp.df.level$i | \
+ sed -e "s/rootino #${fake_inum}/rootino #FAKENO/g" \
+ -e "s/# to ${root_inum}/# to ROOTNO/g"
+ echo "list restore_dir"
+ _list_dir $restore_dir | _check_quota_file | tee $tmp.restorals.$i
+ let i=$i+1
+done
+
+echo ""
+echo "Do the ls comparison"
+i=0
+while [ $i -le $num_dumps ]; do
+ echo "Comparing ls of FS with restored FS at level $i"
+ diff -s $tmp.ls.$i $tmp.restorals.$i | sed "s#$tmp#TMP#g"
+ echo ""
+ let i=$i+1
+done
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 568
+Do the incremental dumps
+Listing of what files we have at level 0:
+dumpdir/addeddir1 XXX drwxr-xr-x 0,0
+dumpdir/addeddir2 XXX drwxr-xr-x 0,0
+dumpdir/addeddir3 XXX drwxr-xr-x 0,0
+dumpdir/addeddir3/addedfile4 5 -rw-r--r-- 0,0
+dumpdir/addeddir4 XXX drwxr-xr-x 0,0
+dumpdir/addeddir4/addedfile5 5 -rw-r--r-- 0,0
+dumpdir/addedfile0 5 -rw-r--r-- 0,0
+dumpdir/addedfile1 5 -rw-r--r-- 0,0
+dumpdir/addedfile2 5 -rw-r--r-- 0,0
+dumpdir/addedfile3 5 -rw-r--r-- 0,0
+Dumping to file...
+xfsdump -l0 -f DUMP_FILE -M stress_tape_media -L stress_568 SCRATCH_MNT
+xfsdump: using file dump (drive_simple) strategy
+xfsdump: level 0 dump of HOSTNAME:SCRATCH_MNT
+xfsdump: dump date: DATE
+xfsdump: session id: ID
+xfsdump: session label: "stress_568"
+xfsdump: ino map <PHASES>
+xfsdump: ino map construction complete
+xfsdump: estimated dump size: NUM bytes
+xfsdump: /var/xfsdump/inventory created
+xfsdump: creating dump session media file 0 (media 0, file 0)
+xfsdump: dumping ino map
+xfsdump: dumping directories
+xfsdump: dumping non-directory files
+xfsdump: ending media file
+xfsdump: media file size NUM bytes
+xfsdump: dump size (non-dir files) : NUM bytes
+xfsdump: dump complete: SECS seconds elapsed
+xfsdump: Dump Status: SUCCESS
+Listing of what files we have at level 1:
+dumpdir/addeddir1 XXX drwxr-xr-x 0,0
+dumpdir/addeddir4 XXX drwxr-xr-x 0,0
+dumpdir/addeddir4/addedfile5 5 -rw-r--r-- 0,0
+dumpdir/addedfile0 5 -rw-r--r-- 0,0
+dumpdir/addedfile1 5 -rw-r--r-- 0,0
+dumpdir/addedfile3 5 -rw-r--r-- 0,0
+Dumping to file...
+xfsdump -l1 -f DUMP_FILE -M stress_tape_media -L stress_568 SCRATCH_MNT
+xfsdump: using file dump (drive_simple) strategy
+xfsdump: level 1 incremental dump of HOSTNAME:SCRATCH_MNT based on level 0 dump begun DATE
+xfsdump: dump date: DATE
+xfsdump: session id: ID
+xfsdump: session label: "stress_568"
+xfsdump: ino map <PHASES>
+xfsdump: ino map construction complete
+xfsdump: estimated dump size: NUM bytes
+xfsdump: creating dump session media file 0 (media 0, file 0)
+xfsdump: dumping ino map
+xfsdump: dumping directories
+xfsdump: dumping non-directory files
+xfsdump: ending media file
+xfsdump: media file size NUM bytes
+xfsdump: dump size (non-dir files) : NUM bytes
+xfsdump: dump complete: SECS seconds elapsed
+xfsdump: Dump Status: SUCCESS
+Listing of what files we have at level 2:
+dumpdir/addeddir2 XXX drwxr-xr-x 0,0
+dumpdir/addeddir6 XXX drwxr-xr-x 0,0
+dumpdir/addeddir6/addedfile4 5 -rw-r--r-- 0,0
+dumpdir/addedfile0 5 -rw-r--r-- 0,0
+dumpdir/addedfile2 5 -rw-r--r-- 0,0
+dumpdir/addedfile3 5 -rw-r--r-- 0,0
+Dumping to file...
+xfsdump -l2 -f DUMP_FILE -M stress_tape_media -L stress_568 SCRATCH_MNT
+xfsdump: using file dump (drive_simple) strategy
+xfsdump: level 2 incremental dump of HOSTNAME:SCRATCH_MNT based on level 1 dump begun DATE
+xfsdump: dump date: DATE
+xfsdump: session id: ID
+xfsdump: session label: "stress_568"
+xfsdump: ino map <PHASES>
+xfsdump: ino map construction complete
+xfsdump: estimated dump size: NUM bytes
+xfsdump: creating dump session media file 0 (media 0, file 0)
+xfsdump: dumping ino map
+xfsdump: dumping directories
+xfsdump: dumping non-directory files
+xfsdump: ending media file
+xfsdump: media file size NUM bytes
+xfsdump: dump size (non-dir files) : NUM bytes
+xfsdump: dump complete: SECS seconds elapsed
+xfsdump: Dump Status: SUCCESS
+Listing of what files we have at level 3:
+dumpdir/addeddir2 XXX drwxr-xr-x 0,0
+dumpdir/addeddir6 XXX drwxr-xr-x 0,0
+dumpdir/addeddir6/addedfile4 5 -rw-r--r-- 0,0
+dumpdir/addedfile0 5 -rw-r--r-- 0,0
+dumpdir/addedfile2 5 -rw-r--r-- 0,0
+dumpdir/addedfile3 5 -rw-r--r-- 0,0
+dumpdir/linkfile0 5 -rw-r--r-- 0,0
+dumpdir/linkfile0_1 5 -rw-r--r-- 0,0
+dumpdir/linkfile2 5 -rw-r--r-- 0,0
+dumpdir/linkfile64 5 -rw-r--r-- 0,0
+Dumping to file...
+xfsdump -l3 -f DUMP_FILE -M stress_tape_media -L stress_568 SCRATCH_MNT
+xfsdump: using file dump (drive_simple) strategy
+xfsdump: level 3 incremental dump of HOSTNAME:SCRATCH_MNT based on level 2 dump begun DATE
+xfsdump: dump date: DATE
+xfsdump: session id: ID
+xfsdump: session label: "stress_568"
+xfsdump: ino map <PHASES>
+xfsdump: ino map construction complete
+xfsdump: estimated dump size: NUM bytes
+xfsdump: creating dump session media file 0 (media 0, file 0)
+xfsdump: dumping ino map
+xfsdump: dumping directories
+xfsdump: dumping non-directory files
+xfsdump: ending media file
+xfsdump: media file size NUM bytes
+xfsdump: dump size (non-dir files) : NUM bytes
+xfsdump: dump complete: SECS seconds elapsed
+xfsdump: Dump Status: SUCCESS
+Listing of what files we have at level 4:
+dumpdir/addeddir2 XXX drwxr-xr-x 0,0
+dumpdir/addeddir6 XXX drwxr-xr-x 0,0
+dumpdir/addedfile0 5 -rw-r--r-- 0,0
+dumpdir/addedfile3 5 -rw-r--r-- 0,0
+dumpdir/linkfile0_1 5 -rw-r--r-- 0,0
+dumpdir/linkfile2 5 -rw-r--r-- 0,0
+Dumping to file...
+xfsdump -l4 -f DUMP_FILE -M stress_tape_media -L stress_568 SCRATCH_MNT
+xfsdump: using file dump (drive_simple) strategy
+xfsdump: level 4 incremental dump of HOSTNAME:SCRATCH_MNT based on level 3 dump begun DATE
+xfsdump: dump date: DATE
+xfsdump: session id: ID
+xfsdump: session label: "stress_568"
+xfsdump: ino map <PHASES>
+xfsdump: ino map construction complete
+xfsdump: estimated dump size: NUM bytes
+xfsdump: creating dump session media file 0 (media 0, file 0)
+xfsdump: dumping ino map
+xfsdump: dumping directories
+xfsdump: dumping non-directory files
+xfsdump: ending media file
+xfsdump: media file size NUM bytes
+xfsdump: dump size (non-dir files) : NUM bytes
+xfsdump: dump complete: SECS seconds elapsed
+xfsdump: Dump Status: SUCCESS
+Listing of what files we have at level 5:
+dumpdir/addedfile6 5 -rw-r--r-- 0,0
+dumpdir/linkfile6_1 5 -rw-r--r-- 0,0
+dumpdir/linkfile6_2 5 -rw-r--r-- 0,0
+dumpdir/linkfile6_3 5 -rw-r--r-- 0,0
+Dumping to file...
+xfsdump -l5 -f DUMP_FILE -M stress_tape_media -L stress_568 SCRATCH_MNT
+xfsdump: using file dump (drive_simple) strategy
+xfsdump: level 5 incremental dump of HOSTNAME:SCRATCH_MNT based on level 4 dump begun DATE
+xfsdump: dump date: DATE
+xfsdump: session id: ID
+xfsdump: session label: "stress_568"
+xfsdump: ino map <PHASES>
+xfsdump: ino map construction complete
+xfsdump: estimated dump size: NUM bytes
+xfsdump: creating dump session media file 0 (media 0, file 0)
+xfsdump: dumping ino map
+xfsdump: dumping directories
+xfsdump: dumping non-directory files
+xfsdump: ending media file
+xfsdump: media file size NUM bytes
+xfsdump: dump size (non-dir files) : NUM bytes
+xfsdump: dump complete: SECS seconds elapsed
+xfsdump: Dump Status: SUCCESS
+Listing of what files we have at level 6:
+dumpdir/addedfile6_mv 5 -rw-r--r-- 0,0
+dumpdir/linkfile6_mv_1 5 -rw-r--r-- 0,0
+dumpdir/linkfile6_mv_2 5 -rw-r--r-- 0,0
+dumpdir/linkfile6_mv_3 5 -rw-r--r-- 0,0
+Dumping to file...
+xfsdump -l6 -f DUMP_FILE -M stress_tape_media -L stress_568 SCRATCH_MNT
+xfsdump: using file dump (drive_simple) strategy
+xfsdump: level 6 incremental dump of HOSTNAME:SCRATCH_MNT based on level 5 dump begun DATE
+xfsdump: dump date: DATE
+xfsdump: session id: ID
+xfsdump: session label: "stress_568"
+xfsdump: ino map <PHASES>
+xfsdump: ino map construction complete
+xfsdump: estimated dump size: NUM bytes
+xfsdump: creating dump session media file 0 (media 0, file 0)
+xfsdump: dumping ino map
+xfsdump: dumping directories
+xfsdump: dumping non-directory files
+xfsdump: ending media file
+xfsdump: media file size NUM bytes
+xfsdump: dump size (non-dir files) : NUM bytes
+xfsdump: dump complete: SECS seconds elapsed
+xfsdump: Dump Status: SUCCESS
+Listing of what files we have at level 7:
+dumpdir/addedfile6_mv 5 -rw-r--r-- 0,0
+dumpdir/linkfile6_mv_1 5 -rw-r--r-- 0,0
+dumpdir/linkfile6_mv_2 5 -rw-r--r-- 0,0
+dumpdir/linkfile6_mv_3 5 -rw-r--r-- 0,0
+Dumping to file...
+xfsdump -l7 -f DUMP_FILE -M stress_tape_media -L stress_568 SCRATCH_MNT
+xfsdump: using file dump (drive_simple) strategy
+xfsdump: level 7 incremental dump of HOSTNAME:SCRATCH_MNT based on level 6 dump begun DATE
+xfsdump: dump date: DATE
+xfsdump: session id: ID
+xfsdump: session label: "stress_568"
+xfsdump: ino map <PHASES>
+xfsdump: ino map construction complete
+xfsdump: estimated dump size: NUM bytes
+xfsdump: creating dump session media file 0 (media 0, file 0)
+xfsdump: dumping ino map
+xfsdump: dumping directories
+xfsdump: dumping non-directory files
+xfsdump: ending media file
+xfsdump: media file size NUM bytes
+xfsdump: dump size (non-dir files) : NUM bytes
+xfsdump: dump complete: SECS seconds elapsed
+xfsdump: Dump Status: SUCCESS
+Listing of what files we have at level 8:
+dumpdir/addedfile6_mv 5 -rw-r--r-- 0,0
+dumpdir/linkfile6_mv_1 5 -rw-r--r-- 0,0
+dumpdir/linkfile6_mv_2 5 -rw-r--r-- 0,0
+dumpdir/linkfile6_mv_3 5 -rw-r--r-- 0,0
+Dumping to file...
+xfsdump -l8 -f DUMP_FILE -M stress_tape_media -L stress_568 SCRATCH_MNT
+xfsdump: using file dump (drive_simple) strategy
+xfsdump: level 8 incremental dump of HOSTNAME:SCRATCH_MNT based on level 7 dump begun DATE
+xfsdump: dump date: DATE
+xfsdump: session id: ID
+xfsdump: session label: "stress_568"
+xfsdump: ino map <PHASES>
+xfsdump: ino map construction complete
+xfsdump: estimated dump size: NUM bytes
+xfsdump: creating dump session media file 0 (media 0, file 0)
+xfsdump: dumping ino map
+xfsdump: dumping directories
+xfsdump: dumping non-directory files
+xfsdump: ending media file
+xfsdump: media file size NUM bytes
+xfsdump: dump size (non-dir files) : NUM bytes
+xfsdump: dump complete: SECS seconds elapsed
+xfsdump: Dump Status: SUCCESS
+Look at what files are contained in the inc. dump
+
+restoring from df.level0
+Contents of dump ...
+xfsrestore -x -f DUMP_FILE -t
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description:
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 0
+xfsrestore: session label: "stress_568"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: found fake rootino #FAKENO, will fix.
+xfsrestore: fix root # to ROOTNO (bind mount?)
+xfsrestore: 6 directories and 12 entries processed
+xfsrestore: directory post-processing
+xfsrestore: reading non-directory files
+xfsrestore: table of contents display complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+
+dumpdir/addeddir3/addedfile4
+dumpdir/addeddir4/addedfile5
+dumpdir/addedfile0
+dumpdir/addedfile1
+dumpdir/addedfile2
+dumpdir/addedfile3
+fakeroot
+
+restoring from df.level1
+Contents of dump ...
+xfsrestore -x -f DUMP_FILE -t
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description:
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 1
+xfsrestore: session label: "stress_568"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: found fake rootino #FAKENO, will fix.
+xfsrestore: fix root # to ROOTNO (bind mount?)
+xfsrestore: 2 directories and 7 entries processed
+xfsrestore: directory post-processing
+xfsrestore: reading non-directory files
+xfsrestore: table of contents display complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+
+
+restoring from df.level2
+Contents of dump ...
+xfsrestore -x -f DUMP_FILE -t
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description:
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 2
+xfsrestore: session label: "stress_568"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: found fake rootino #FAKENO, will fix.
+xfsrestore: fix root # to ROOTNO (bind mount?)
+xfsrestore: 4 directories and 8 entries processed
+xfsrestore: directory post-processing
+xfsrestore: reading non-directory files
+xfsrestore: table of contents display complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+
+dumpdir/addeddir6/addedfile4
+dumpdir/addedfile2
+
+restoring from df.level3
+Contents of dump ...
+xfsrestore -x -f DUMP_FILE -t
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description:
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 3
+xfsrestore: session label: "stress_568"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: found fake rootino #FAKENO, will fix.
+xfsrestore: fix root # to ROOTNO (bind mount?)
+xfsrestore: 3 directories and 12 entries processed
+xfsrestore: directory post-processing
+xfsrestore: reading non-directory files
+xfsrestore: table of contents display complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+
+dumpdir/addeddir6/addedfile4
+dumpdir/addedfile0
+dumpdir/addedfile2
+dumpdir/linkfile0
+dumpdir/linkfile0_1
+dumpdir/linkfile2
+dumpdir/linkfile64
+
+restoring from df.level4
+Contents of dump ...
+xfsrestore -x -f DUMP_FILE -t
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description:
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 4
+xfsrestore: session label: "stress_568"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: found fake rootino #FAKENO, will fix.
+xfsrestore: fix root # to ROOTNO (bind mount?)
+xfsrestore: 3 directories and 8 entries processed
+xfsrestore: directory post-processing
+xfsrestore: reading non-directory files
+xfsrestore: table of contents display complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+
+dumpdir/addedfile0
+dumpdir/linkfile0_1
+dumpdir/linkfile2
+
+restoring from df.level5
+Contents of dump ...
+xfsrestore -x -f DUMP_FILE -t
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description:
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 5
+xfsrestore: session label: "stress_568"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: found fake rootino #FAKENO, will fix.
+xfsrestore: fix root # to ROOTNO (bind mount?)
+xfsrestore: 2 directories and 6 entries processed
+xfsrestore: directory post-processing
+xfsrestore: reading non-directory files
+xfsrestore: table of contents display complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+
+dumpdir/addedfile6
+dumpdir/linkfile6_1
+dumpdir/linkfile6_2
+dumpdir/linkfile6_3
+
+restoring from df.level6
+Contents of dump ...
+xfsrestore -x -f DUMP_FILE -t
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description:
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 6
+xfsrestore: session label: "stress_568"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: found fake rootino #FAKENO, will fix.
+xfsrestore: fix root # to ROOTNO (bind mount?)
+xfsrestore: 2 directories and 6 entries processed
+xfsrestore: directory post-processing
+xfsrestore: reading non-directory files
+xfsrestore: table of contents display complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+
+dumpdir/addedfile6_mv
+dumpdir/linkfile6_mv_1
+dumpdir/linkfile6_mv_2
+dumpdir/linkfile6_mv_3
+
+restoring from df.level7
+Contents of dump ...
+xfsrestore -x -f DUMP_FILE -t
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description:
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 7
+xfsrestore: session label: "stress_568"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: 0 directories and 0 entries processed
+xfsrestore: directory post-processing
+xfsrestore: table of contents display complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+
+
+restoring from df.level8
+Contents of dump ...
+xfsrestore -x -f DUMP_FILE -t
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description:
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 8
+xfsrestore: session label: "stress_568"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: 0 directories and 0 entries processed
+xfsrestore: directory post-processing
+xfsrestore: table of contents display complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+
+Do the cumulative restores
+
+restoring from df.level0
+Restoring cumumlative from file...
+xfsrestore -x -f DUMP_FILE -r RESTORE_DIR
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description:
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 0
+xfsrestore: session label: "stress_568"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: found fake rootino #FAKENO, will fix.
+xfsrestore: fix root # to ROOTNO (bind mount?)
+xfsrestore: 6 directories and 12 entries processed
+xfsrestore: directory post-processing
+xfsrestore: restoring non-directory files
+xfsrestore: restore complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+list restore_dir
+dumpdir/addeddir1 XXX drwxr-xr-x 0,0
+dumpdir/addeddir2 XXX drwxr-xr-x 0,0
+dumpdir/addeddir3 XXX drwxr-xr-x 0,0
+dumpdir/addeddir3/addedfile4 5 -rw-r--r-- 0,0
+dumpdir/addeddir4 XXX drwxr-xr-x 0,0
+dumpdir/addeddir4/addedfile5 5 -rw-r--r-- 0,0
+dumpdir/addedfile0 5 -rw-r--r-- 0,0
+dumpdir/addedfile1 5 -rw-r--r-- 0,0
+dumpdir/addedfile2 5 -rw-r--r-- 0,0
+dumpdir/addedfile3 5 -rw-r--r-- 0,0
+
+restoring from df.level1
+Restoring cumumlative from file...
+xfsrestore -f DUMP_FILE -r RESTORE_DIR
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description:
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 1
+xfsrestore: session label: "stress_568"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: 2 directories and 7 entries processed
+xfsrestore: directory post-processing
+xfsrestore: restoring non-directory files
+xfsrestore: restore complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+list restore_dir
+dumpdir/addeddir1 XXX drwxr-xr-x 0,0
+dumpdir/addeddir4 XXX drwxr-xr-x 0,0
+dumpdir/addeddir4/addedfile5 5 -rw-r--r-- 0,0
+dumpdir/addedfile0 5 -rw-r--r-- 0,0
+dumpdir/addedfile1 5 -rw-r--r-- 0,0
+dumpdir/addedfile3 5 -rw-r--r-- 0,0
+
+restoring from df.level2
+Restoring cumumlative from file...
+xfsrestore -f DUMP_FILE -r RESTORE_DIR
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description:
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 2
+xfsrestore: session label: "stress_568"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: 4 directories and 8 entries processed
+xfsrestore: directory post-processing
+xfsrestore: restoring non-directory files
+xfsrestore: restore complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+list restore_dir
+dumpdir/addeddir2 XXX drwxr-xr-x 0,0
+dumpdir/addeddir6 XXX drwxr-xr-x 0,0
+dumpdir/addeddir6/addedfile4 5 -rw-r--r-- 0,0
+dumpdir/addedfile0 5 -rw-r--r-- 0,0
+dumpdir/addedfile2 5 -rw-r--r-- 0,0
+dumpdir/addedfile3 5 -rw-r--r-- 0,0
+
+restoring from df.level3
+Restoring cumumlative from file...
+xfsrestore -f DUMP_FILE -r RESTORE_DIR
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description:
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 3
+xfsrestore: session label: "stress_568"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: 3 directories and 12 entries processed
+xfsrestore: directory post-processing
+xfsrestore: restoring non-directory files
+xfsrestore: restore complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+list restore_dir
+dumpdir/addeddir2 XXX drwxr-xr-x 0,0
+dumpdir/addeddir6 XXX drwxr-xr-x 0,0
+dumpdir/addeddir6/addedfile4 5 -rw-r--r-- 0,0
+dumpdir/addedfile0 5 -rw-r--r-- 0,0
+dumpdir/addedfile2 5 -rw-r--r-- 0,0
+dumpdir/addedfile3 5 -rw-r--r-- 0,0
+dumpdir/linkfile0 5 -rw-r--r-- 0,0
+dumpdir/linkfile0_1 5 -rw-r--r-- 0,0
+dumpdir/linkfile2 5 -rw-r--r-- 0,0
+dumpdir/linkfile64 5 -rw-r--r-- 0,0
+
+restoring from df.level4
+Restoring cumumlative from file...
+xfsrestore -f DUMP_FILE -r RESTORE_DIR
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description:
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 4
+xfsrestore: session label: "stress_568"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: 3 directories and 8 entries processed
+xfsrestore: directory post-processing
+xfsrestore: restoring non-directory files
+xfsrestore: restore complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+list restore_dir
+dumpdir/addeddir2 XXX drwxr-xr-x 0,0
+dumpdir/addeddir6 XXX drwxr-xr-x 0,0
+dumpdir/addedfile0 5 -rw-r--r-- 0,0
+dumpdir/addedfile3 5 -rw-r--r-- 0,0
+dumpdir/linkfile0_1 5 -rw-r--r-- 0,0
+dumpdir/linkfile2 5 -rw-r--r-- 0,0
+
+restoring from df.level5
+Restoring cumumlative from file...
+xfsrestore -f DUMP_FILE -r RESTORE_DIR
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description:
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 5
+xfsrestore: session label: "stress_568"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: 2 directories and 6 entries processed
+xfsrestore: directory post-processing
+xfsrestore: restoring non-directory files
+xfsrestore: restore complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+list restore_dir
+dumpdir/addedfile6 5 -rw-r--r-- 0,0
+dumpdir/linkfile6_1 5 -rw-r--r-- 0,0
+dumpdir/linkfile6_2 5 -rw-r--r-- 0,0
+dumpdir/linkfile6_3 5 -rw-r--r-- 0,0
+
+restoring from df.level6
+Restoring cumumlative from file...
+xfsrestore -f DUMP_FILE -r RESTORE_DIR
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description:
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 6
+xfsrestore: session label: "stress_568"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: 2 directories and 6 entries processed
+xfsrestore: directory post-processing
+xfsrestore: restoring non-directory files
+xfsrestore: restore complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+list restore_dir
+dumpdir/addedfile6_mv 5 -rw-r--r-- 0,0
+dumpdir/linkfile6_mv_1 5 -rw-r--r-- 0,0
+dumpdir/linkfile6_mv_2 5 -rw-r--r-- 0,0
+dumpdir/linkfile6_mv_3 5 -rw-r--r-- 0,0
+
+restoring from df.level7
+Restoring cumumlative from file...
+xfsrestore -f DUMP_FILE -r RESTORE_DIR
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description:
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 7
+xfsrestore: session label: "stress_568"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: 0 directories and 0 entries processed
+xfsrestore: directory post-processing
+xfsrestore: restoring non-directory files
+xfsrestore: restore complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+list restore_dir
+dumpdir/addedfile6_mv 5 -rw-r--r-- 0,0
+dumpdir/linkfile6_mv_1 5 -rw-r--r-- 0,0
+dumpdir/linkfile6_mv_2 5 -rw-r--r-- 0,0
+dumpdir/linkfile6_mv_3 5 -rw-r--r-- 0,0
+
+restoring from df.level8
+Restoring cumumlative from file...
+xfsrestore -f DUMP_FILE -r RESTORE_DIR
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description:
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 8
+xfsrestore: session label: "stress_568"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: 0 directories and 0 entries processed
+xfsrestore: directory post-processing
+xfsrestore: restoring non-directory files
+xfsrestore: restore complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+list restore_dir
+dumpdir/addedfile6_mv 5 -rw-r--r-- 0,0
+dumpdir/linkfile6_mv_1 5 -rw-r--r-- 0,0
+dumpdir/linkfile6_mv_2 5 -rw-r--r-- 0,0
+dumpdir/linkfile6_mv_3 5 -rw-r--r-- 0,0
+
+Do the ls comparison
+Comparing ls of FS with restored FS at level 0
+Files TMP.ls.0 and TMP.restorals.0 are identical
+
+Comparing ls of FS with restored FS at level 1
+Files TMP.ls.1 and TMP.restorals.1 are identical
+
+Comparing ls of FS with restored FS at level 2
+Files TMP.ls.2 and TMP.restorals.2 are identical
+
+Comparing ls of FS with restored FS at level 3
+Files TMP.ls.3 and TMP.restorals.3 are identical
+
+Comparing ls of FS with restored FS at level 4
+Files TMP.ls.4 and TMP.restorals.4 are identical
+
+Comparing ls of FS with restored FS at level 5
+Files TMP.ls.5 and TMP.restorals.5 are identical
+
+Comparing ls of FS with restored FS at level 6
+Files TMP.ls.6 and TMP.restorals.6 are identical
+
+Comparing ls of FS with restored FS at level 7
+Files TMP.ls.7 and TMP.restorals.7 are identical
+
+Comparing ls of FS with restored FS at level 8
+Files TMP.ls.8 and TMP.restorals.8 are identical
+
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023 Red Hat, Inc. All Rights Reserved.
+#
+# FS QA Test 569
+#
+# Check for any installed example mkfs config files and validate that
+# mkfs.xfs can properly use them.
+#
+. ./common/preamble
+_begin_fstest mkfs
+
+# real QA test starts here
+
+# Modify as appropriate.
+_supported_fs xfs
+_require_scratch_nocheck
+
+ls /usr/share/xfsprogs/mkfs/*.conf &>/dev/null || \
+ _notrun "No mkfs.xfs config files installed"
+
+# We only fail if mkfs.xfs fails outright, ignoring warnings etc
+echo "Silence is golden"
+
+for CONFIG in /usr/share/xfsprogs/mkfs/*.conf; do
+ $MKFS_XFS_PROG -c options=$CONFIG -f $SCRATCH_DEV &>>$seqres.full || \
+ echo "mkfs.xfs config file $CONFIG failed"
+done
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 569
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc. All Rights Reserved.
+#
+# FS QA Test No. 570
+#
+# Race fsstress and superblock scrub for a while to see if we crash or livelock.
+#
+. ./common/preamble
+_begin_fstest scrub dangerous_fsstress_scrub
+
+_cleanup() {
+ _scratch_xfs_stress_scrub_cleanup &> /dev/null
+ cd /
+ rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_scrub
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_scrub -s "scrub sb %agno%"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
--- /dev/null
+QA output created by 570
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc. All Rights Reserved.
+#
+# FS QA Test No. 571
+#
+# Race fsstress and AGF scrub for a while to see if we crash or livelock.
+#
+. ./common/preamble
+_begin_fstest scrub dangerous_fsstress_scrub
+
+_cleanup() {
+ _scratch_xfs_stress_scrub_cleanup &> /dev/null
+ cd /
+ rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_scrub
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_scrub -s "scrub agf %agno%"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
--- /dev/null
+QA output created by 571
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc. All Rights Reserved.
+#
+# FS QA Test No. 572
+#
+# Race fsstress and AGFL scrub for a while to see if we crash or livelock.
+#
+. ./common/preamble
+_begin_fstest scrub dangerous_fsstress_scrub
+
+_cleanup() {
+ _scratch_xfs_stress_scrub_cleanup &> /dev/null
+ cd /
+ rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_scrub
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_scrub -s "scrub agfl %agno%"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
--- /dev/null
+QA output created by 572
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc. All Rights Reserved.
+#
+# FS QA Test No. 573
+#
+# Race fsstress and AGI scrub for a while to see if we crash or livelock.
+#
+. ./common/preamble
+_begin_fstest scrub dangerous_fsstress_scrub
+
+_cleanup() {
+ _scratch_xfs_stress_scrub_cleanup &> /dev/null
+ cd /
+ rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_scrub
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_scrub -s "scrub agi %agno%"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
--- /dev/null
+QA output created by 573
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc. All Rights Reserved.
+#
+# FS QA Test No. 574
+#
+# Race fsstress and freespace by block btree scrub for a while to see if we
+# crash or livelock.
+#
+. ./common/preamble
+_begin_fstest scrub dangerous_fsstress_scrub
+
+_cleanup() {
+ _scratch_xfs_stress_scrub_cleanup &> /dev/null
+ cd /
+ rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_scrub
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_scrub -s "scrub bnobt %agno%"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
--- /dev/null
+QA output created by 574
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc. All Rights Reserved.
+#
+# FS QA Test No. 575
+#
+# Race fsstress and free space by length btree scrub for a while to see if we
+# crash or livelock.
+#
+. ./common/preamble
+_begin_fstest scrub dangerous_fsstress_scrub
+
+_cleanup() {
+ _scratch_xfs_stress_scrub_cleanup &> /dev/null
+ cd /
+ rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_scrub
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_scrub -s "scrub cntbt %agno%"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
--- /dev/null
+QA output created by 575
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc. All Rights Reserved.
+#
+# FS QA Test No. 576
+#
+# Race fsstress and inode btree scrub for a while to see if we crash or
+# livelock.
+#
+. ./common/preamble
+_begin_fstest scrub dangerous_fsstress_scrub
+
+_cleanup() {
+ _scratch_xfs_stress_scrub_cleanup &> /dev/null
+ cd /
+ rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_scrub
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_scrub -x 'dir' -s "scrub inobt %agno%"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
--- /dev/null
+QA output created by 576
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc. All Rights Reserved.
+#
+# FS QA Test No. 577
+#
+# Race fsstress and free inode btree scrub for a while to see if we crash or
+# livelock.
+#
+. ./common/preamble
+_begin_fstest scrub dangerous_fsstress_scrub
+
+_cleanup() {
+ _scratch_xfs_stress_scrub_cleanup &> /dev/null
+ cd /
+ rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_scrub
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_require_xfs_has_feature "$SCRATCH_MNT" finobt
+_scratch_xfs_stress_scrub -x 'dir' -s "scrub finobt %agno%"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
--- /dev/null
+QA output created by 577
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc. All Rights Reserved.
+#
+# FS QA Test No. 578
+#
+# Race fsstress and reverse mapping btree scrub for a while to see if we crash
+# or livelock.
+#
+. ./common/preamble
+_begin_fstest scrub dangerous_fsstress_scrub
+
+_cleanup() {
+ _scratch_xfs_stress_scrub_cleanup &> /dev/null
+ cd /
+ rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_scrub
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_require_xfs_has_feature "$SCRATCH_MNT" rmapbt
+_scratch_xfs_stress_scrub -s "scrub rmapbt %agno%"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
--- /dev/null
+QA output created by 578
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc. All Rights Reserved.
+#
+# FS QA Test No. 579
+#
+# Race fsstress and reference count btree scrub for a while to see if we crash
+# or livelock.
+#
+. ./common/preamble
+_begin_fstest scrub dangerous_fsstress_scrub
+
+_cleanup() {
+ _scratch_xfs_stress_scrub_cleanup &> /dev/null
+ cd /
+ rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+. ./common/reflink
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_scrub
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_require_xfs_has_feature "$SCRATCH_MNT" reflink
+_scratch_xfs_stress_scrub -s "scrub refcountbt %agno%"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
--- /dev/null
+QA output created by 579
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 580
+#
+# Race fsstress and fscounter scrub on the realtime device for a while to see
+# if we crash or livelock.
+#
+. ./common/preamble
+_begin_fstest scrub dangerous_fsstress_scrub
+
+_cleanup() {
+ _scratch_xfs_stress_scrub_cleanup &> /dev/null
+ cd /
+ rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_realtime
+_require_scratch
+_require_xfs_stress_scrub
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_require_xfs_has_feature "$SCRATCH_MNT" realtime
+
+# Force all files to be allocated on the realtime device
+_xfs_force_bdev realtime $SCRATCH_MNT
+
+_scratch_xfs_stress_scrub -s 'scrub fscounters'
+
+# success, all done
+echo Silence is golden
+status=0
+exit
--- /dev/null
+QA output created by 580
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc. All Rights Reserved.
+#
+# FS QA Test No. 581
+#
+# Race fsstress and realtime bitmap scrub for a while to see if we crash or
+# livelock.
+#
+. ./common/preamble
+_begin_fstest scrub dangerous_fsstress_scrub
+
+_cleanup() {
+ _scratch_xfs_stress_scrub_cleanup &> /dev/null
+ cd /
+ rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_realtime
+_require_scratch
+_require_xfs_stress_scrub
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_require_xfs_has_feature "$SCRATCH_MNT" realtime
+_scratch_xfs_stress_scrub -s "scrub rtbitmap"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
--- /dev/null
+QA output created by 581
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc. All Rights Reserved.
+#
+# FS QA Test No. 582
+#
+# Race fsstress and realtime summary scrub for a while to see if we crash or
+# livelock.
+#
+. ./common/preamble
+_begin_fstest scrub dangerous_fsstress_scrub
+
+_cleanup() {
+ _scratch_xfs_stress_scrub_cleanup &> /dev/null
+ cd /
+ rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_realtime
+_require_scratch
+_require_xfs_stress_scrub
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_require_xfs_has_feature "$SCRATCH_MNT" realtime
+
+# XXX the realtime summary scrubber isn't currently implemented upstream.
+# Don't bother trying to test it on those kernels
+$XFS_IO_PROG -c 'scrub rtsummary' -c 'scrub rtsummary' "$SCRATCH_MNT" 2>&1 | \
+ grep -q 'Scan was not complete' && \
+ _notrun "rtsummary scrub is incomplete"
+
+_scratch_xfs_stress_scrub -s "scrub rtsummary"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
--- /dev/null
+QA output created by 582
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc. All Rights Reserved.
+#
+# FS QA Test No. 583
+#
+# Race fsstress and user quota scrub for a while to see if we crash or
+# livelock.
+#
+. ./common/preamble
+_begin_fstest scrub dangerous_fsstress_scrub
+
+_cleanup() {
+ _scratch_xfs_stress_scrub_cleanup &> /dev/null
+ cd /
+ rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+. ./common/quota
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_scrub
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_require_xfs_quota_acct_enabled "$SCRATCH_DEV" usrquota
+_scratch_xfs_stress_scrub -s "scrub usrquota"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
--- /dev/null
+QA output created by 583
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc. All Rights Reserved.
+#
+# FS QA Test No. 584
+#
+# Race fsstress and group quota scrub for a while to see if we crash or
+# livelock.
+#
+. ./common/preamble
+_begin_fstest scrub dangerous_fsstress_scrub
+
+_cleanup() {
+ _scratch_xfs_stress_scrub_cleanup &> /dev/null
+ cd /
+ rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+. ./common/quota
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_scrub
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_require_xfs_quota_acct_enabled "$SCRATCH_DEV" grpquota
+_scratch_xfs_stress_scrub -s "scrub grpquota"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
--- /dev/null
+QA output created by 584
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc. All Rights Reserved.
+#
+# FS QA Test No. 585
+#
+# Race fsstress and project quota scrub for a while to see if we crash or
+# livelock.
+#
+. ./common/preamble
+_begin_fstest scrub dangerous_fsstress_scrub
+
+_cleanup() {
+ _scratch_xfs_stress_scrub_cleanup &> /dev/null
+ cd /
+ rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+. ./common/quota
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_scrub
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_require_xfs_quota_acct_enabled "$SCRATCH_DEV" prjquota
+_scratch_xfs_stress_scrub -s "scrub prjquota"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
--- /dev/null
+QA output created by 585
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc. All Rights Reserved.
+#
+# FS QA Test No. 586
+#
+# Race fsstress and summary counters scrub for a while to see if we crash or
+# livelock.
+#
+. ./common/preamble
+_begin_fstest scrub dangerous_fsstress_scrub
+
+_cleanup() {
+ _scratch_xfs_stress_scrub_cleanup &> /dev/null
+ cd /
+ rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_scrub
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_scrub -s "scrub fscounters"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
--- /dev/null
+QA output created by 586
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc. All Rights Reserved.
+#
+# FS QA Test No. 587
+#
+# Race fsstress and inode record scrub for a while to see if we crash or
+# livelock.
+#
+. ./common/preamble
+_begin_fstest scrub dangerous_fsstress_scrub
+
+_cleanup() {
+ _scratch_xfs_stress_scrub_cleanup &> /dev/null
+ cd /
+ rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_scrub
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_scrub -s "scrub inode" -t "%file%"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
--- /dev/null
+QA output created by 587
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc. All Rights Reserved.
+#
+# FS QA Test No. 588
+#
+# Race fsstress and data fork scrub for a while to see if we crash or livelock.
+#
+. ./common/preamble
+_begin_fstest scrub dangerous_fsstress_scrub
+
+_cleanup() {
+ _scratch_xfs_stress_scrub_cleanup &> /dev/null
+ cd /
+ rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_scrub
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_scrub -s "scrub bmapbtd" -t "%datafile%"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
--- /dev/null
+QA output created by 588
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc. All Rights Reserved.
+#
+# FS QA Test No. 589
+#
+# Race fsstress and attr fork scrub for a while to see if we crash or livelock.
+#
+. ./common/preamble
+_begin_fstest scrub dangerous_fsstress_scrub
+
+_cleanup() {
+ _scratch_xfs_stress_scrub_cleanup &> /dev/null
+ cd /
+ rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+. ./common/attr
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_attrs
+_require_xfs_stress_scrub
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_scrub -x 'xattr' -s "scrub bmapbta" -t "%attrfile%"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
--- /dev/null
+QA output created by 589
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc. All Rights Reserved.
+#
+# FS QA Test No. 590
+#
+# Race fsstress and cow fork scrub for a while to see if we crash or livelock.
+#
+. ./common/preamble
+_begin_fstest scrub dangerous_fsstress_scrub
+
+_cleanup() {
+ _scratch_xfs_stress_scrub_cleanup &> /dev/null
+ cd /
+ rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+. ./common/reflink
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_scrub
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_require_xfs_has_feature "$SCRATCH_MNT" reflink
+_scratch_xfs_stress_scrub -s "scrub bmapbtc" -t "%cowfile%"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
--- /dev/null
+QA output created by 590
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc. All Rights Reserved.
+#
+# FS QA Test No. 591
+#
+# Race fsstress and directory scrub for a while to see if we crash or livelock.
+#
+. ./common/preamble
+_begin_fstest scrub dangerous_fsstress_scrub
+
+_cleanup() {
+ _scratch_xfs_stress_scrub_cleanup &> /dev/null
+ cd /
+ rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_scrub
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_scrub -x 'dir' -s "scrub directory" -t "%dir%"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
--- /dev/null
+QA output created by 591
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc. All Rights Reserved.
+#
+# FS QA Test No. 592
+#
+# Race fsstress and extended attributes scrub for a while to see if we crash or
+# livelock.
+#
+. ./common/preamble
+_begin_fstest scrub dangerous_fsstress_scrub
+
+_cleanup() {
+ _scratch_xfs_stress_scrub_cleanup &> /dev/null
+ cd /
+ rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+. ./common/attr
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_attrs
+_require_xfs_stress_scrub
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_scrub -x 'xattr' -s "scrub xattr" -t "%attrfile%"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
--- /dev/null
+QA output created by 592
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc. All Rights Reserved.
+#
+# FS QA Test No. 593
+#
+# Race fsstress and parent pointers scrub for a while to see if we crash or
+# livelock.
+#
+. ./common/preamble
+_begin_fstest scrub dangerous_fsstress_scrub
+
+_cleanup() {
+ _scratch_xfs_stress_scrub_cleanup &> /dev/null
+ cd /
+ rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_scrub
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_scrub -s "scrub parent" -t "%dir%"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
--- /dev/null
+QA output created by 593
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc. All Rights Reserved.
+#
+# FS QA Test No. 594
+#
+# Race fsstress and symlink scrub for a while to see if we crash or livelock.
+# We can't open symlink files directly for scrubbing, so we use xfs_scrub(8).
+#
+. ./common/preamble
+_begin_fstest scrub dangerous_fsstress_scrub
+
+_cleanup() {
+ _scratch_xfs_stress_scrub_cleanup &> /dev/null
+ cd /
+ rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_scrub
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+XFS_SCRUB_PHASE=3 _scratch_xfs_stress_scrub -x 'symlink' -S '-n'
+
+# success, all done
+echo Silence is golden
+status=0
+exit
--- /dev/null
+QA output created by 594
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc. All Rights Reserved.
+#
+# FS QA Test No. 595
+#
+# Race fsstress and special file scrub for a while to see if we crash or
+# livelock. We can't open special files directly for scrubbing, so we use
+# xfs_scrub(8).
+#
+. ./common/preamble
+_begin_fstest scrub dangerous_fsstress_scrub
+
+_cleanup() {
+ _scratch_xfs_stress_scrub_cleanup &> /dev/null
+ cd /
+ rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_scrub
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+XFS_SCRUB_PHASE=3 _scratch_xfs_stress_scrub -x 'mknod' -S '-n'
+
+# success, all done
+echo Silence is golden
+status=0
+exit
--- /dev/null
+QA output created by 595
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2000-2002 Silicon Graphics, Inc. All Rights Reserved.
+#
+# FS QA Test No. 596
+#
+# growfs QA tests - repeatedly fill/grow the rt volume of the filesystem check
+# the filesystem contents after each operation. This is the rt equivalent of
+# xfs/041.
+#
+. ./common/preamble
+_begin_fstest growfs ioctl auto
+
+# Override the default cleanup function.
+_cleanup()
+{
+ cd /
+ _scratch_unmount
+ rm -f $tmp.*
+}
+
+# Import common functions.
+. ./common/filter
+
+# real QA test starts here
+_supported_fs xfs
+
+_require_scratch
+_require_realtime
+_require_no_large_scratch_dev
+_scratch_unmount 2>/dev/null
+
+_fill()
+{
+ if [ $# -ne 1 ]; then echo "Usage: _fill \"path\"" 1>&2 ; exit 1; fi
+ _do "Fill filesystem" \
+ "$here/src/fill2fs --verbose --dir=$1 --seed=0 --filesize=65536 --stddev=32768 --list=- >>$tmp.manifest"
+}
+
+_do_die_on_error=message_only
+rtsize=32
+echo -n "Make $rtsize megabyte rt filesystem on SCRATCH_DEV and mount... "
+_scratch_mkfs_xfs -rsize=${rtsize}m | _filter_mkfs 2> "$tmp.mkfs" >> $seqres.full
+test "${PIPESTATUS[0]}" -eq 0 || _fail "mkfs failed"
+
+. $tmp.mkfs
+onemeginblocks=`expr 1048576 / $dbsize`
+_scratch_mount
+
+# We're growing the realtime device, so force new file creation there
+_xfs_force_bdev realtime $SCRATCH_MNT
+
+echo "done"
+
+# full allocation group -> partial; partial -> expand partial + new partial;
+# partial -> expand partial; partial -> full
+# cycle through 33m -> 67m -> 75m -> 96m
+for size in 33 67 75 96
+do
+ grow_size=`expr $size \* $onemeginblocks`
+ _fill $SCRATCH_MNT/fill_$size
+ _do "Grow filesystem to ${size}m" "xfs_growfs -R $grow_size $SCRATCH_MNT"
+ echo -n "Flush filesystem... "
+ _do "_scratch_unmount"
+ _do "_try_scratch_mount"
+ echo "done"
+ echo -n "Check files... "
+ if ! _do "$here/src/fill2fs_check $tmp.manifest"; then
+ echo "fail (see $seqres.full)"
+ _do "cat $tmp.manifest"
+ _do "ls -altrR $SCRATCH_MNT"
+ status=1 ; exit
+ fi
+ echo "done"
+done
+
+# success, all done
+echo "Growfs tests passed."
+status=0 ; exit
--- /dev/null
+QA output created by 596
+Make 32 megabyte rt filesystem on SCRATCH_DEV and mount... done
+Fill filesystem... done
+Grow filesystem to 33m... done
+Flush filesystem... done
+Check files... done
+Fill filesystem... done
+Grow filesystem to 67m... done
+Flush filesystem... done
+Check files... done
+Fill filesystem... done
+Grow filesystem to 75m... done
+Flush filesystem... done
+Check files... done
+Fill filesystem... done
+Grow filesystem to 96m... done
+Flush filesystem... done
+Check files... done
+Growfs tests passed.
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 597
+#
+# Make sure that the kernel and userspace agree on which byte sequences are
+# ASCII uppercase letters, and how to convert them.
+#
+. ./common/preamble
+_begin_fstest auto ci dir
+
+# Import common functions.
+. ./common/filter
+
+_fixed_by_kernel_commit a9248538facc \
+ "xfs: stabilize the dirent name transformation function used for ascii-ci dir hash computation"
+_fixed_by_kernel_commit 9dceccc5822f \
+ "xfs: use the directory name hash function for dir scrubbing"
+
+_supported_fs xfs
+_require_scratch
+_require_xfs_mkfs_ciname
+
+_scratch_mkfs -n version=ci > $seqres.full
+_scratch_mount
+
+# Create a two-block directory to force leaf format
+mkdir "$SCRATCH_MNT/lol"
+touch "$SCRATCH_MNT/lol/autoexec.bat"
+i=0
+dblksz=$(_xfs_get_dir_blocksize "$SCRATCH_MNT")
+nr_dirents=$((dblksz * 2 / 256))
+
+for ((i = 0; i < nr_dirents; i++)); do
+ name="$(printf "y%0254d" $i)"
+ ln "$SCRATCH_MNT/lol/autoexec.bat" "$SCRATCH_MNT/lol/$name"
+done
+
+dirsz=$(stat -c '%s' $SCRATCH_MNT/lol)
+test $dirsz -gt $dblksz || echo "dir size $dirsz, expected at least $dblksz?"
+stat $SCRATCH_MNT/lol >> $seqres.full
+
+# Create names with extended ascii characters in them to exploit the fact
+# that the Linux kernel will transform extended ASCII uppercase characters
+# but libc won't. Need to force LANG=C here so that awk doesn't spit out utf8
+# sequences.
+test "$LANG" = "C" || _notrun "LANG=C required"
+awk 'END { for (i = 192; i < 247; i++) printf("%c\n", i); }' < /dev/null | while read name; do
+ ln "$SCRATCH_MNT/lol/autoexec.bat" "$SCRATCH_MNT/lol/$name" 2>&1 | _filter_scratch
+done
+
+# Now just let repair run
+
+status=0
+exit
--- /dev/null
+QA output created by 597
+ln: failed to create hard link 'SCRATCH_MNT/lol/'$'\340': File exists
+ln: failed to create hard link 'SCRATCH_MNT/lol/'$'\341': File exists
+ln: failed to create hard link 'SCRATCH_MNT/lol/'$'\342': File exists
+ln: failed to create hard link 'SCRATCH_MNT/lol/'$'\343': File exists
+ln: failed to create hard link 'SCRATCH_MNT/lol/'$'\344': File exists
+ln: failed to create hard link 'SCRATCH_MNT/lol/'$'\345': File exists
+ln: failed to create hard link 'SCRATCH_MNT/lol/'$'\346': File exists
+ln: failed to create hard link 'SCRATCH_MNT/lol/'$'\347': File exists
+ln: failed to create hard link 'SCRATCH_MNT/lol/'$'\350': File exists
+ln: failed to create hard link 'SCRATCH_MNT/lol/'$'\351': File exists
+ln: failed to create hard link 'SCRATCH_MNT/lol/'$'\352': File exists
+ln: failed to create hard link 'SCRATCH_MNT/lol/'$'\353': File exists
+ln: failed to create hard link 'SCRATCH_MNT/lol/'$'\354': File exists
+ln: failed to create hard link 'SCRATCH_MNT/lol/'$'\355': File exists
+ln: failed to create hard link 'SCRATCH_MNT/lol/'$'\356': File exists
+ln: failed to create hard link 'SCRATCH_MNT/lol/'$'\357': File exists
+ln: failed to create hard link 'SCRATCH_MNT/lol/'$'\360': File exists
+ln: failed to create hard link 'SCRATCH_MNT/lol/'$'\361': File exists
+ln: failed to create hard link 'SCRATCH_MNT/lol/'$'\362': File exists
+ln: failed to create hard link 'SCRATCH_MNT/lol/'$'\363': File exists
+ln: failed to create hard link 'SCRATCH_MNT/lol/'$'\364': File exists
+ln: failed to create hard link 'SCRATCH_MNT/lol/'$'\365': File exists
+ln: failed to create hard link 'SCRATCH_MNT/lol/'$'\366': File exists
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 598
+#
+# Make sure that metadump obfuscation works for filesystems with ascii-ci
+# enabled.
+#
+. ./common/preamble
+_begin_fstest auto dir ci
+
+_cleanup()
+{
+ cd /
+ rm -r -f $tmp.* $testdir
+}
+
+_fixed_by_git_commit xfsprogs 10a01bcd \
+ "xfs_db: fix metadump name obfuscation for ascii-ci filesystems"
+
+_fixed_by_kernel_commit a9248538facc \
+ "xfs: stabilize the dirent name transformation function used for ascii-ci dir hash computation"
+_fixed_by_kernel_commit 9dceccc5822f \
+ "xfs: use the directory name hash function for dir scrubbing"
+
+_supported_fs xfs
+_require_test
+_require_scratch
+_require_xfs_mkfs_ciname
+
+_scratch_mkfs -n version=ci > $seqres.full
+_scratch_mount
+
+# Create a two-block directory to force leaf format
+mkdir "$SCRATCH_MNT/lol"
+touch "$SCRATCH_MNT/lol/autoexec.bat"
+i=0
+dblksz=$(_xfs_get_dir_blocksize "$SCRATCH_MNT")
+nr_dirents=$((dblksz * 2 / 256))
+
+for ((i = 0; i < nr_dirents; i++)); do
+ name="$(printf "y%0254d" $i)"
+ ln "$SCRATCH_MNT/lol/autoexec.bat" "$SCRATCH_MNT/lol/$name"
+done
+
+dirsz=$(stat -c '%s' $SCRATCH_MNT/lol)
+test $dirsz -gt $dblksz || echo "dir size $dirsz, expected at least $dblksz?"
+stat $SCRATCH_MNT/lol >> $seqres.full
+
+# Create a two-block attr to force leaf format
+i=0
+for ((i = 0; i < nr_dirents; i++)); do
+ name="$(printf "user.%0250d" $i)"
+ $SETFATTR_PROG -n "$name" -v 1 "$SCRATCH_MNT/lol/autoexec.bat"
+done
+stat $SCRATCH_MNT/lol/autoexec.bat >> $seqres.full
+
+_scratch_unmount
+
+testdir=$TEST_DIR/$seq.metadumps
+mkdir -p $testdir
+metadump_file=$testdir/scratch.md
+metadump_file_a=${metadump_file}.a
+metadump_file_o=${metadump_file}.o
+metadump_file_ao=${metadump_file}.ao
+
+echo metadump
+_scratch_xfs_metadump $metadump_file >> $seqres.full
+
+echo metadump a
+_scratch_xfs_metadump $metadump_file_a -a >> $seqres.full
+
+echo metadump o
+_scratch_xfs_metadump $metadump_file_o -o >> $seqres.full
+
+echo metadump ao
+_scratch_xfs_metadump $metadump_file_ao -a -o >> $seqres.full
+
+echo mdrestore
+_scratch_xfs_mdrestore $metadump_file
+_scratch_mount
+_check_scratch_fs
+_scratch_unmount
+
+echo mdrestore a
+_scratch_xfs_mdrestore $metadump_file_a
+_scratch_mount
+_check_scratch_fs
+_scratch_unmount
+
+echo mdrestore o
+_scratch_xfs_mdrestore $metadump_file_o
+_scratch_mount
+_check_scratch_fs
+_scratch_unmount
+
+echo mdrestore ao
+_scratch_xfs_mdrestore $metadump_file_ao
+_scratch_mount
+_check_scratch_fs
+_scratch_unmount
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 598
+metadump
+metadump a
+metadump o
+metadump ao
+mdrestore
+mdrestore a
+mdrestore o
+mdrestore ao
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 599
+#
+# Make sure that the kernel and utilities can handle large numbers of dirhash
+# collisions in both the directory and extended attribute structures.
+#
+# This started as a regression test for the new 'hashcoll' function in xfs_db,
+# but became a regression test for an xfs_repair bug affecting hashval checks
+# applied to the second and higher node levels of a dabtree.
+#
+. ./common/preamble
+_begin_fstest auto dir
+
+_fixed_by_git_commit xfsprogs b7b81f336ac \
+ "xfs_repair: fix incorrect dabtree hashval comparison"
+
+_supported_fs xfs
+_require_xfs_db_command "hashcoll"
+_require_xfs_db_command "path"
+_require_scratch
+
+_scratch_mkfs > $seqres.full
+_scratch_mount
+
+crash_dir=$SCRATCH_MNT/lol/
+crash_attrs=$SCRATCH_MNT/hah
+
+mkdir -p "$crash_dir"
+touch "$crash_attrs"
+
+# Create enough dirents to fill two dabtree node blocks with names that all
+# hash to the same value. Each dirent gets its own record in the dabtree,
+# so we must create enough dirents to get a dabtree of at least height 2.
+dblksz=$(_xfs_get_dir_blocksize "$SCRATCH_MNT")
+
+da_records_per_block=$((dblksz / 8)) # 32-bit hash and 32-bit before
+nr_dirents=$((da_records_per_block * 2))
+
+longname="$(mktemp --dry-run "$(perl -e 'print "X" x 255;')" | tr ' ' 'X')"
+echo "creating $nr_dirents dirents from '$longname'" >> $seqres.full
+_scratch_xfs_db -r -c "hashcoll -n $nr_dirents -p $crash_dir $longname"
+
+# Create enough xattrs to fill two dabtree nodes. Each attribute entry gets
+# its own record in the dabtree, so we have to create enough attributes to get
+# a dabtree of at least height 2.
+blksz=$(_get_block_size "$SCRATCH_MNT")
+
+da_records_per_block=$((blksz / 8)) # 32-bit hash and 32-bit before
+nr_attrs=$((da_records_per_block * 2))
+
+longname="$(mktemp --dry-run "$(perl -e 'print "X" x 249;')" | tr ' ' 'X')"
+echo "creating $nr_attrs attrs from '$longname'" >> $seqres.full
+_scratch_xfs_db -r -c "hashcoll -a -n $nr_attrs -p $crash_attrs $longname"
+
+_scratch_unmount
+
+# Make sure that there's one hash value dominating the dabtree block.
+# We don't require 100% because directories create dabtree records for dot
+# and dotdot.
+filter_hashvals() {
+ uniq -c | awk -v seqres_full="$seqres.full" \
+ '{print $0 >> seqres_full; tot += $1; if ($1 > biggest) biggest = $1;} END {if (biggest >= (tot - 2)) exit(0); exit(1);}'
+ test "${PIPESTATUS[1]}" -eq 0 || \
+ echo "Scattered dabtree hashes? See seqres.full"
+}
+
+# Did we actually get a two-level dabtree for the directory? Does it contain a
+# long run of hashes?
+echo "dir check" >> $seqres.full
+da_node_block_offset=$(( (2 ** 35) / blksz ))
+dir_db_args=(-c 'path /lol/' -c "dblock $da_node_block_offset" -c 'addr nbtree[0].before')
+dir_count="$(_scratch_xfs_db "${dir_db_args[@]}" -c 'print lhdr.count' | awk '{print $3}')"
+_scratch_xfs_db "${dir_db_args[@]}" -c "print lents[0-$((dir_count - 1))].hashval" | sed -e 's/lents\[[0-9]*\]/lents[NN]/g' | filter_hashvals
+
+# Did we actually get a two-level dabtree for the attrs? Does it contain a
+# long run of hashes?
+echo "attr check" >> $seqres.full
+attr_db_args=(-c 'path /hah' -c "ablock 0" -c 'addr btree[0].before')
+attr_count="$(_scratch_xfs_db "${attr_db_args[@]}" -c 'print hdr.count' | awk '{print $3}')"
+_scratch_xfs_db "${attr_db_args[@]}" -c "print btree[0-$((attr_count - 1))].hashval" | sed -e 's/btree\[[0-9]*\]/btree[NN]/g' | filter_hashvals
+
+# Remount to get some coverage of xfs_scrub before seeing if xfs_repair
+# will trip over the large dabtrees.
+echo Silence is golden
+_scratch_mount
+status=0
+exit
--- /dev/null
+QA output created by 599
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 600
+#
+# Regression test for an agbno overflow bug in XFS GETFSMAP involving an
+# fsmap_advance call. Userspace can indicate that a GETFSMAP call is actually
+# a continuation of a previous call by setting the "low" key to the last record
+# returned by the previous call.
+#
+# If the last record returned by GETFSMAP is a non-shareable extent at the end
+# of an AG and the AG size is exactly a power of two, the startblock in the low
+# key of the rmapbt query can be set to a value larger than EOAG. When this
+# happens, GETFSMAP will return EINVAL instead of returning records for the
+# next AG.
+#
+. ./common/preamble
+_begin_fstest auto quick fsmap
+
+. ./common/filter
+
+_fixed_by_git_commit kernel cfa2df68b7ce \
+ "xfs: fix an agbno overflow in __xfs_getfsmap_datadev"
+
+# Modify as appropriate.
+_supported_fs generic
+_require_xfs_io_command fsmap
+_require_xfs_scratch_rmapbt
+
+_scratch_mkfs | _filter_mkfs 2> $tmp.mkfs >> $seqres.full
+source $tmp.mkfs
+
+# Find the next power of two agsize smaller than whatever the default is.
+for ((p = 31; p > 0; p--)); do
+ desired_agsize=$((2 ** p))
+ test "$desired_agsize" -lt "$agsize" && break
+done
+
+echo "desired asize=$desired_agsize" >> $seqres.full
+_scratch_mkfs -d "agsize=${desired_agsize}b" | _filter_mkfs 2> $tmp.mkfs >> $seqres.full
+source $tmp.mkfs
+
+test "$desired_agsize" -eq "$agsize" || _notrun "wanted agsize=$desired_agsize, got $agsize"
+
+_scratch_mount
+$XFS_IO_PROG -c 'fsmap -n 1024 -v' $SCRATCH_MNT >> $tmp.big
+$XFS_IO_PROG -c 'fsmap -n 1 -v' $SCRATCH_MNT >> $tmp.small
+
+diff -Naurpw $tmp.big $tmp.small
+
+# success, all done
+echo Silence is golden
+status=0
+exit
--- /dev/null
+QA output created by 600
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2019 Oracle, Inc. All Rights Reserved.
+#
+# FS QA Test No. 601
+#
+# Populate a XFS filesystem and ensure that xfs_copy works properly.
+#
+. ./common/preamble
+_begin_fstest auto copy
+
+_register_cleanup "_cleanup" BUS
+
+# Override the default cleanup function.
+_cleanup()
+{
+ cd /
+ rm -rf $tmp.* $testdir
+}
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+
+testdir=$TEST_DIR/test-$seq
+
+# real QA test starts here
+_supported_fs xfs
+
+_require_xfs_copy
+_require_scratch_nocheck
+_require_populate_commands
+_xfs_skip_online_rebuild
+_xfs_skip_offline_rebuild
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+mkdir -p $testdir
+copy_file=$testdir/copy.img
+
+echo copy
+$XFS_COPY_PROG $SCRATCH_DEV $copy_file >> $seqres.full
+_check_scratch_fs $copy_file
+
+echo recopy
+$XFS_COPY_PROG $copy_file $SCRATCH_DEV >> $seqres.full
+_scratch_mount
+_check_scratch_fs
+_scratch_unmount
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 601
+Format and populate
+copy
+recopy
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 602
+#
+# Test using runtime code to fix unlinked inodes on a clean filesystem that
+# never got cleaned up.
+#
+. ./common/preamble
+_begin_fstest auto quick unlink
+
+. ./common/filter
+. ./common/fuzzy
+. ./common/quota
+
+# real QA test starts here
+
+_supported_fs xfs
+_require_xfs_db_command iunlink
+_require_scratch_nocheck # we'll run repair ourselves
+
+# From the AGI definition
+XFS_AGI_UNLINKED_BUCKETS=64
+
+# Try to make each iunlink bucket have this many inodes in it.
+IUNLINK_BUCKETLEN=5
+
+# Disable quota since quotacheck will break this test
+orig_mount_options="$MOUNT_OPTIONS"
+_qmount_option 'noquota'
+
+format_scratch() {
+ _scratch_mkfs -d agcount=1 | _filter_mkfs 2> "${tmp}.mkfs" >> $seqres.full
+ source "${tmp}.mkfs"
+ test "${agcount}" -eq 1 || _notrun "test requires 1 AG for error injection"
+
+ local nr_iunlinks="$((IUNLINK_BUCKETLEN * XFS_AGI_UNLINKED_BUCKETS))"
+ readarray -t BADINODES < <(_scratch_xfs_db -x -c "iunlink -n $nr_iunlinks" | awk '{print $4}')
+}
+
+__repair_check_scratch() {
+ _scratch_xfs_repair -o force_geometry -n 2>&1 | \
+ tee -a $seqres.full | \
+ grep -E '(disconnected inode.*would move|next_unlinked in inode|unlinked bucket.*is.*in ag)'
+ return "${PIPESTATUS[0]}"
+}
+
+exercise_scratch() {
+ # Create a bunch of files...
+ declare -A inums
+ for ((i = 0; i < (XFS_AGI_UNLINKED_BUCKETS * 2); i++)); do
+ touch "${SCRATCH_MNT}/${i}" || break
+ inums["${i}"]="$(stat -c %i "${SCRATCH_MNT}/${i}")"
+ done
+
+ # ...then delete them to exercise the unlinked buckets
+ for ((i = 0; i < (XFS_AGI_UNLINKED_BUCKETS * 2); i++)); do
+ if ! rm -f "${SCRATCH_MNT}/${i}"; then
+ echo "rm failed on inum ${inums[$i]}"
+ break
+ fi
+ done
+}
+
+# Offline repair should not find anything
+final_check_scratch() {
+ __repair_check_scratch
+ res=$?
+ if [ $res -eq 2 ]; then
+ echo "scratch fs went offline?"
+ _scratch_mount
+ _scratch_unmount
+ __repair_check_scratch
+ fi
+ test "$res" -ne 0 && echo "repair returned $res?"
+}
+
+echo "+ Part 0: See if runtime can recover the unlinked list" | tee -a $seqres.full
+format_scratch
+_kernlog "part 0"
+_scratch_mount
+exercise_scratch
+_scratch_unmount
+final_check_scratch
+
+echo "+ Part 1: See if bulkstat can recover the unlinked list" | tee -a $seqres.full
+format_scratch
+_kernlog "part 1"
+_scratch_mount
+$XFS_IO_PROG -c 'bulkstat' $SCRATCH_MNT > /dev/null
+exercise_scratch
+_scratch_unmount
+final_check_scratch
+
+echo "+ Part 2: See if quotacheck can recover the unlinked list" | tee -a $seqres.full
+if [ -f /proc/fs/xfs/xqmstat ]; then
+ MOUNT_OPTIONS="$orig_mount_options"
+ _qmount_option 'quota'
+ format_scratch
+ _kernlog "part 2"
+ _scratch_mount
+ exercise_scratch
+ _scratch_unmount
+ final_check_scratch
+fi
+
+# success, all done
+echo Silence is golden
+status=0
+exit
--- /dev/null
+QA output created by 602
++ Part 0: See if runtime can recover the unlinked list
++ Part 1: See if bulkstat can recover the unlinked list
++ Part 2: See if quotacheck can recover the unlinked list
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 603
+#
+# Functional test of using online repair to fix unlinked inodes on a clean
+# filesystem that never got cleaned up.
+#
+. ./common/preamble
+_begin_fstest auto online_repair
+
+. ./common/filter
+. ./common/fuzzy
+. ./common/quota
+
+# real QA test starts here
+
+_supported_fs xfs
+_require_xfs_db_command iunlink
+# The iunlink bucket repair code wasn't added to the AGI repair code
+# until after the directory repair code was merged
+_require_xfs_io_command repair -R directory
+_require_scratch_nocheck # repair doesn't like single-AG fs
+
+# From the AGI definition
+XFS_AGI_UNLINKED_BUCKETS=64
+
+# Try to make each iunlink bucket have this many inodes in it.
+IUNLINK_BUCKETLEN=5
+
+# Disable quota since quotacheck will break this test
+_qmount_option 'noquota'
+
+format_scratch() {
+ _scratch_mkfs -d agcount=1 | _filter_mkfs 2> "${tmp}.mkfs" >> $seqres.full
+ source "${tmp}.mkfs"
+ test "${agcount}" -eq 1 || _notrun "test requires 1 AG for error injection"
+
+ local nr_iunlinks="$((IUNLINK_BUCKETLEN * XFS_AGI_UNLINKED_BUCKETS))"
+ readarray -t BADINODES < <(_scratch_xfs_db -x -c "iunlink -n $nr_iunlinks" | awk '{print $4}')
+}
+
+__repair_check_scratch() {
+ _scratch_xfs_repair -o force_geometry -n 2>&1 | \
+ tee -a $seqres.full | \
+ grep -E '(disconnected inode.*would move|next_unlinked in inode|unlinked bucket.*is.*in ag)'
+ return "${PIPESTATUS[0]}"
+}
+
+corrupt_scratch() {
+ # How far into the iunlink bucket chain do we target inodes for corruption?
+ # 1 = target the inode pointed to by the AGI
+ # 3 = middle of bucket list
+ # 5 = last element in bucket
+ local corruption_bucket_depth="$1"
+ if ((corruption_bucket_depth < 1 || corruption_bucket_depth > IUNLINK_BUCKETLEN)); then
+ echo "${corruption_bucket_depth}: Value must be between 1 and ${IUNLINK_BUCKETLEN}."
+ return 1
+ fi
+
+ # Index of the inode numbers within BADINODES
+ local bad_ino1_idx=$(( (IUNLINK_BUCKETLEN - corruption_bucket_depth) * XFS_AGI_UNLINKED_BUCKETS))
+ local bad_ino2_idx=$((bad_ino1_idx + 1))
+
+ # Inode numbers to target
+ local bad_ino1="${BADINODES[bad_ino1_idx]}"
+ local bad_ino2="${BADINODES[bad_ino2_idx]}"
+ printf "bad: 0x%x 0x%x\n" "${bad_ino1}" "${bad_ino2}" | _tee_kernlog >> $seqres.full
+
+ # Bucket within AGI 0's iunlinked array.
+ local ino1_bucket="$((bad_ino1 % XFS_AGI_UNLINKED_BUCKETS))"
+ local ino2_bucket="$((bad_ino2 % XFS_AGI_UNLINKED_BUCKETS))"
+
+ # The first bad inode stays on the unlinked list but gets a nonzero
+ # nlink; the second bad inode is removed from the unlinked list but
+ # keeps its zero nlink
+ _scratch_xfs_db -x \
+ -c "inode ${bad_ino1}" -c "write -d core.nlinkv2 5555" \
+ -c "agi 0" -c "fuzz -d unlinked[${ino2_bucket}] ones" -c "print unlinked" >> $seqres.full
+
+ local iwatch=()
+ local idx
+
+ # Make a list of the adjacent iunlink bucket inodes for the first inode
+ # that we targeted.
+ if [ "${corruption_bucket_depth}" -gt 1 ]; then
+ # Previous ino in bucket
+ idx=$(( (IUNLINK_BUCKETLEN - corruption_bucket_depth + 1) * XFS_AGI_UNLINKED_BUCKETS))
+ iwatch+=("${BADINODES[idx]}")
+ fi
+ iwatch+=("${bad_ino1}")
+ if [ "$((corruption_bucket_depth + 1))" -lt "${IUNLINK_BUCKETLEN}" ]; then
+ # Next ino in bucket
+ idx=$(( (IUNLINK_BUCKETLEN - corruption_bucket_depth - 1) * XFS_AGI_UNLINKED_BUCKETS))
+ iwatch+=("${BADINODES[idx]}")
+ fi
+
+ # Make a list of the adjacent iunlink bucket inodes for the second
+ # inode that we targeted.
+ if [ "${corruption_bucket_depth}" -gt 1 ]; then
+ # Previous ino in bucket
+ idx=$(( (IUNLINK_BUCKETLEN - corruption_bucket_depth + 1) * XFS_AGI_UNLINKED_BUCKETS))
+ iwatch+=("${BADINODES[idx + 1]}")
+ fi
+ iwatch+=("${bad_ino2}")
+ if [ "$((corruption_bucket_depth + 1))" -lt "${IUNLINK_BUCKETLEN}" ]; then
+ # Next ino in bucket
+ idx=$(( (IUNLINK_BUCKETLEN - corruption_bucket_depth - 1) * XFS_AGI_UNLINKED_BUCKETS))
+ iwatch+=("${BADINODES[idx + 1]}")
+ fi
+
+ # Construct a grep string for tracepoints.
+ GREP_STR="(xrep_attempt|xrep_done|bucket ${ino1_bucket} |bucket ${ino2_bucket} |bucket ${fuzz_bucket} "
+ GREP_STR="(xrep_attempt|xrep_done|bucket ${ino1_bucket} |bucket ${ino2_bucket} "
+ for ino in "${iwatch[@]}"; do
+ f="$(printf "|ino 0x%x" "${ino}")"
+ GREP_STR="${GREP_STR}${f}"
+ done
+ GREP_STR="${GREP_STR})"
+ echo "grep -E \"${GREP_STR}\"" >> $seqres.full
+
+ # Dump everything we did to to the full file.
+ local db_dump=(-c 'agi 0' -c 'print unlinked')
+ db_dump+=(-c 'addr root' -c 'print')
+ test "${ino1_bucket}" -gt 0 && \
+ db_dump+=(-c "dump_iunlinked -a 0 -b $((ino1_bucket - 1))")
+ db_dump+=(-c "dump_iunlinked -a 0 -b ${ino1_bucket}")
+ db_dump+=(-c "dump_iunlinked -a 0 -b ${ino2_bucket}")
+ test "${ino2_bucket}" -lt 63 && \
+ db_dump+=(-c "dump_iunlinked -a 0 -b $((ino2_bucket + 1))")
+ db_dump+=(-c "inode $bad_ino1" -c 'print core.nlinkv2 v3.inumber next_unlinked')
+ db_dump+=(-c "inode $bad_ino2" -c 'print core.nlinkv2 v3.inumber next_unlinked')
+ _scratch_xfs_db "${db_dump[@]}" >> $seqres.full
+
+ # Test run of repair to make sure we find disconnected inodes
+ __repair_check_scratch | \
+ sed -e 's/disconnected inode \([0-9]*\)/disconnected inode XXXXXX/g' \
+ -e 's/next_unlinked in inode \([0-9]*\)/next_unlinked in inode XXXXXX/g' \
+ -e 's/unlinked bucket \([0-9]*\) is \([0-9]*\) in ag \([0-9]*\) .inode=\([0-9]*\)/unlinked bucket YY is XXXXXX in ag Z (inode=AAAAAA/g' | \
+ uniq -c >> $seqres.full
+ res=${PIPESTATUS[0]}
+ test "$res" -ne 0 || echo "repair returned $res after corruption?"
+}
+
+exercise_scratch() {
+ # Create a bunch of files...
+ declare -A inums
+ for ((i = 0; i < (XFS_AGI_UNLINKED_BUCKETS * 2); i++)); do
+ touch "${SCRATCH_MNT}/${i}" || break
+ inums["${i}"]="$(stat -c %i "${SCRATCH_MNT}/${i}")"
+ done
+
+ # ...then delete them to exercise the unlinked buckets
+ for ((i = 0; i < (XFS_AGI_UNLINKED_BUCKETS * 2); i++)); do
+ if ! rm -f "${SCRATCH_MNT}/${i}"; then
+ echo "rm failed on inum ${inums[$i]}"
+ break
+ fi
+ done
+}
+
+# Offline repair should not find anything
+final_check_scratch() {
+ __repair_check_scratch
+ res=$?
+ if [ $res -eq 2 ]; then
+ echo "scratch fs went offline?"
+ _scratch_mount
+ _scratch_unmount
+ __repair_check_scratch
+ fi
+ test "$res" -ne 0 && echo "repair returned $res?"
+}
+
+echo "+ Part 1: See if scrub can recover the unlinked list" | tee -a $seqres.full
+format_scratch
+_kernlog "no bad inodes"
+_scratch_mount
+_scratch_scrub >> $seqres.full
+exercise_scratch
+_scratch_unmount
+final_check_scratch
+
+echo "+ Part 2: Corrupt the first inode in the bucket" | tee -a $seqres.full
+format_scratch
+corrupt_scratch 1
+_scratch_mount
+_scratch_scrub >> $seqres.full
+exercise_scratch
+_scratch_unmount
+final_check_scratch
+
+echo "+ Part 3: Corrupt the middle inode in the bucket" | tee -a $seqres.full
+format_scratch
+corrupt_scratch 3
+_scratch_mount
+_scratch_scrub >> $seqres.full
+exercise_scratch
+_scratch_unmount
+final_check_scratch
+
+echo "+ Part 4: Corrupt the last inode in the bucket" | tee -a $seqres.full
+format_scratch
+corrupt_scratch 5
+_scratch_mount
+_scratch_scrub >> $seqres.full
+exercise_scratch
+_scratch_unmount
+final_check_scratch
+
+# success, all done
+echo Silence is golden
+status=0
+exit
--- /dev/null
+QA output created by 603
++ Part 1: See if scrub can recover the unlinked list
++ Part 2: Corrupt the first inode in the bucket
++ Part 3: Corrupt the middle inode in the bucket
++ Part 4: Corrupt the last inode in the bucket
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) Meta Platforms, Inc. and affiliates.
+#
+# FS QA Test No. 604
+#
+# Regression test for patch "xfs: fix internal error from AGFL exhaustion".
+#
+. ./common/preamble
+_begin_fstest auto prealloc punch
+
+. ./common/filter
+
+_supported_fs xfs
+_require_scratch
+_require_xfs_io_command "falloc"
+_require_xfs_io_command "fpunch"
+_require_test_program punch-alternating
+_fixed_by_kernel_commit f63a5b3769ad "xfs: fix internal error from AGFL exhaustion"
+
+# Disable the rmapbt so we only need to worry about splitting the bnobt and
+# cntbt at the same time.
+opts=
+if $MKFS_XFS_PROG |& grep -q rmapbt; then
+ opts="-m rmapbt=0"
+fi
+_scratch_mkfs $opts | _filter_mkfs > /dev/null 2> "$tmp.mkfs"
+. "$tmp.mkfs"
+_scratch_mount
+
+alloc_block_len=$((_fs_has_crcs ? 56 : 16))
+allocbt_leaf_maxrecs=$(((dbsize - alloc_block_len) / 8))
+allocbt_node_maxrecs=$(((dbsize - alloc_block_len) / 12))
+
+# Create a big file with a size such that the punches below create the exact
+# free extents we want.
+num_holes=$((allocbt_leaf_maxrecs * allocbt_node_maxrecs - 1))
+falloc_size=$((9 * dbsize + num_holes * dbsize * 2))
+$XFS_IO_PROG -c "falloc 0 $falloc_size" -f "$SCRATCH_MNT/big" ||
+ _notrun "Not enough space on device for falloc_size=$(echo "scale=2; $falloc_size / 1073741824" | $BC -q)GB and bs=$dbsize"
+
+# Fill in any small free extents in AG 0. After this, there should be only one,
+# large free extent.
+_scratch_unmount
+mapfile -t gaps < <(_scratch_xfs_db -c 'agf 0' -c 'addr cntroot' -c btdump |
+ $SED_PROG -rn 's/^[0-9]+:\[[0-9]+,([0-9]+)\].*/\1/p' |
+ tac | tail -n +2)
+_scratch_mount
+for gap_i in "${!gaps[@]}"; do
+ gap=${gaps[$gap_i]}
+ $XFS_IO_PROG -c "falloc 0 $((gap * dbsize))" -f "$SCRATCH_MNT/gap$gap_i"
+done
+
+# Create enough free space records to make the bnobt and cntbt both full,
+# 2-level trees, plus one more record to make them split all the way to the
+# root and become 3-level trees. After this, there is a 7-block free extent in
+# the rightmost leaf of the cntbt, and all of the leaves of the cntbt other
+# than the rightmost two are full. Without the fix, the free list is also
+# empty.
+$XFS_IO_PROG -c "fpunch $dbsize $((7 * dbsize))" "$SCRATCH_MNT/big"
+"$here/src/punch-alternating" -o 9 "$SCRATCH_MNT/big"
+
+# Do an arbitrary operation that refills the free list. Without the fix, this
+# will allocate 6 blocks from the 7-block free extent in the rightmost leaf of
+# the cntbt, then try to insert the remaining 1 block free extent in the
+# leftmost leaf of the cntbt. But that leaf is full, so this tries to split the
+# leaf and fails because the free list is empty, returning EFSCORRUPTED.
+$XFS_IO_PROG -c "fpunch 0 $dbsize" "$SCRATCH_MNT/big"
+
+echo "Silence is golden"
+status=0
+exit
--- /dev/null
+QA output created by 604
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2024 Oracle, Inc. All Rights Reserved.
+#
+# FS QA Test No. 605
+#
+# Test metadump/mdrestore's ability to dump a dirty log and restore it
+# correctly.
+#
+. ./common/preamble
+_begin_fstest auto quick metadump log logprint punch
+
+# Override the default cleanup function.
+_cleanup()
+{
+ cd /
+ rm -r -f $tmp.*
+ _xfs_cleanup_verify_metadump
+}
+
+# Import common functions.
+. ./common/dmflakey
+. ./common/inject
+. ./common/metadump
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_test
+_require_loop
+_require_xfs_debug
+_require_xfs_io_error_injection log_item_pin
+_require_dm_target flakey
+_require_xfs_io_command "pwrite"
+_require_test_program "punch-alternating"
+_xfs_setup_verify_metadump
+
+testfile=${SCRATCH_MNT}/testfile
+
+echo "Format filesystem on scratch device"
+_scratch_mkfs >> $seqres.full 2>&1
+
+echo "Initialize and mount filesystem on flakey device"
+_init_flakey
+_load_flakey_table $FLAKEY_ALLOW_WRITES
+_mount_flakey
+
+echo "Create test file"
+$XFS_IO_PROG -s -f -c "pwrite 0 5M" $testfile >> $seqres.full
+
+echo "Punch alternative blocks of test file"
+$here/src/punch-alternating $testfile
+
+echo "Mount cycle the filesystem on flakey device"
+_unmount_flakey
+_mount_flakey
+
+device=$(readlink -f $FLAKEY_DEV)
+device=$(_short_dev $device)
+
+echo "Pin log items in the AIL"
+echo 1 > /sys/fs/xfs/${device}/errortag/log_item_pin
+
+echo "Create two checkpoint transactions on ondisk log"
+for ct in $(seq 1 2); do
+ offset=$($XFS_IO_PROG -c 'fiemap' $testfile | tac | grep -v hole | \
+ head -n 1 | awk -F '[\\[.]' '{ print $2 * 512; }')
+ $XFS_IO_PROG -c "truncate $offset" -c fsync $testfile
+done
+
+echo "Drop writes to filesystem from here onwards"
+_load_flakey_table $FLAKEY_DROP_WRITES
+
+echo "Unpin log items in AIL"
+echo 0 > /sys/fs/xfs/${device}/errortag/log_item_pin
+
+echo "Unmount filesystem on flakey device"
+_unmount_flakey
+
+echo "Clean up flakey device"
+_cleanup_flakey
+
+echo -n "Filesystem has a "
+_print_logstate
+
+echo "Create metadump file, restore it and check restored fs"
+_xfs_verify_metadumps '-a -o'
+
+# Mount the fs to replay the contents from the dirty log.
+_scratch_mount
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 605
+Format filesystem on scratch device
+Initialize and mount filesystem on flakey device
+Create test file
+Punch alternative blocks of test file
+Mount cycle the filesystem on flakey device
+Pin log items in the AIL
+Create two checkpoint transactions on ondisk log
+Drop writes to filesystem from here onwards
+Unpin log items in AIL
+Unmount filesystem on flakey device
+Clean up flakey device
+Filesystem has a dirty log
+Create metadump file, restore it and check restored fs
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2024 Red Hat, Inc. All Rights Reserved.
+#
+# FS QA Test No. 606
+#
+# Test xfs_growfs with "too-small" size expansion, which lead to a delta of "0"
+# in xfs_growfs_data_private. This's a regression test of 84712492e6da ("xfs:
+# short circuit xfs_growfs_data_private() if delta is zero").
+#
+. ./common/preamble
+_begin_fstest auto quick growfs
+
+_cleanup()
+{
+ local dev
+ $UMOUNT_PROG $LOOP_MNT 2>/dev/null
+ dev=$(losetup -j testfile | cut -d: -f1)
+ losetup -d $dev 2>/dev/null
+ rm -rf $LOOP_IMG $LOOP_MNT
+ cd /
+ rm -f $tmp.*
+}
+
+# real QA test starts here
+_supported_fs xfs
+_fixed_by_kernel_commit 84712492e6da \
+ "xfs: short circuit xfs_growfs_data_private() if delta is zero"
+_require_test
+_require_loop
+_require_xfs_io_command "truncate"
+_require_command "$XFS_GROWFS_PROG" xfs_growfs
+
+LOOP_IMG=$TEST_DIR/$seq.dev
+LOOP_MNT=$TEST_DIR/$seq.mnt
+rm -rf $LOOP_IMG $LOOP_MNT
+mkdir -p $LOOP_MNT
+
+# 1G image
+$XFS_IO_PROG -f -c "truncate 1073741824" $LOOP_IMG
+$MKFS_XFS_PROG -f $LOOP_IMG >$seqres.full
+# Extend by just 8K, expected to start with the last full-size AG ends of
+# above 1G block device.
+$XFS_IO_PROG -f -c "truncate 1073750016" $LOOP_IMG
+_mount -oloop $LOOP_IMG $LOOP_MNT
+# A known bug shows "XFS_IOC_FSGROWFSDATA xfsctl failed: No space left on
+# device" at here, refer to _fixed_by_kernel_commit above
+$XFS_GROWFS_PROG $LOOP_MNT >$seqres.full
+if [ $? -ne 0 ];then
+ echo "xfs_growfs fails!"
+fi
+
+echo "Silence is golden"
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 606
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022-2024 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 607
+#
+# This is a regression test for "xfs: Fix false ENOSPC when performing direct
+# write on a delalloc extent in cow fork". If there is a lot of free space but
+# it is very fragmented, it's possible that a very large delalloc reservation
+# could be created in the CoW fork by a buffered write. If a directio write
+# tries to convert the delalloc reservation to a real extent, it's possible
+# that the allocation will succeed but fail to convert even the first block of
+# the directio write range. In this case, XFS will return ENOSPC even though
+# all it needed to do was to keep converting until the allocator returns ENOSPC
+# or the first block of the direct write got some space.
+#
+. ./common/preamble
+_begin_fstest auto quick clone
+
+_cleanup()
+{
+ cd /
+ rm -f $file1 $file2 $fragmentedfile
+}
+
+# Import common functions.
+. ./common/reflink
+. ./common/inject
+
+# real QA test starts here
+_fixed_by_kernel_commit d62113303d69 \
+ "xfs: Fix false ENOSPC when performing direct write on a delalloc extent in cow fork"
+
+# Modify as appropriate.
+_supported_fs xfs
+_require_test_program "punch-alternating"
+_require_test_reflink
+_require_xfs_io_error_injection "bmap_alloc_minlen_extent"
+_require_test_delalloc
+
+file1=$TEST_DIR/file1.$seq
+file2=$TEST_DIR/file2.$seq
+fragmentedfile=$TEST_DIR/fragmentedfile.$seq
+
+rm -f $file1 $file2 $fragmentedfile
+
+# COW operates on pages, so we must not perform operations in units smaller
+# than a page.
+blksz=$(_get_file_block_size $TEST_DIR)
+pagesz=$(_get_page_size)
+if (( $blksz < $pagesz )); then
+ blksz=$pagesz
+fi
+
+echo "Create source file"
+$XFS_IO_PROG -f -c "pwrite 0 $((blksz * 256))" $file1 >> $seqres.full
+
+sync
+
+echo "Create Reflinked file"
+_cp_reflink $file1 $file2 >> $seqres.full
+
+echo "Set cowextsize"
+$XFS_IO_PROG -c "cowextsize $((blksz * 128))" -c stat $file1 >> $seqres.full
+
+echo "Fragment FS"
+$XFS_IO_PROG -f -c "pwrite 0 $((blksz * 512))" $fragmentedfile >> $seqres.full
+sync
+$here/src/punch-alternating $fragmentedfile
+
+echo "Allocate block sized extent from now onwards"
+_test_inject_error bmap_alloc_minlen_extent 1
+
+echo "Create big delalloc extent in CoW fork"
+$XFS_IO_PROG -c "pwrite 0 $blksz" $file1 >> $seqres.full
+
+sync
+
+$XFS_IO_PROG -c 'bmap -elpv' -c 'bmap -celpv' $file1 &>> $seqres.full
+
+echo "Direct I/O write at offset 3FSB"
+$XFS_IO_PROG -d -c "pwrite $((blksz * 3)) $((blksz * 2))" $file1 >> $seqres.full
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 607
+Create source file
+Create Reflinked file
+Set cowextsize
+Fragment FS
+Allocate block sized extent from now onwards
+Create big delalloc extent in CoW fork
+Direct I/O write at offset 3FSB
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 708
+#
+# Race fsstress and bnobt repair for a while to see if we crash or livelock.
+#
+. ./common/preamble
+_begin_fstest online_repair dangerous_fsstress_repair
+
+_cleanup() {
+ _scratch_xfs_stress_scrub_cleanup &> /dev/null
+ cd /
+ rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_online_repair
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_online_repair -s "repair bnobt %agno%"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
--- /dev/null
+QA output created by 708
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 709
+#
+# Race fsstress and inobt repair for a while to see if we crash or livelock.
+#
+. ./common/preamble
+_begin_fstest online_repair dangerous_fsstress_repair
+
+_cleanup() {
+ _scratch_xfs_stress_scrub_cleanup &> /dev/null
+ cd /
+ rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_online_repair
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_online_repair -s "repair inobt %agno%"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
--- /dev/null
+QA output created by 709
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 710
+#
+# Race fsstress and refcountbt repair for a while to see if we crash or livelock.
+#
+. ./common/preamble
+_begin_fstest online_repair dangerous_fsstress_repair
+
+_cleanup() {
+ _scratch_xfs_stress_scrub_cleanup &> /dev/null
+ cd /
+ rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_online_repair
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_require_xfs_has_feature "$SCRATCH_MNT" reflink
+_scratch_xfs_stress_online_repair -s "repair refcountbt %agno%"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
--- /dev/null
+QA output created by 710
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 711
+#
+# Race fsstress and superblock repair for a while to see if we crash or livelock.
+#
+. ./common/preamble
+_begin_fstest online_repair dangerous_fsstress_repair
+
+_cleanup() {
+ _scratch_xfs_stress_scrub_cleanup &> /dev/null
+ cd /
+ rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_online_repair
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_online_repair -a 1 -s "repair sb %agno%"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
--- /dev/null
+QA output created by 711
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 712
+#
+# Race fsstress and agf repair for a while to see if we crash or livelock.
+#
+. ./common/preamble
+_begin_fstest online_repair dangerous_fsstress_repair
+
+_cleanup() {
+ _scratch_xfs_stress_scrub_cleanup &> /dev/null
+ cd /
+ rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_online_repair
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_online_repair -s "repair agf %agno%"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
--- /dev/null
+QA output created by 712
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 713
+#
+# Race fsstress and agfl repair for a while to see if we crash or livelock.
+#
+. ./common/preamble
+_begin_fstest online_repair dangerous_fsstress_repair
+
+_cleanup() {
+ _scratch_xfs_stress_scrub_cleanup &> /dev/null
+ cd /
+ rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_online_repair
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_online_repair -s "repair agfl %agno%"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
--- /dev/null
+QA output created by 713
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 714
+#
+# Race fsstress and agi repair for a while to see if we crash or livelock.
+#
+. ./common/preamble
+_begin_fstest online_repair dangerous_fsstress_repair
+
+_cleanup() {
+ _scratch_xfs_stress_scrub_cleanup &> /dev/null
+ cd /
+ rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_online_repair
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_online_repair -s "repair agi %agno%"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
--- /dev/null
+QA output created by 714
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc. All Rights Reserved.
+#
+# FS QA Test No. 715
+#
+# Race fsstress and inode record repair for a while to see if we crash or
+# livelock.
+#
+. ./common/preamble
+_begin_fstest online_repair dangerous_fsstress_repair
+
+_cleanup() {
+ _scratch_xfs_stress_scrub_cleanup &> /dev/null
+ cd /
+ rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_online_repair
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_online_repair -s "repair inode" -t "%file%"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
--- /dev/null
+QA output created by 715
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 716
+#
+# Make sure online repair can handle rebuilding xattrs when the data fork is
+# in btree format and we cannot just zap the attr fork.
+
+. ./common/preamble
+_begin_fstest auto quick online_repair
+
+# Override the default cleanup function.
+_cleanup()
+{
+ cd /
+}
+
+# Import common functions.
+. ./common/inject
+. ./common/filter
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_io_error_injection "force_repair"
+_require_xfs_io_command "falloc"
+_require_xfs_io_command "repair"
+_require_test_program "punch-alternating"
+
+_scratch_mkfs > $tmp.mkfs
+_scratch_mount
+
+_require_scratch_xfs_scrub
+
+# Force data device extents so that we can create a file with the exact bmbt
+# that we need regardless of rt configuration.
+_xfs_force_bdev data $SCRATCH_MNT
+
+file=$SCRATCH_MNT/moofile
+touch $file
+
+# Create some xattrs so that we have to rebuild them.
+$XFS_IO_PROG -f -c 'pwrite -S 0x58 0 76' $file.txt >> $seqres.full
+$SETFATTR_PROG -n user.SGI_BCL_FILE -v "$(cat $file.txt)" $file
+
+$SETFATTR_PROG -n user.crtime_usec -v 12345678 $file
+
+blksz=$(_get_file_block_size $SCRATCH_MNT)
+ino=$(stat -c '%i' $file)
+
+# Figure out how many extents we need to have to create a data fork that's in
+# btree format.
+umount $SCRATCH_MNT
+di_forkoff=$(_scratch_xfs_db -c "inode $ino" -c "p core.forkoff" | \
+ awk '{print $3}')
+_scratch_xfs_db -c "inode $ino" -c "p" >> $seqres.full
+_scratch_mount
+
+# Create a data fork in btree format
+min_ext_for_btree=$((di_forkoff * 8 / 16))
+$XFS_IO_PROG -c "falloc 0 $(( (min_ext_for_btree + 1) * 2 * blksz))" $file
+$here/src/punch-alternating $file
+
+# Make sure the data fork is in btree format.
+umount $SCRATCH_MNT
+_scratch_xfs_db -c "inode $ino" -c "p core.format" | grep -q "btree" || \
+ echo "data fork not in btree format?"
+echo "about to start test" >> $seqres.full
+_scratch_xfs_db -c "inode $ino" -c "p" >> $seqres.full
+_scratch_mount
+
+# Force repair the xattr fork
+_scratch_inject_error force_repair
+$XFS_IO_PROG -x -c 'repair xattr' $file 2>&1 | tee $tmp.repair.log
+grep -q 'Operation not supported' $tmp.repair.log && \
+ _notrun "online xattr repair not supported"
+
+# If online repair did it correctly, the filesystem won't be corrupt. Let the
+# post-test check do its thing.
+
+# success, all done
+echo "Silence is golden."
+status=0
+exit
--- /dev/null
+QA output created by 716
+Silence is golden.
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc. All Rights Reserved.
+#
+# FS QA Test No. 717
+#
+# Race fsstress and data fork repair for a while to see if we crash or livelock.
+#
+. ./common/preamble
+_begin_fstest online_repair dangerous_fsstress_repair
+
+_cleanup() {
+ _scratch_xfs_stress_scrub_cleanup &> /dev/null
+ cd /
+ rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_online_repair
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_online_repair -s "repair bmapbtd" -t "%datafile%"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
--- /dev/null
+QA output created by 717
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc. All Rights Reserved.
+#
+# FS QA Test No. 718
+#
+# Race fsstress and attr fork repair for a while to see if we crash or livelock.
+#
+. ./common/preamble
+_begin_fstest online_repair dangerous_fsstress_repair
+
+_cleanup() {
+ _scratch_xfs_stress_scrub_cleanup &> /dev/null
+ cd /
+ rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+. ./common/attr
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_attrs
+_require_xfs_stress_online_repair
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_online_repair -x 'xattr' -s "repair bmapbta" -t "%attrfile%"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
--- /dev/null
+QA output created by 718
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc. All Rights Reserved.
+#
+# FS QA Test No. 719
+#
+# Race fsstress and CoW fork repair for a while to see if we crash or livelock.
+#
+. ./common/preamble
+_begin_fstest online_repair dangerous_fsstress_repair
+
+_cleanup() {
+ _scratch_xfs_stress_scrub_cleanup &> /dev/null
+ cd /
+ rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+. ./common/reflink
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_online_repair
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_require_xfs_has_feature "$SCRATCH_MNT" reflink
+_scratch_xfs_stress_online_repair -s "repair bmapbtc" -t "%cowfile%"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
--- /dev/null
+QA output created by 719
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 720
+#
+# Ensure that the sysadmin won't hit EDQUOT while repairing file data forks
+# even if the file's quota limits have been exceeded. This tests the quota
+# reservation handling inside the bmap btree rebuilding code.
+#
+. ./common/preamble
+_begin_fstest online_repair
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/quota
+
+# real QA test starts here
+
+# Modify as appropriate.
+_supported_fs xfs
+_require_xfs_io_command "falloc"
+_require_quota
+_require_user
+_require_test_program "punch-alternating"
+_require_scratch
+_require_xfs_stress_online_repair
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_qmount_option usrquota
+_qmount
+
+blocksize=$(_get_block_size $SCRATCH_MNT)
+alloc_unit=$(_get_file_block_size $SCRATCH_MNT)
+
+# Make sure we can actually repair a data fork
+scratchfile=$SCRATCH_MNT/file
+touch $scratchfile
+$XFS_IO_PROG -x -c 'inject force_repair' $SCRATCH_MNT
+__stress_scrub_check_commands "$scratchfile" "" 'repair bmapbtd'
+
+# Compute the number of extent records needed to guarantee btree format,
+# assuming 16 bytes for each ondisk extent record
+bmbt_records=$(( (blocksize / 16) * 5 / 4 ))
+total_size=$(( bmbt_records * 2 * alloc_unit ))
+
+# Create a file with a data fork in bmap btree format
+$XFS_IO_PROG -c "falloc 0 $total_size" $scratchfile >> $seqres.full
+$here/src/punch-alternating $scratchfile
+
+# Set a low quota hardlimit for an unprivileged uid and chown the file to it
+echo "set up quota" >> $seqres.full
+$XFS_QUOTA_PROG -x -c "limit -u bhard=$((alloc_unit * 2)) $qa_user" $SCRATCH_MNT
+chown $qa_user $scratchfile
+$XFS_QUOTA_PROG -x -c 'report -u' $SCRATCH_MNT >> $seqres.full
+
+# Rebuild the data fork
+echo "repairs" >> $seqres.full
+$XFS_IO_PROG -x -c 'inject force_repair' -c 'repair bmapbtd' $scratchfile
+$XFS_QUOTA_PROG -x -c 'report -u' $SCRATCH_MNT >> $seqres.full
+
+# Fail at appending the file as qa_user to ensure quota enforcement works
+echo "fail quota" >> $seqres.full
+su - "$qa_user" -c "$XFS_IO_PROG -c 'pwrite 10g 1' $scratchfile" >> $seqres.full
+$XFS_QUOTA_PROG -x -c 'report -u' $SCRATCH_MNT >> $seqres.full
+
+# success, all done
+echo Silence is golden
+status=0
+exit
--- /dev/null
+QA output created by 720
+pwrite: Disk quota exceeded
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc. All Rights Reserved.
+#
+# FS QA Test No. 721
+#
+# Race fsstress and symlink repair for a while to see if we crash or livelock.
+# We can't open special files directly for scrubbing, so we use xfs_scrub(8).
+#
+. ./common/preamble
+_begin_fstest online_repair dangerous_fsstress_repair
+
+_cleanup() {
+ _scratch_xfs_stress_scrub_cleanup &> /dev/null
+ cd /
+ rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_online_repair
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+XFS_SCRUB_PHASE=3 _scratch_xfs_stress_online_repair -x 'symlink' -S '-k'
+
+# success, all done
+echo Silence is golden
+status=0
+exit
--- /dev/null
+QA output created by 721
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc. All Rights Reserved.
+#
+# FS QA Test No. 722
+#
+# Race fsstress and special file repair for a while to see if we crash or
+# livelock. We can't open special files directly for scrubbing, so we use
+# xfs_scrub(8).
+#
+. ./common/preamble
+_begin_fstest online_repair dangerous_fsstress_repair
+
+_cleanup() {
+ _scratch_xfs_stress_scrub_cleanup &> /dev/null
+ cd /
+ rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_online_repair
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+XFS_SCRUB_PHASE=3 _scratch_xfs_stress_online_repair -x 'mknod' -S '-k'
+
+# success, all done
+echo Silence is golden
+status=0
+exit
--- /dev/null
+QA output created by 722
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc. All Rights Reserved.
+#
+# FS QA Test No. 723
+#
+# Race fsstress and user quota repair for a while to see if we crash or
+# livelock.
+#
+. ./common/preamble
+_begin_fstest online_repair dangerous_fsstress_repair
+
+_cleanup() {
+ _scratch_xfs_stress_scrub_cleanup &> /dev/null
+ cd /
+ rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+. ./common/quota
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_online_repair
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_require_xfs_quota_acct_enabled "$SCRATCH_DEV" usrquota
+_scratch_xfs_stress_online_repair -s "repair usrquota"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
--- /dev/null
+QA output created by 723
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc. All Rights Reserved.
+#
+# FS QA Test No. 724
+#
+# Race fsstress and group quota repair for a while to see if we crash or
+# livelock.
+#
+. ./common/preamble
+_begin_fstest online_repair dangerous_fsstress_repair
+
+_cleanup() {
+ _scratch_xfs_stress_scrub_cleanup &> /dev/null
+ cd /
+ rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+. ./common/quota
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_online_repair
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_require_xfs_quota_acct_enabled "$SCRATCH_DEV" grpquota
+_scratch_xfs_stress_online_repair -s "repair grpquota"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
--- /dev/null
+QA output created by 724
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc. All Rights Reserved.
+#
+# FS QA Test No. 725
+#
+# Race fsstress and project quota repair for a while to see if we crash or
+# livelock.
+#
+. ./common/preamble
+_begin_fstest online_repair dangerous_fsstress_repair
+
+_cleanup() {
+ _scratch_xfs_stress_scrub_cleanup &> /dev/null
+ cd /
+ rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+. ./common/quota
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_online_repair
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_require_xfs_quota_acct_enabled "$SCRATCH_DEV" prjquota
+_scratch_xfs_stress_online_repair -s "repair prjquota"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
--- /dev/null
+QA output created by 725
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc. All Rights Reserved.
+#
+# FS QA Test No. 726
+#
+# Race fsstress and quotacheck repair for a while to see if we crash or
+# livelock.
+#
+. ./common/preamble
+_begin_fstest online_repair dangerous_fsstress_repair
+
+_cleanup() {
+ _scratch_xfs_stress_scrub_cleanup &> /dev/null
+ cd /
+ rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+. ./common/quota
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_online_repair
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_require_xfs_quota_acct_enabled "$SCRATCH_DEV" any
+_scratch_xfs_stress_online_repair -s "repair quotacheck"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
--- /dev/null
+QA output created by 726
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc. All Rights Reserved.
+#
+# FS QA Test No. 727
+#
+# Race fsstress and quotacheck scrub for a while to see if we crash or
+# livelock.
+#
+. ./common/preamble
+_begin_fstest scrub dangerous_fsstress_scrub
+
+_cleanup() {
+ _scratch_xfs_stress_scrub_cleanup &> /dev/null
+ cd /
+ rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+. ./common/quota
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_scrub
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_require_xfs_quota_acct_enabled "$SCRATCH_DEV" any
+_scratch_xfs_stress_scrub -s "scrub quotacheck"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
--- /dev/null
+QA output created by 727
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 728
+#
+# Race fsstress and inode link count repair for a while to see if we crash or
+# livelock.
+#
+. ./common/preamble
+_begin_fstest online_repair dangerous_fsstress_repair
+
+_cleanup() {
+ _scratch_xfs_stress_scrub_cleanup &> /dev/null
+ cd /
+ rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_online_repair
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_online_repair -x "dir" -s "repair nlinks"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
--- /dev/null
+QA output created by 728
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 729
+#
+# Race fsstress and nlinks scrub for a while to see if we crash or livelock.
+#
+. ./common/preamble
+_begin_fstest scrub dangerous_fsstress_scrub
+
+_cleanup() {
+ _scratch_xfs_stress_scrub_cleanup &> /dev/null
+ cd /
+ rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_scrub
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_scrub -x "dir" -s "scrub nlinks"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
--- /dev/null
+QA output created by 729
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 730
+#
+# Populate a XFS filesystem and fuzz every fscounter field.
+# Use xfs_scrub to fix the corruption.
+#
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_scrub dangerous_online_repair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+echo "Fuzz fscounters"
+test -z "$SCRATCH_XFS_LIST_METADATA_FIELDS" &&
+ SCRATCH_XFS_LIST_METADATA_FIELDS='icount,ifree,fdblocks'
+export SCRATCH_XFS_LIST_METADATA_FIELDS
+_scratch_xfs_fuzz_metadata '' 'online' 'sb 0' >> $seqres.full
+echo "Done fuzzing fscounters"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 730
+Format and populate
+Fuzz fscounters
+Done fuzzing fscounters
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 731
+#
+# Race fsstress and fscounter repair for a while to see if we crash or livelock.
+# Summary counter repair requires us to freeze the filesystem to stop all
+# filesystem activity, so we can't have userspace wandering in and thawing it.
+#
+. ./common/preamble
+_begin_fstest online_repair dangerous_fsstress_repair
+
+# Override the default cleanup function.
+_cleanup()
+{
+ _scratch_xfs_stress_scrub_cleanup &> /dev/null
+ cd /
+ rm -rf $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_online_repair
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_online_repair -s "repair fscounters"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
--- /dev/null
+QA output created by 731
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 732
+#
+# Race fsstress and fscounter repair on the realtime device for a while to see
+# if we crash or livelock. Summary counter repair requires us to freeze the
+# filesystem to stop all filesystem activity, so we can't have userspace
+# wandering in and thawing it.
+#
+. ./common/preamble
+_begin_fstest auto quick rw scrub realtime
+
+_cleanup() {
+ _scratch_xfs_stress_scrub_cleanup &> /dev/null
+ cd /
+ rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_realtime
+_require_xfs_stress_scrub
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_require_xfs_has_feature "$SCRATCH_MNT" realtime
+
+# Force all files to be allocated on the realtime device
+_xfs_force_bdev realtime $SCRATCH_MNT
+_scratch_xfs_stress_online_repair -s 'scrub fscounters' -s "repair fscounters"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
--- /dev/null
+QA output created by 732
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 733
+#
+# Populate a XFS filesystem and fuzz the data mappings of every directory type.
+# Use xfs_scrub to fix the corruption.
+#
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_scrub dangerous_online_repair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+_scratch_xfs_set_dir_fuzz_types
+
+# Now fuzz the block maps of each directory type.
+for dirtype in "${SCRATCH_XFS_DIR_FUZZ_TYPES[@]}"; do
+ echo "Fuzz block map for ${dirtype}" | tee -a $seqres.full
+
+ # Restore a correct copy of the filesystem before we start a round of
+ # fuzzing. This avoids corruption errors from xfs_db when
+ # _scratch_xfs_fuzz_metadata probes the directory block fields.
+ __scratch_xfs_fuzz_mdrestore
+
+ _scratch_mount
+ inum=$(stat -c '%i' $SCRATCH_MNT/S_IFDIR.FMT_${dirtype})
+ _scratch_unmount
+
+ _scratch_xfs_fuzz_metadata 'u*.bmx' 'online' "inode ${inum}" >> $seqres.full
+ echo "Done fuzzing dir map ${dirtype}"
+done
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 733
+Format and populate
+Fuzz block map for BLOCK
+Done fuzzing dir map BLOCK
+Fuzz block map for LEAF
+Done fuzzing dir map LEAF
+Fuzz block map for LEAFN
+Done fuzzing dir map LEAFN
+Fuzz block map for NODE
+Done fuzzing dir map NODE
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 734
+#
+# Populate a XFS filesystem and fuzz the data mappings of every directory type.
+# Use xfs_repair to fix the corruption.
+#
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_scrub dangerous_repair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+_scratch_xfs_set_dir_fuzz_types
+
+# Now fuzz the block maps of each directory type.
+for dirtype in "${SCRATCH_XFS_DIR_FUZZ_TYPES[@]}"; do
+ echo "Fuzz block map for ${dirtype}" | tee -a $seqres.full
+
+ # Restore a correct copy of the filesystem before we start a round of
+ # fuzzing. This avoids corruption errors from xfs_db when
+ # _scratch_xfs_fuzz_metadata probes the directory block fields.
+ __scratch_xfs_fuzz_mdrestore
+
+ _scratch_mount
+ inum=$(stat -c '%i' $SCRATCH_MNT/S_IFDIR.FMT_${dirtype})
+ _scratch_unmount
+
+ _scratch_xfs_fuzz_metadata 'u*.bmx' 'offline' "inode ${inum}" >> $seqres.full
+ echo "Done fuzzing dir map ${dirtype}"
+done
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 734
+Format and populate
+Fuzz block map for BLOCK
+Done fuzzing dir map BLOCK
+Fuzz block map for LEAF
+Done fuzzing dir map LEAF
+Fuzz block map for LEAFN
+Done fuzzing dir map LEAFN
+Fuzz block map for NODE
+Done fuzzing dir map NODE
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 735
+#
+# Populate a XFS filesystem and fuzz the data mappings of every directory type.
+# Do not fix the filesystem, to test metadata verifiers.
+#
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_scrub dangerous_norepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+_scratch_xfs_set_dir_fuzz_types
+
+# Now fuzz the block maps of each directory type.
+for dirtype in "${SCRATCH_XFS_DIR_FUZZ_TYPES[@]}"; do
+ echo "Fuzz block map for ${dirtype}" | tee -a $seqres.full
+
+ # Restore a correct copy of the filesystem before we start a round of
+ # fuzzing. This avoids corruption errors from xfs_db when
+ # _scratch_xfs_fuzz_metadata probes the directory block fields.
+ __scratch_xfs_fuzz_mdrestore
+
+ _scratch_mount
+ inum=$(stat -c '%i' $SCRATCH_MNT/S_IFDIR.FMT_${dirtype})
+ _scratch_unmount
+
+ _scratch_xfs_fuzz_metadata 'u*.bmx' 'none' "inode ${inum}" >> $seqres.full
+ echo "Done fuzzing dir map ${dirtype}"
+done
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 735
+Format and populate
+Fuzz block map for BLOCK
+Done fuzzing dir map BLOCK
+Fuzz block map for LEAF
+Done fuzzing dir map LEAF
+Fuzz block map for LEAFN
+Done fuzzing dir map LEAFN
+Fuzz block map for NODE
+Done fuzzing dir map NODE
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 736
+#
+# Populate a XFS filesystem and fuzz the attr mappings of every xattr type.
+# Use xfs_scrub to fix the corruption.
+#
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_scrub dangerous_online_repair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+_scratch_xfs_set_xattr_fuzz_types
+
+# Now fuzz the block maps of each xattr type.
+for attrtype in "${SCRATCH_XFS_XATTR_FUZZ_TYPES[@]}"; do
+ echo "Fuzz block map for ${attrtype}" | tee -a $seqres.full
+
+ # Restore a correct copy of the filesystem before we start a round of
+ # fuzzing. This avoids corruption errors from xfs_db when
+ # _scratch_xfs_fuzz_metadata probes the xattr block fields.
+ __scratch_xfs_fuzz_mdrestore
+
+ _scratch_mount
+ inum=$(stat -c '%i' $SCRATCH_MNT/ATTR.FMT_${attrtype})
+ _scratch_unmount
+
+ _scratch_xfs_fuzz_metadata 'a*.bmx' 'online' "inode ${inum}" >> $seqres.full
+ echo "Done fuzzing attr map ${attrtype}"
+done
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 736
+Format and populate
+Fuzz block map for EXTENTS_REMOTE3K
+Done fuzzing attr map EXTENTS_REMOTE3K
+Fuzz block map for EXTENTS_REMOTE4K
+Done fuzzing attr map EXTENTS_REMOTE4K
+Fuzz block map for LEAF
+Done fuzzing attr map LEAF
+Fuzz block map for NODE
+Done fuzzing attr map NODE
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 737
+#
+# Populate a XFS filesystem and fuzz the attr mappings of every xattr type.
+# Use xfs_repair to fix the corruption.
+#
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_scrub dangerous_repair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+_scratch_xfs_set_xattr_fuzz_types
+
+# Now fuzz the block maps of each xattr type.
+for attrtype in "${SCRATCH_XFS_XATTR_FUZZ_TYPES[@]}"; do
+ echo "Fuzz block map for ${attrtype}" | tee -a $seqres.full
+
+ # Restore a correct copy of the filesystem before we start a round of
+ # fuzzing. This avoids corruption errors from xfs_db when
+ # _scratch_xfs_fuzz_metadata probes the xattr block fields.
+ __scratch_xfs_fuzz_mdrestore
+
+ _scratch_mount
+ inum=$(stat -c '%i' $SCRATCH_MNT/ATTR.FMT_${attrtype})
+ _scratch_unmount
+
+ _scratch_xfs_fuzz_metadata 'a*.bmx' 'offline' "inode ${inum}" >> $seqres.full
+ echo "Done fuzzing attr map ${attrtype}"
+done
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 737
+Format and populate
+Fuzz block map for EXTENTS_REMOTE3K
+Done fuzzing attr map EXTENTS_REMOTE3K
+Fuzz block map for EXTENTS_REMOTE4K
+Done fuzzing attr map EXTENTS_REMOTE4K
+Fuzz block map for LEAF
+Done fuzzing attr map LEAF
+Fuzz block map for NODE
+Done fuzzing attr map NODE
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 738
+#
+# Populate a XFS filesystem and fuzz the attr mappings of every xattr type.
+# Do not fix the filesystem, to test metadata verifiers.
+#
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_scrub dangerous_norepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+_scratch_xfs_set_xattr_fuzz_types
+
+# Now fuzz the block maps of each xattr type.
+for attrtype in "${SCRATCH_XFS_XATTR_FUZZ_TYPES[@]}"; do
+ echo "Fuzz block map for ${attrtype}" | tee -a $seqres.full
+
+ # Restore a correct copy of the filesystem before we start a round of
+ # fuzzing. This avoids corruption errors from xfs_db when
+ # _scratch_xfs_fuzz_metadata probes the xattr block fields.
+ __scratch_xfs_fuzz_mdrestore
+
+ _scratch_mount
+ inum=$(stat -c '%i' $SCRATCH_MNT/ATTR.FMT_${attrtype})
+ _scratch_unmount
+
+ _scratch_xfs_fuzz_metadata 'a*.bmx' 'none' "inode ${inum}" >> $seqres.full
+ echo "Done fuzzing attr map ${attrtype}"
+done
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 738
+Format and populate
+Fuzz block map for EXTENTS_REMOTE3K
+Done fuzzing attr map EXTENTS_REMOTE3K
+Fuzz block map for EXTENTS_REMOTE4K
+Done fuzzing attr map EXTENTS_REMOTE4K
+Fuzz block map for LEAF
+Done fuzzing attr map LEAF
+Fuzz block map for NODE
+Done fuzzing attr map NODE
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 739
+#
+# Populate a XFS filesystem and fuzz every realtime bitmap field.
+# Use xfs_scrub to fix the corruption.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_scrub dangerous_online_repair realtime
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_realtime
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+echo "Fuzz rtbitmap"
+is_metadir=$(_scratch_xfs_get_metadata_field "core.version" 'path -m /realtime/0.bitmap')
+if [ -n "$is_metadir" ]; then
+ path=('path -m /realtime/0.bitmap')
+else
+ path=('sb' 'addr rbmino')
+fi
+_scratch_xfs_fuzz_metadata '' 'online' "${path[@]}" 'dblock 0' >> $seqres.full
+echo "Done fuzzing rtbitmap"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 739
+Format and populate
+Fuzz rtbitmap
+Done fuzzing rtbitmap
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 740
+#
+# Populate a XFS filesystem and fuzz every realtime summary field.
+# Use xfs_scrub to fix the corruption.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_scrub dangerous_online_repair realtime
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_realtime
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+echo "Fuzz rtsummary"
+is_metadir=$(_scratch_xfs_get_metadata_field "core.version" 'path -m /realtime/0.summary')
+if [ -n "$is_metadir" ]; then
+ path=('path -m /realtime/0.summary')
+else
+ path=('sb' 'addr rsumino')
+fi
+_scratch_xfs_fuzz_metadata '' 'online' "${path[@]}" 'dblock 0' >> $seqres.full
+echo "Done fuzzing rtsummary"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 740
+Format and populate
+Fuzz rtsummary
+Done fuzzing rtsummary
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 741
+#
+# Populate a XFS filesystem and fuzz every realtime bitmap field.
+# Use xfs_repair to fix the corruption.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_scrub dangerous_repair realtime
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_realtime
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+echo "Fuzz rtbitmap"
+is_metadir=$(_scratch_xfs_get_metadata_field "core.version" 'path -m /realtime/0.bitmap')
+if [ -n "$is_metadir" ]; then
+ path=('path -m /realtime/0.bitmap')
+else
+ path=('sb' 'addr rbmino')
+fi
+_scratch_xfs_fuzz_metadata '' 'offline' "${path[@]}" 'dblock 0' >> $seqres.full
+echo "Done fuzzing rtbitmap"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 741
+Format and populate
+Fuzz rtbitmap
+Done fuzzing rtbitmap
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 742
+#
+# Populate a XFS filesystem and fuzz every realtime summary field.
+# Use xfs_repair to fix the corruption.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_scrub dangerous_repair realtime
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_realtime
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+echo "Fuzz rtsummary"
+is_metadir=$(_scratch_xfs_get_metadata_field "core.version" 'path -m /realtime/0.summary')
+if [ -n "$is_metadir" ]; then
+ path=('path -m /realtime/0.summary')
+else
+ path=('sb' 'addr rsumino')
+fi
+_scratch_xfs_fuzz_metadata '' 'offline' "${path[@]}" 'dblock 0' >> $seqres.full
+echo "Done fuzzing rtsummary"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 742
+Format and populate
+Fuzz rtsummary
+Done fuzzing rtsummary
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 743
+#
+# Populate a XFS filesystem and fuzz every realtime bitmap field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_scrub dangerous_bothrepair realtime
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_realtime
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+echo "Fuzz rtbitmap"
+is_metadir=$(_scratch_xfs_get_metadata_field "core.version" 'path -m /realtime/0.bitmap')
+if [ -n "$is_metadir" ]; then
+ path=('path -m /realtime/0.bitmap')
+else
+ path=('sb' 'addr rbmino')
+fi
+_scratch_xfs_fuzz_metadata '' 'both' "${path[@]}" 'dblock 0' >> $seqres.full
+echo "Done fuzzing rtbitmap"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 743
+Format and populate
+Fuzz rtbitmap
+Done fuzzing rtbitmap
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 744
+#
+# Populate a XFS filesystem and fuzz every realtime summary field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_scrub dangerous_bothrepair realtime
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_realtime
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+echo "Fuzz rtsummary"
+is_metadir=$(_scratch_xfs_get_metadata_field "core.version" 'path -m /realtime/0.summary')
+if [ -n "$is_metadir" ]; then
+ path=('path -m /realtime/0.summary')
+else
+ path=('sb' 'addr rsumino')
+fi
+_scratch_xfs_fuzz_metadata '' 'both' "${path[@]}" 'dblock 0' >> $seqres.full
+echo "Done fuzzing rtsummary"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 744
+Format and populate
+Fuzz rtsummary
+Done fuzzing rtsummary
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 745
+#
+# Populate a XFS filesystem and fuzz every realtime bitmap field.
+# Do not fix the filesystem, to test metadata verifiers.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_scrub dangerous_norepair realtime
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_realtime
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+echo "Fuzz rtbitmap"
+is_metadir=$(_scratch_xfs_get_metadata_field "core.version" 'path -m /realtime/0.bitmap')
+if [ -n "$is_metadir" ]; then
+ path=('path -m /realtime/0.bitmap')
+else
+ path=('sb' 'addr rbmino')
+fi
+_scratch_xfs_fuzz_metadata '' 'none' "${path[@]}" 'dblock 0' >> $seqres.full
+echo "Done fuzzing rtbitmap"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 745
+Format and populate
+Fuzz rtbitmap
+Done fuzzing rtbitmap
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 746
+#
+# Populate a XFS filesystem and fuzz every realtime summary field.
+# Do not fix the filesystem, to test metadata verifiers.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_scrub dangerous_norepair realtime
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_realtime
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+echo "Fuzz rtsummary"
+is_metadir=$(_scratch_xfs_get_metadata_field "core.version" 'path -m /realtime/0.summary')
+if [ -n "$is_metadir" ]; then
+ path=('path -m /realtime/0.summary')
+else
+ path=('sb' 'addr rsumino')
+fi
+_scratch_xfs_fuzz_metadata '' 'none' "${path[@]}" 'dblock 0' >> $seqres.full
+echo "Done fuzzing rtsummary"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 746
+Format and populate
+Fuzz rtsummary
+Done fuzzing rtsummary
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 747
+#
+# Populate a XFS filesystem and fuzz every superblock field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+echo "Fuzz superblock"
+_scratch_xfs_fuzz_metadata '' 'both' 'sb 1' >> $seqres.full
+echo "Done fuzzing superblock"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 747
+Format and populate
+Fuzz superblock
+Done fuzzing superblock
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 748
+#
+# Populate a XFS filesystem and fuzz every AGF field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+echo "Fuzz AGF"
+_scratch_xfs_fuzz_metadata '' 'both' 'agf 0' >> $seqres.full
+echo "Done fuzzing AGF"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 748
+Format and populate
+Fuzz AGF
+Done fuzzing AGF
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 749
+#
+# Populate a XFS filesystem and fuzz every AGFL field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+echo "Fuzz AGFL"
+_scratch_xfs_fuzz_metadata '' 'both' 'agfl 0' >> $seqres.full
+echo "Done fuzzing AGFL"
+
+# Restore a correct copy of the filesystem before we start the second round of
+# fuzzing. This avoids corruption errors from xfs_db when we probe for flfirst
+# in the AGF and later when _scratch_xfs_fuzz_metadata probes the AGFL fields.
+__scratch_xfs_fuzz_mdrestore
+flfirst=$(_scratch_xfs_db -c 'agf 0' -c 'p flfirst' | sed -e 's/flfirst = //g')
+
+echo "Fuzz AGFL flfirst"
+SCRATCH_XFS_LIST_METADATA_FIELDS="bno[${flfirst}]" _scratch_xfs_fuzz_metadata '' 'both' 'agfl 0' >> $seqres.full
+echo "Done fuzzing AGFL flfirst"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 749
+Format and populate
+Fuzz AGFL
+Done fuzzing AGFL
+Fuzz AGFL flfirst
+Done fuzzing AGFL flfirst
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 750
+#
+# Populate a XFS filesystem and fuzz every AGI field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+echo "Fuzz AGI"
+_scratch_xfs_fuzz_metadata '' 'both' 'agi 0' >> $seqres.full
+echo "Done fuzzing AGI"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 750
+Format and populate
+Fuzz AGI
+Done fuzzing AGI
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 751
+#
+# Populate a XFS filesystem and fuzz every bnobt field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+path="$(_scratch_xfs_find_agbtree_height 'bno' 2)" || \
+ _fail "could not find two-level bnobt"
+
+echo "Fuzz bnobt recs"
+_scratch_xfs_fuzz_metadata '' 'both' "$path" 'addr bnoroot' 'addr ptrs[1]' >> $seqres.full
+echo "Done fuzzing bnobt recs"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 751
+Format and populate
+Fuzz bnobt recs
+Done fuzzing bnobt recs
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 752
+#
+# Populate a XFS filesystem and fuzz every bnobt key/pointer.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+path="$(_scratch_xfs_find_agbtree_height 'bno' 2)" || \
+ _fail "could not find two-level bnobt"
+
+echo "Fuzz bnobt keyptr"
+_scratch_xfs_fuzz_metadata '' 'both' "$path" 'addr bnoroot' >> $seqres.full
+echo "Done fuzzing bnobt keyptr"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 752
+Format and populate
+Fuzz bnobt keyptr
+Done fuzzing bnobt keyptr
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 753
+#
+# Populate a XFS filesystem and fuzz every cntbt field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+path="$(_scratch_xfs_find_agbtree_height 'cnt' 2)" || \
+ _fail "could not find two-level cntbt"
+
+echo "Fuzz cntbt"
+_scratch_xfs_fuzz_metadata '' 'both' "$path" 'addr cntroot' 'addr ptrs[1]' >> $seqres.full
+echo "Done fuzzing cntbt"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 753
+Format and populate
+Fuzz cntbt
+Done fuzzing cntbt
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 754
+#
+# Populate a XFS filesystem and fuzz every inobt field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+path="$(_scratch_xfs_find_agbtree_height 'ino' 2)" || \
+ _fail "could not find two-level inobt"
+
+echo "Fuzz inobt"
+_scratch_xfs_fuzz_metadata '' 'both' "$path" 'addr root' 'addr ptrs[1]' >> $seqres.full
+echo "Done fuzzing inobt"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 754
+Format and populate
+Fuzz inobt
+Done fuzzing inobt
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 755
+#
+# Populate a XFS filesystem and fuzz every finobt field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+_require_xfs_finobt
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+path="$(_scratch_xfs_find_agbtree_height 'fino' 2)" || \
+ _fail "could not find two-level finobt"
+
+echo "Fuzz finobt"
+_scratch_xfs_fuzz_metadata '' 'both' "$path" 'addr free_root' 'addr ptrs[1]' >> $seqres.full
+echo "Done fuzzing finobt"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 755
+Format and populate
+Fuzz finobt
+Done fuzzing finobt
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 756
+#
+# Populate a XFS filesystem and fuzz every rmapbt field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_xfs_scratch_rmapbt
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+path="$(_scratch_xfs_find_agbtree_height 'rmap' 2)" || \
+ _fail "could not find two-level rmapbt"
+
+echo "Fuzz rmapbt recs"
+_scratch_xfs_fuzz_metadata '' 'both' "$path" 'addr rmaproot' 'addr ptrs[1]' >> $seqres.full
+echo "Done fuzzing rmapbt recs"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 756
+Format and populate
+Fuzz rmapbt recs
+Done fuzzing rmapbt recs
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 757
+#
+# Populate a XFS filesystem and fuzz every rmapbt key/pointer field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_xfs_scratch_rmapbt
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+path="$(_scratch_xfs_find_agbtree_height 'rmap' 2)" || \
+ _fail "could not find two-level rmapbt"
+
+echo "Fuzz rmapbt keyptr"
+_scratch_xfs_fuzz_metadata '' 'both' "$path" 'addr rmaproot' >> $seqres.full
+echo "Done fuzzing rmapbt keyptr"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 757
+Format and populate
+Fuzz rmapbt keyptr
+Done fuzzing rmapbt keyptr
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 758
+#
+# Populate a XFS filesystem and fuzz every refcountbt key/pointer field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+. ./common/reflink
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_reflink
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+path="$(_scratch_xfs_find_agbtree_height 'refcnt' 2)" || \
+ _fail "could not find two-level refcountbt"
+
+echo "Fuzz refcountbt"
+_scratch_xfs_fuzz_metadata '' 'both' "$path" 'addr refcntroot' >> $seqres.full
+echo "Done fuzzing refcountbt"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 758
+Format and populate
+Fuzz refcountbt
+Done fuzzing refcountbt
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 759
+#
+# Populate a XFS filesystem and fuzz every btree-format directory inode field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+echo "Find btree-format dir inode"
+_scratch_mount
+inum=$(stat -c '%i' $SCRATCH_MNT/S_IFDIR.FMT_BTREE)
+_scratch_unmount
+
+echo "Fuzz inode"
+_scratch_xfs_fuzz_metadata '' 'both' "inode ${inum}" >> $seqres.full
+echo "Done fuzzing inode"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 759
+Format and populate
+Find btree-format dir inode
+Fuzz inode
+Done fuzzing inode
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 760
+#
+# Populate a XFS filesystem and fuzz every extents-format file inode field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+echo "Find extents-format file inode"
+_scratch_mount
+inum=$(stat -c '%i' $SCRATCH_MNT/S_IFREG.FMT_EXTENTS)
+_scratch_unmount
+
+echo "Fuzz inode"
+_scratch_xfs_fuzz_metadata '' 'both' "inode ${inum}" >> $seqres.full
+echo "Done fuzzing inode"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 760
+Format and populate
+Find extents-format file inode
+Fuzz inode
+Done fuzzing inode
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 761
+#
+# Populate a XFS filesystem and fuzz every btree-format file inode field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+echo "Find btree-format file inode"
+_scratch_mount
+inum=$(stat -c '%i' $SCRATCH_MNT/S_IFREG.FMT_BTREE)
+_scratch_unmount
+
+echo "Fuzz inode"
+_scratch_xfs_fuzz_metadata '' 'both' "inode ${inum}" >> $seqres.full
+echo "Done fuzzing inode"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 761
+Format and populate
+Find btree-format file inode
+Fuzz inode
+Done fuzzing inode
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 762
+#
+# Populate a XFS filesystem and fuzz every bmbt block field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+echo "Find bmbt block"
+_scratch_mount
+inum=$(stat -c '%i' $SCRATCH_MNT/S_IFREG.FMT_BTREE)
+_scratch_unmount
+
+inode_ver=$(_scratch_xfs_get_metadata_field "core.version" "inode ${inum}")
+
+echo "Fuzz bmbt"
+_scratch_xfs_fuzz_metadata '' 'both' "inode ${inum}" "addr u${inode_ver}.bmbt.ptrs[1]" >> $seqres.full
+echo "Done fuzzing bmbt"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 762
+Format and populate
+Find bmbt block
+Fuzz bmbt
+Done fuzzing bmbt
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 763
+#
+# Populate a XFS filesystem and fuzz every symlink remote block field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+echo "Find symlink remote block"
+_scratch_mount
+inum=$(stat -c '%i' $SCRATCH_MNT/S_IFLNK.FMT_EXTENTS)
+_scratch_unmount
+
+echo "Fuzz symlink remote block"
+_scratch_xfs_fuzz_metadata '' 'both' "inode ${inum}" 'dblock 0' >> $seqres.full
+echo "Done fuzzing symlink remote block"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 763
+Format and populate
+Find symlink remote block
+Fuzz symlink remote block
+Done fuzzing symlink remote block
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 764
+#
+# Populate a XFS filesystem and fuzz every inline directory inode field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+echo "Find inline-format dir inode"
+_scratch_mount
+inum=$(stat -c '%i' $SCRATCH_MNT/S_IFDIR.FMT_INLINE)
+_scratch_unmount
+
+echo "Fuzz inline-format dir inode"
+_scratch_xfs_fuzz_metadata '' 'both' "inode ${inum}" >> $seqres.full
+echo "Done fuzzing inline-format dir inode"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 764
+Format and populate
+Find inline-format dir inode
+Fuzz inline-format dir inode
+Done fuzzing inline-format dir inode
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 765
+#
+# Populate a XFS filesystem and fuzz every block-format dir block field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+echo "Find data-format dir block"
+_scratch_mount
+inum=$(stat -c '%i' $SCRATCH_MNT/S_IFDIR.FMT_BLOCK)
+_scratch_unmount
+
+echo "Fuzz data-format dir block"
+_scratch_xfs_fuzz_metadata '' 'both' "inode ${inum}" 'dblock 0' >> $seqres.full
+echo "Done fuzzing data-format dir block"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 765
+Format and populate
+Find data-format dir block
+Fuzz data-format dir block
+Done fuzzing data-format dir block
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 766
+#
+# Populate a XFS filesystem and fuzz every data-format dir block field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+echo "Find data-format dir block"
+_scratch_mount
+inum=$(stat -c '%i' $SCRATCH_MNT/S_IFDIR.FMT_LEAF)
+blk_sz=$(_get_block_size $SCRATCH_MNT)
+_scratch_unmount
+
+echo "Fuzz data-format dir block"
+_scratch_xfs_fuzz_metadata '' 'both' "inode ${inum}" "dblock 0" >> $seqres.full
+echo "Done fuzzing data-format dir block"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 766
+Format and populate
+Find data-format dir block
+Fuzz data-format dir block
+Done fuzzing data-format dir block
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 767
+#
+# Populate a XFS filesystem and fuzz every leaf1-format dir block field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+echo "Find leaf1-format dir block"
+_scratch_mount
+inum=$(stat -c '%i' $SCRATCH_MNT/S_IFDIR.FMT_LEAF)
+blk_sz=$(_get_block_size $SCRATCH_MNT)
+_scratch_unmount
+
+leaf_offset=$(( (2 ** 35) / blk_sz))
+echo "Fuzz leaf1-format dir block"
+_scratch_xfs_fuzz_metadata '' 'both' "inode ${inum}" "dblock ${leaf_offset}" >> $seqres.full
+echo "Done fuzzing leaf1-format dir block"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 767
+Format and populate
+Find leaf1-format dir block
+Fuzz leaf1-format dir block
+Done fuzzing leaf1-format dir block
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 768
+#
+# Populate a XFS filesystem and fuzz every leafn-format dir block field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+echo "Find leafn-format dir block"
+_scratch_mount
+inum=$(stat -c '%i' $SCRATCH_MNT/S_IFDIR.FMT_NODE)
+blk_sz=$(_get_block_size $SCRATCH_MNT)
+_scratch_unmount
+
+leaf_offset=$(( ( (2 ** 35) / blk_sz) + 1))
+echo "Fuzz leafn-format dir block"
+_scratch_xfs_fuzz_metadata '' 'both' "inode ${inum}" "dblock ${leaf_offset}" >> $seqres.full
+echo "Done fuzzing leafn-format dir block"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 768
+Format and populate
+Find leafn-format dir block
+Fuzz leafn-format dir block
+Done fuzzing leafn-format dir block
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 769
+#
+# Populate a XFS filesystem and fuzz every node-format dir block field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+echo "Find node-format dir block"
+_scratch_mount
+inum=$(stat -c '%i' $SCRATCH_MNT/S_IFDIR.FMT_NODE)
+blk_sz=$(_get_block_size $SCRATCH_MNT)
+_scratch_unmount
+
+leaf_offset=$(( (2 ** 35) / blk_sz ))
+echo "Fuzz node-format dir block"
+_scratch_xfs_fuzz_metadata '' 'both' "inode ${inum}" "dblock ${leaf_offset}" >> $seqres.full
+echo "Done fuzzing node-format dir block"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 769
+Format and populate
+Find node-format dir block
+Fuzz node-format dir block
+Done fuzzing node-format dir block
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 770
+#
+# Populate a XFS filesystem and fuzz every freeindex-format dir block field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+echo "Find freeindex-format dir block"
+_scratch_mount
+inum=$(stat -c '%i' $SCRATCH_MNT/S_IFDIR.FMT_NODE)
+blk_sz=$(_get_block_size $SCRATCH_MNT)
+_scratch_unmount
+
+leaf_offset=$(( (2 ** 36) / blk_sz ))
+echo "Fuzz freeindex-format dir block"
+_scratch_xfs_fuzz_metadata '' 'both' "inode ${inum}" "dblock ${leaf_offset}" >> $seqres.full
+echo "Done fuzzing freeindex-format dir block"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 770
+Format and populate
+Find freeindex-format dir block
+Fuzz freeindex-format dir block
+Done fuzzing freeindex-format dir block
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 771
+#
+# Populate a XFS filesystem and fuzz every inline attr inode field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+echo "Find inline-format attr inode"
+_scratch_mount
+inum=$(stat -c '%i' $SCRATCH_MNT/ATTR.FMT_LOCAL)
+_scratch_unmount
+
+echo "Fuzz inline-format attr inode"
+_scratch_xfs_fuzz_metadata '' 'both' "inode ${inum}" >> $seqres.full
+echo "Done fuzzing inline-format attr inode"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 771
+Format and populate
+Find inline-format attr inode
+Fuzz inline-format attr inode
+Done fuzzing inline-format attr inode
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 772
+#
+# Populate a XFS filesystem and fuzz every leaf-format attr block field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+echo "Find leaf-format attr block"
+_scratch_mount
+inum=$(stat -c '%i' $SCRATCH_MNT/ATTR.FMT_LEAF)
+_scratch_unmount
+
+echo "Fuzz leaf-format attr block"
+_scratch_xfs_fuzz_metadata '' 'both' "inode ${inum}" 'ablock 0' >> $seqres.full
+echo "Done fuzzing leaf-format attr block"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 772
+Format and populate
+Find leaf-format attr block
+Fuzz leaf-format attr block
+Done fuzzing leaf-format attr block
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 773
+#
+# Populate a XFS filesystem and fuzz every node-format attr block field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+echo "Find node-format attr block"
+_scratch_mount
+inum=$(stat -c '%i' $SCRATCH_MNT/ATTR.FMT_NODE)
+_scratch_unmount
+
+echo "Fuzz node-format attr block"
+_scratch_xfs_fuzz_metadata '' 'both' "inode ${inum}" "ablock 0" >> $seqres.full
+echo "Done fuzzing node-format attr block"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 773
+Format and populate
+Find node-format attr block
+Fuzz node-format attr block
+Done fuzzing node-format attr block
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 774
+#
+# Populate a XFS filesystem and fuzz every external attr block field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+echo "Find external attr block"
+_scratch_mount
+inum=$(stat -c '%i' $SCRATCH_MNT/ATTR.FMT_EXTENTS_REMOTE3K)
+_scratch_unmount
+
+echo "Fuzz external attr block"
+_scratch_xfs_fuzz_metadata '' 'both' "inode ${inum}" "ablock 1" >> $seqres.full
+echo "Done fuzzing external attr block"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 774
+Format and populate
+Find external attr block
+Fuzz external attr block
+Done fuzzing external attr block
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 775
+#
+# Populate a XFS filesystem and fuzz every refcountbt field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+. ./common/reflink
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_reflink
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+path="$(_scratch_xfs_find_agbtree_height 'refcnt' 2)" || \
+ _fail "could not find two-level refcountbt"
+
+echo "Fuzz refcountbt"
+_scratch_xfs_fuzz_metadata '' 'both' "$path" 'addr refcntroot' 'addr ptrs[1]' >> $seqres.full
+echo "Done fuzzing refcountbt"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 775
+Format and populate
+Fuzz refcountbt
+Done fuzzing refcountbt
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 776
+#
+# Populate a XFS filesystem and fuzz every btree-format attr inode field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+echo "Find btree-format attr inode"
+_scratch_mount
+inum=$(stat -c '%i' $SCRATCH_MNT/ATTR.FMT_BTREE)
+_scratch_unmount
+
+echo "Fuzz inode"
+_scratch_xfs_fuzz_metadata '' 'both' "inode ${inum}" >> $seqres.full
+echo "Done fuzzing inode"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 776
+Format and populate
+Find btree-format attr inode
+Fuzz inode
+Done fuzzing inode
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 777
+#
+# Populate a XFS filesystem and fuzz every blockdev inode field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+echo "Find blockdev inode"
+_scratch_mount
+inum=$(stat -c '%i' $SCRATCH_MNT/S_IFBLK)
+_scratch_unmount
+
+echo "Fuzz inode"
+_scratch_xfs_fuzz_metadata '' 'both' "inode ${inum}" >> $seqres.full
+echo "Done fuzzing inode"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 777
+Format and populate
+Find blockdev inode
+Fuzz inode
+Done fuzzing inode
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 778
+#
+# Populate a XFS filesystem and fuzz every local-format symlink inode field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+echo "Find local-format symlink inode"
+_scratch_mount
+inum=$(stat -c '%i' $SCRATCH_MNT/S_IFLNK.FMT_LOCAL)
+_scratch_unmount
+
+echo "Fuzz inode"
+_scratch_xfs_fuzz_metadata '' 'both' "inode ${inum}" >> $seqres.full
+echo "Done fuzzing inode"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 778
+Format and populate
+Find local-format symlink inode
+Fuzz inode
+Done fuzzing inode
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 779
+#
+# Populate a XFS filesystem and fuzz every user dquot field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+. ./common/quota
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+_require_quota
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+echo "${MOUNT_OPTIONS}" | grep -q 'usrquota' || _notrun "user quota disabled"
+
+echo "Fuzz user 0 dquot"
+_scratch_xfs_fuzz_metadata '' 'both' "dquot -u 0" >> $seqres.full
+echo "Done fuzzing dquot"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 779
+Format and populate
+Fuzz user 0 dquot
+Done fuzzing dquot
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 780
+#
+# Populate a XFS filesystem and fuzz every group dquot field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+. ./common/quota
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+_require_quota
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+echo "${MOUNT_OPTIONS}" | grep -q 'grpquota' || _notrun "group quota disabled"
+
+echo "Fuzz group 0 dquot"
+_scratch_xfs_fuzz_metadata '' 'both' "dquot -g 0" >> $seqres.full
+echo "Done fuzzing dquot"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 780
+Format and populate
+Fuzz group 0 dquot
+Done fuzzing dquot
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 781
+#
+# Populate a XFS filesystem and fuzz every project dquot field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+. ./common/quota
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+_require_quota
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+echo "${MOUNT_OPTIONS}" | grep -q 'prjquota' || _notrun "project quota disabled"
+
+echo "Fuzz project 0 dquot"
+_scratch_xfs_fuzz_metadata '' 'both' "dquot -p 0" >> $seqres.full
+echo "Done fuzzing dquot"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 781
+Format and populate
+Fuzz project 0 dquot
+Done fuzzing dquot
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 782
+#
+# Populate a XFS filesystem and fuzz every single-leafn-format dir block field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+#
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+echo "Find single-leafn-format dir block"
+_scratch_mount
+inum=$(stat -c '%i' $SCRATCH_MNT/S_IFDIR.FMT_LEAFN)
+blk_sz=$(_get_block_size $SCRATCH_MNT)
+_scratch_unmount
+
+leaf_offset=$(( (2 ** 35) / blk_sz ))
+echo "Fuzz single-leafn-format dir block"
+_scratch_xfs_fuzz_metadata '' 'both' "inode ${inum}" "dblock ${leaf_offset}" >> $seqres.full
+echo "Done fuzzing single-leafn-format dir block"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 782
+Format and populate
+Find single-leafn-format dir block
+Fuzz single-leafn-format dir block
+Done fuzzing single-leafn-format dir block
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 783
+#
+# Populate a XFS filesystem and fuzz the data mappings of every directory type.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+#
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_scrub dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+_scratch_xfs_set_dir_fuzz_types
+
+# Now fuzz the block maps of each directory type.
+for dirtype in "${SCRATCH_XFS_DIR_FUZZ_TYPES[@]}"; do
+ echo "Fuzz block map for ${dirtype}" | tee -a $seqres.full
+
+ # Restore a correct copy of the filesystem before we start a round of
+ # fuzzing. This avoids corruption errors from xfs_db when
+ # _scratch_xfs_fuzz_metadata probes the directory block fields.
+ __scratch_xfs_fuzz_mdrestore
+
+ _scratch_mount
+ inum=$(stat -c '%i' $SCRATCH_MNT/S_IFDIR.FMT_${dirtype})
+ _scratch_unmount
+
+ _scratch_xfs_fuzz_metadata 'u*.bmx' 'both' "inode ${inum}" >> $seqres.full
+ echo "Done fuzzing dir map ${dirtype}"
+done
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 783
+Format and populate
+Fuzz block map for BLOCK
+Done fuzzing dir map BLOCK
+Fuzz block map for LEAF
+Done fuzzing dir map LEAF
+Fuzz block map for LEAFN
+Done fuzzing dir map LEAFN
+Fuzz block map for NODE
+Done fuzzing dir map NODE
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 784
+#
+# Populate a XFS filesystem and fuzz the attr mappings of every xattr type.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+#
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_scrub dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+_scratch_xfs_set_xattr_fuzz_types
+
+# Now fuzz the block maps of each xattr type.
+for attrtype in "${SCRATCH_XFS_XATTR_FUZZ_TYPES[@]}"; do
+ echo "Fuzz block map for ${attrtype}" | tee -a $seqres.full
+
+ # Restore a correct copy of the filesystem before we start a round of
+ # fuzzing. This avoids corruption errors from xfs_db when
+ # _scratch_xfs_fuzz_metadata probes the xattr block fields.
+ __scratch_xfs_fuzz_mdrestore
+
+ _scratch_mount
+ inum=$(stat -c '%i' $SCRATCH_MNT/ATTR.FMT_${attrtype})
+ _scratch_unmount
+
+ _scratch_xfs_fuzz_metadata 'a*.bmx' 'both' "inode ${inum}" >> $seqres.full
+ echo "Done fuzzing attr map ${attrtype}"
+done
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 784
+Format and populate
+Fuzz block map for EXTENTS_REMOTE3K
+Done fuzzing attr map EXTENTS_REMOTE3K
+Fuzz block map for EXTENTS_REMOTE4K
+Done fuzzing attr map EXTENTS_REMOTE4K
+Fuzz block map for LEAF
+Done fuzzing attr map LEAF
+Fuzz block map for NODE
+Done fuzzing attr map NODE
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle, Inc. All Rights Reserved.
+#
+# FS QA Test No. 785
+#
+# Populate a XFS filesystem and fuzz every inobt key/pointer field.
+# Use xfs_repair to fix the corruption.
+#
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_scrub dangerous_repair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+path="$(_scratch_xfs_find_agbtree_height 'ino' 2)" || \
+ _fail "could not find two-level inobt"
+
+echo "Fuzz inobt"
+_scratch_xfs_fuzz_metadata '' 'offline' "$path" 'addr root' >> $seqres.full
+echo "Done fuzzing inobt"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 785
+Format and populate
+Fuzz inobt
+Done fuzzing inobt
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle, Inc. All Rights Reserved.
+#
+# FS QA Test No. 786
+#
+# Populate a XFS filesystem and fuzz every inobt key/pointer field.
+# Use xfs_scrub to fix the corruption.
+#
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_scrub dangerous_online_repair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+path="$(_scratch_xfs_find_agbtree_height 'ino' 2)" || \
+ _fail "could not find two-level inobt"
+
+echo "Fuzz inobt"
+_scratch_xfs_fuzz_metadata '' 'online' "$path" 'addr root' >> $seqres.full
+echo "Done fuzzing inobt"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 786
+Format and populate
+Fuzz inobt
+Done fuzzing inobt
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 787
+#
+# Populate a XFS filesystem and fuzz every inobt key/pointer field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+path="$(_scratch_xfs_find_agbtree_height 'ino' 2)" || \
+ _fail "could not find two-level inobt"
+
+echo "Fuzz inobt"
+_scratch_xfs_fuzz_metadata '' 'both' "$path" 'addr root' >> $seqres.full
+echo "Done fuzzing inobt"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 787
+Format and populate
+Fuzz inobt
+Done fuzzing inobt
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle, Inc. All rights reserved.
+#
+# FS QA Test No. 788
+#
+# Populate a XFS filesystem and fuzz every inobt key/pointer field.
+# Do not fix the filesystem, to test metadata verifiers.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_norepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+path="$(_scratch_xfs_find_agbtree_height 'ino' 2)" || \
+ _fail "could not find two-level inobt"
+
+echo "Fuzz inobt"
+_scratch_xfs_fuzz_metadata '' 'none' "$path" 'addr root' >> $seqres.full
+echo "Done fuzzing inobt"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 788
+Format and populate
+Fuzz inobt
+Done fuzzing inobt
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 789
+#
+# Simple tests of the old xfs swapext ioctl
+
+. ./common/preamble
+_begin_fstest auto quick fiexchange swapext
+
+# Override the default cleanup function.
+_cleanup()
+{
+ cd /
+ rm -r -f $tmp.* $dir
+}
+
+# Import common functions.
+. ./common/filter
+
+# real QA test starts here
+_supported_fs xfs
+_require_xfs_io_command swapext '-v exchrange'
+_require_test
+
+# We can't do any reasonable swapping if the files we're going to create are
+# realtime files, the rt extent size is greater than 1 block, and we can't use
+# atomic extent swapping to make sure that partially written extents are fully
+# swapped.
+file_blksz=$(_get_file_block_size $TEST_DIR)
+fs_blksz=$(_get_block_size $TEST_DIR)
+if (( $file_blksz != $fs_blksz )); then
+ _xfs_has_feature $TEST_DIR reflink || \
+ _notrun "test requires atomic extent swapping for rextsize=$((file_blksz / fs_blksz))"
+fi
+
+dir=$TEST_DIR/test-$seq
+mkdir -p $dir
+
+$XFS_IO_PROG -f -c 'pwrite -S 0x58 0 256k -b 1m' $dir/a >> $seqres.full
+$XFS_IO_PROG -f -c 'pwrite -S 0x59 0 256k -b 1m' $dir/b >> $seqres.full
+$XFS_IO_PROG -f -c 'pwrite -S 0x60 0 256k -b 1m' $dir/c >> $seqres.full
+$XFS_IO_PROG -f -c 'pwrite -S 0x61 0 128k -b 1m' $dir/d >> $seqres.full
+md5sum $dir/a $dir/b $dir/c $dir/d | _filter_test_dir
+
+# Swap two files that are the same length
+echo swap
+$XFS_IO_PROG -c "swapext $dir/b" $dir/a
+md5sum $dir/a $dir/b | _filter_test_dir
+
+# Try to swap two files that are not the same length
+echo fail swap
+$XFS_IO_PROG -c "swapext $dir/c" $dir/d
+md5sum $dir/c $dir/d | _filter_test_dir
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 789
+af5c6e2d6c297f3139a4e99df396c072 TEST_DIR/test-789/a
+fba5c83875b2ab054e06a0284346ebdf TEST_DIR/test-789/b
+40c08c6f2aca19bb0d2cf1dbd8bc1b1c TEST_DIR/test-789/c
+81615449a98aaaad8dc179b3bec87f38 TEST_DIR/test-789/d
+swap
+fba5c83875b2ab054e06a0284346ebdf TEST_DIR/test-789/a
+af5c6e2d6c297f3139a4e99df396c072 TEST_DIR/test-789/b
+fail swap
+swapext: Bad address
+40c08c6f2aca19bb0d2cf1dbd8bc1b1c TEST_DIR/test-789/c
+81615449a98aaaad8dc179b3bec87f38 TEST_DIR/test-789/d
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 790
+#
+# Make sure an atomic swapext actually runs to completion even if we shut
+# down the filesystem midway through.
+
+. ./common/preamble
+_begin_fstest auto quick fiexchange swapext
+
+# Override the default cleanup function.
+_cleanup()
+{
+ cd /
+ rm -r -f $tmp.* $dir
+}
+
+# Import common functions.
+. ./common/filter
+. ./common/reflink
+. ./common/inject
+
+# real QA test starts here
+_supported_fs xfs
+_require_xfs_io_command swapext '-v exchrange -a'
+_require_test_program "punch-alternating"
+_require_xfs_io_command startupdate
+_require_xfs_io_error_injection "bmap_finish_one"
+_require_test
+
+filesnap() {
+ echo "$1"
+ md5sum $dir/a $dir/b $dir/c | _filter_test_dir
+}
+
+dir=$TEST_DIR/test-$seq
+mkdir -p $dir
+blksz=65536
+nrblks=137
+
+# Create two files to swap, and try to fragment the first file.
+rm -f $dir/a
+_pwrite_byte 0x58 0 $((blksz * nrblks)) $dir/a >> $seqres.full
+$here/src/punch-alternating $dir/a
+_pwrite_byte 0x59 0 $((blksz * nrblks)) $dir/b >> $seqres.full
+_pwrite_byte 0x59 0 $((blksz * nrblks)) $dir/c >> $seqres.full
+_pwrite_byte 0x58 0 $((blksz * nrblks)) $dir/a >> $seqres.full
+sync
+
+# Inject a bmap error and trigger it via swapext.
+filesnap "before commit"
+$XFS_IO_PROG -x -c 'inject bmap_finish_one' -c "swapext $dir/b" $dir/a
+
+# Check the file afterwards.
+_test_cycle_mount
+filesnap "after commit"
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 790
+before commit
+c7221b1494117327570a0958b0abca51 TEST_DIR/test-790/a
+30cc2b6b307081e10972317013efb0f3 TEST_DIR/test-790/b
+30cc2b6b307081e10972317013efb0f3 TEST_DIR/test-790/c
+swapext: Input/output error
+after commit
+30cc2b6b307081e10972317013efb0f3 TEST_DIR/test-790/a
+c7221b1494117327570a0958b0abca51 TEST_DIR/test-790/b
+30cc2b6b307081e10972317013efb0f3 TEST_DIR/test-790/c
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 791
+#
+# Test scatter-gather atomic file writes. We create a temporary file, write
+# sparsely to it, then use XFS_EXCH_RANGE_FILE1_WRITTEN flag to swap
+# atomicallly only the ranges that we wrote. Inject an error so that we can
+# test that log recovery finishes the swap.
+
+. ./common/preamble
+_begin_fstest auto quick fiexchange swapext
+
+# Override the default cleanup function.
+_cleanup()
+{
+ cd /
+ rm -r -f $tmp.* $dir
+}
+
+# Import common functions.
+. ./common/filter
+. ./common/inject
+
+# real QA test starts here
+_supported_fs xfs
+_require_xfs_io_command swapext '-v exchrange -a'
+_require_xfs_scratch_atomicswap
+_require_xfs_io_error_injection "bmap_finish_one"
+
+_scratch_mkfs >> $seqres.full
+_scratch_mount
+_require_congruent_file_oplen $SCRATCH_MNT 65536
+
+# Create original file
+_pwrite_byte 0x58 0 1m $SCRATCH_MNT/a >> $seqres.full
+
+# Create the donor file
+$XFS_IO_PROG -f -c 'truncate 1m' $SCRATCH_MNT/b
+_pwrite_byte 0x59 64k 64k $SCRATCH_MNT/b >> $seqres.full
+_pwrite_byte 0x57 768k 64k $SCRATCH_MNT/b >> $seqres.full
+sync
+
+md5sum $SCRATCH_MNT/a | _filter_scratch
+md5sum $SCRATCH_MNT/b | _filter_scratch
+
+# Test swapext. -h means skip holes in /b, and -e means operate to EOF
+echo swap | tee -a $seqres.full
+$XFS_IO_PROG -x -c 'inject bmap_finish_one' \
+ -c "swapext -v exchrange -f -u -h -e -a $SCRATCH_MNT/b" $SCRATCH_MNT/a
+_scratch_cycle_mount
+
+md5sum $SCRATCH_MNT/a | _filter_scratch
+md5sum $SCRATCH_MNT/b | _filter_scratch
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 791
+310f146ce52077fcd3308dcbe7632bb2 SCRATCH_MNT/a
+c9fb827e2e3e579dc2a733ddad486d1d SCRATCH_MNT/b
+swap
+swapext: Input/output error
+e9cbfe8489a68efaa5fcf40cf3106118 SCRATCH_MNT/a
+faf8ed02a5b0638096a817abcc6c2127 SCRATCH_MNT/b
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 792
+#
+# Test scatter-gather atomic file commits. Use the startupdate command to
+# create a temporary file, write sparsely to it, then commitupdate -h to
+# perform the scattered update. Inject an error so that we can test that log
+# recovery finishes the swap.
+
+. ./common/preamble
+_begin_fstest auto quick fiexchange swapext
+
+# Override the default cleanup function.
+_cleanup()
+{
+ cd /
+ rm -r -f $tmp.* $dir
+}
+
+# Import common functions.
+. ./common/filter
+. ./common/inject
+
+# real QA test starts here
+_supported_fs xfs
+_require_xfs_io_command swapext '-v exchrange -a'
+_require_xfs_io_command startupdate '-e'
+_require_xfs_scratch_atomicswap
+_require_xfs_io_error_injection "bmap_finish_one"
+
+_scratch_mkfs >> $seqres.full
+_scratch_mount
+
+# Create original file
+_pwrite_byte 0x58 0 1m $SCRATCH_MNT/a >> $seqres.full
+sync
+md5sum $SCRATCH_MNT/a | _filter_scratch
+
+# Test atomic scatter-gather file commits.
+echo commit | tee -a $seqres.full
+$XFS_IO_PROG -x \
+ -c 'bmap -elpv' \
+ -c 'startupdate -e' \
+ -c 'truncate 1m' \
+ -c 'pwrite -S 0x59 64k 64k' \
+ -c 'pwrite -S 0x57 768k 64k' \
+ -c 'sync' \
+ -c 'bmap -elpv' \
+ -c 'inject bmap_finish_one' \
+ -c 'commitupdate -h -k' \
+ $SCRATCH_MNT/a >> $seqres.full
+_scratch_cycle_mount
+
+$XFS_IO_PROG -c 'bmap -elpv' $SCRATCH_MNT/a >> $seqres.full
+md5sum $SCRATCH_MNT/a | _filter_scratch
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 792
+310f146ce52077fcd3308dcbe7632bb2 SCRATCH_MNT/a
+commit
+committing update: Input/output error
+e9cbfe8489a68efaa5fcf40cf3106118 SCRATCH_MNT/a
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc. All Rights Reserved.
+#
+# FS QA Test No. 793
+#
+# Race fsstress and realtime summary repair for a while to see if we crash or
+# livelock.
+#
+. ./common/preamble
+_begin_fstest online_repair dangerous_fsstress_repair
+
+_cleanup() {
+ _scratch_xfs_stress_scrub_cleanup &> /dev/null
+ cd /
+ rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_realtime
+_require_scratch
+_require_xfs_stress_online_repair
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_require_xfs_has_feature "$SCRATCH_MNT" realtime
+_xfs_force_bdev realtime $SCRATCH_MNT
+
+# XXX the realtime summary scrubber isn't currently implemented upstream.
+# Don't bother trying to fix it on those kernels
+$XFS_IO_PROG -c 'scrub rtsummary' -c 'scrub rtsummary' "$SCRATCH_MNT" 2>&1 | \
+ grep -q 'Scan was not complete' && \
+ _notrun "rtsummary scrub is incomplete"
+
+_scratch_xfs_stress_online_repair -s "repair rtsummary"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
--- /dev/null
+QA output created by 793
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc. All Rights Reserved.
+#
+# FS QA Test No. 794
+#
+# Race fsstress and extended attributes repair for a while to see if we crash
+# or livelock.
+#
+. ./common/preamble
+_begin_fstest online_repair dangerous_fsstress_repair
+
+_cleanup() {
+ _scratch_xfs_stress_scrub_cleanup &> /dev/null
+ cd /
+ rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+. ./common/attr
+
+# real QA test starts here
+_supported_fs xfs
+_require_attrs
+_require_scratch
+_require_xfs_stress_online_repair
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_online_repair -x 'xattr' -s "repair xattr" -t "%attrfile%"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
--- /dev/null
+QA output created by 794
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 795
+#
+# Ensure that the sysadmin won't hit EDQUOT while repairing file data contents
+# even if the file's quota limits have been exceeded. This tests the quota
+# reservation handling inside the swapext code used by repair.
+#
+. ./common/preamble
+_begin_fstest online_repair
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/quota
+
+# real QA test starts here
+
+# Modify as appropriate.
+_supported_fs xfs
+_require_quota
+_require_user
+_require_scratch
+_require_xfs_stress_online_repair
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_qmount_option usrquota
+_qmount
+
+blocksize=$(_get_block_size $SCRATCH_MNT)
+alloc_unit=$(_xfs_get_dir_blocksize $SCRATCH_MNT)
+
+# Make sure we can actually repair a directory
+scratchdir=$SCRATCH_MNT/dir
+scratchfile=$SCRATCH_MNT/file
+mkdir $scratchdir
+touch $scratchfile
+$XFS_IO_PROG -x -c 'inject force_repair' $SCRATCH_MNT
+__stress_scrub_check_commands "$scratchdir" "" 'repair directory'
+
+# Create a 2-dirblock directory
+total_size=$((alloc_unit * 2))
+dirents=$((total_size / 255))
+
+for ((i = 0; i < dirents; i++)); do
+ name=$(printf "x%0254d" $i)
+ ln $scratchfile $scratchdir/$name
+done
+
+# Set a low quota hardlimit for an unprivileged uid and chown the files to it
+echo "set up quota" >> $seqres.full
+$XFS_QUOTA_PROG -x -c "limit -u bhard=$alloc_unit $qa_user" $SCRATCH_MNT
+chown $qa_user $scratchdir $scratchfile
+$XFS_QUOTA_PROG -x -c 'report -u' $SCRATCH_MNT >> $seqres.full
+
+# Rebuild the directory
+echo "repairs" >> $seqres.full
+$XFS_IO_PROG -x -c 'inject force_repair' -c 'repair directory' $scratchdir
+$XFS_QUOTA_PROG -x -c 'report -u' $SCRATCH_MNT >> $seqres.full
+
+# Fail at appending the directory as qa_user to ensure quota enforcement works
+echo "fail quota" >> $seqres.full
+for ((i = 0; i < dirents; i++)); do
+ name=$(printf "y%0254d" $i)
+ su - "$qa_user" -c "ln $scratchfile $scratchdir/$name" 2>&1 | \
+ _filter_scratch | sed -e 's/y[0-9]*/yXXX/g'
+ test "${PIPESTATUS[0]}" -ne 0 && break
+done
+$XFS_QUOTA_PROG -x -c 'report -u' $SCRATCH_MNT >> $seqres.full
+
+# success, all done
+echo Silence is golden
+status=0
+exit
--- /dev/null
+QA output created by 795
+ln: failed to create hard link 'SCRATCH_MNT/dir/yXXX': Disk quota exceeded
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc. All Rights Reserved.
+#
+# FS QA Test No. 796
+#
+# Race fsstress and directory repair for a while to see if we crash or livelock.
+#
+. ./common/preamble
+_begin_fstest online_repair dangerous_fsstress_repair
+
+_cleanup() {
+ _scratch_xfs_stress_scrub_cleanup &> /dev/null
+ cd /
+ rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_online_repair
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_online_repair -x 'dir' -s "repair directory" -t "%dir%"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
--- /dev/null
+QA output created by 796
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc. All Rights Reserved.
+#
+# FS QA Test No. 797
+#
+# Race fsstress and parent pointers repair for a while to see if we crash or
+# livelock.
+#
+. ./common/preamble
+_begin_fstest online_repair dangerous_fsstress_repair
+
+_cleanup() {
+ _scratch_xfs_stress_scrub_cleanup &> /dev/null
+ cd /
+ rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_online_repair
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_online_repair -s "repair parent" -t "%dir%"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
--- /dev/null
+QA output created by 797
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 798
+#
+# Make sure that xfs_scrub dry run, preen, and repair modes only modify the
+# things that they're allowed to touch.
+#
+. ./common/preamble
+_begin_fstest auto quick online_repair
+
+# Import common functions.
+. ./common/fuzzy
+. ./common/filter
+
+# real QA test starts here
+
+# Modify as appropriate.
+_supported_fs xfs
+_require_scratch_nocheck
+_require_scrub
+_require_xfs_db_command "fuzz"
+_require_xfs_io_command "repair"
+
+# Make sure we support repair?
+output="$($XFS_IO_PROG -x -c 'repair -R probe' $SCRATCH_MNT 2>&1)"
+test -z "$output" && _notrun 'kernel does not support repair'
+
+# Make sure xfs_scrub is new enough to support -p(reen)
+$XFS_SCRUB_PROG -p 2>&1 | grep -q 'invalid option' && \
+ _notrun 'scrub does not support -p'
+
+_scratch_mkfs | _filter_mkfs 2>$tmp.mkfs >/dev/null
+. $tmp.mkfs
+
+test $agcount -ge 3 || _notrun 'filesystem must have at least 3 AGs'
+
+AWK_PROG='
+{
+ if ($1 ~ /Optimized:/)
+ optimized++;
+ else if ($1 ~ /Repaired:/)
+ repaired++;
+ else if ($1 ~ /Corruption:/)
+ corruption++;
+}
+END {
+ printf("corruption: %u optimized: %u repaired: %u\n",
+ corruption, optimized, repaired);
+}
+'
+
+test_scrub() {
+ local mode="$1"
+ local scrub_arg="$2"
+ local db_args=(-x)
+
+ # Fuzz secondary superblocks because this won't cause mount failures
+ if [[ $mode =~ c ]]; then
+ db_args+=(-c 'sb 1' -c 'fuzz -d dblocks add')
+ fi
+ if [[ $mode =~ o ]]; then
+ db_args+=(-c 'sb 2' -c 'fuzz -d fname random')
+ fi
+
+ echo "testing mode? $mode scrub_arg $scrub_arg"
+ echo "db_args:${db_args[@]}:scrub_arg:$scrub_arg:$mode:" >> $seqres.full
+ echo "----------------" >> $seqres.full
+
+ _scratch_mkfs >> $seqres.full
+
+ # Make sure there's nothing left to optimize, at least according to
+ # xfs_scrub. This clears the way for us to make targeted changes to
+ # the filesystem.
+ _scratch_mount
+ _scratch_scrub $scrub_arg >> /dev/null
+ _scratch_unmount
+
+ # Modify the filesystem as needed to trip up xfs_scrub
+ _scratch_xfs_db "${db_args[@]}" >> $seqres.full
+
+ # See how many optimizations, repairs, and corruptions it'll report
+ _scratch_mount
+ _scratch_scrub $scrub_arg 2>&1 | awk "$AWK_PROG"
+ test "${PIPESTATUS[0]}" -eq 0 || echo "xfs_scrub returned ${PIPESTATUS[0]}?"
+ echo
+ _scratch_unmount
+}
+
+test_scrub 'o' -n # dry run with possible optimizations
+test_scrub 'o' -p # preen
+test_scrub 'o' # repair
+
+test_scrub 'co' -n # dry run with corruptions and optimizations
+test_scrub 'co' -p # preen
+test_scrub 'co' # repair
+
+test_scrub 'c' -n # dry run with corruptions
+test_scrub 'c' -p # preen
+test_scrub 'c' # repair
+
+# success, all done
+status=0
+exit
--- /dev/null
+QA output created by 798
+testing mode? o scrub_arg -n
+corruption: 0 optimized: 0 repaired: 0
+
+testing mode? o scrub_arg -p
+corruption: 0 optimized: 1 repaired: 0
+
+testing mode? o scrub_arg
+corruption: 0 optimized: 1 repaired: 0
+
+testing mode? co scrub_arg -n
+corruption: 1 optimized: 0 repaired: 0
+xfs_scrub returned 1?
+
+testing mode? co scrub_arg -p
+corruption: 1 optimized: 0 repaired: 0
+xfs_scrub returned 1?
+
+testing mode? co scrub_arg
+corruption: 0 optimized: 1 repaired: 1
+
+testing mode? c scrub_arg -n
+corruption: 1 optimized: 0 repaired: 0
+xfs_scrub returned 1?
+
+testing mode? c scrub_arg -p
+corruption: 1 optimized: 0 repaired: 0
+xfs_scrub returned 1?
+
+testing mode? c scrub_arg
+corruption: 0 optimized: 0 repaired: 1
+
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023 Oracle, Inc. All Rights Reserved.
+#
+# FS QA Test No. 799
+#
+# Race fsstress doing mostly renames and xfs_scrub in force-repair mode for a
+# while to see if we crash or livelock.
+#
+. ./common/preamble
+_begin_fstest online_repair dangerous_fsstress_repair
+
+_cleanup() {
+ cd /
+ _scratch_xfs_stress_scrub_cleanup &> /dev/null
+ rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_online_repair
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_online_repair -S '-k' -x 'parent'
+
+# success, all done
+echo Silence is golden
+status=0
+exit
--- /dev/null
+QA output created by 799
+Silence is golden
--- /dev/null
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023 Oracle, Inc. All Rights Reserved.
+#
+# FS QA Test No. 800
+#
+# Race fsstress doing mostly renames and xfs_scrub in read-only mode for a
+# while to see if we crash or livelock.
+#
+. ./common/preamble
+_begin_fstest scrub dangerous_fsstress_scrub
+
+_cleanup() {
+ cd /
+ _scratch_xfs_stress_scrub_cleanup &> /dev/null
+ rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_scrub
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_scrub -S '-n' -x 'parent'
+
+# success, all done
+echo Silence is golden
+status=0
+exit
--- /dev/null
+QA output created by 800
+Silence is golden
include $(BUILDRULES)
-install:
+install: default
$(INSTALL) -m 755 -d $(TARGET_DIR)
$(INSTALL) -m 755 $(TESTS) $(TARGET_DIR)
$(INSTALL) -m 644 group.list $(TARGET_DIR)
--- /dev/null
+#!/usr/bin/env perl
+# SPDX-License-Identifier: GPL-2.0
+#
+# This script is copied from linux/scripts/get_maintainer.pl, then
+# changes for fstests specifically.
+#
+# Print selected MAINTAINERS information for
+# the files modified in a patch or for a file
+#
+# usage: perl tools/get_maintainer.pl [OPTIONS] <patch>
+# perl tools/get_maintainer.pl [OPTIONS] -f <file>
+
+use warnings;
+use strict;
+
+my $P = $0;
+my $V = '0.26';
+
+use Getopt::Long qw(:config no_auto_abbrev);
+use Cwd;
+use File::Find;
+use File::Spec::Functions;
+
+my $cur_path = fastgetcwd() . '/';
+my $lk_path = "./";
+my $email = 1;
+my $email_usename = 1;
+my $email_maintainer = 1;
+my $email_reviewer = 1;
+my $email_fixes = 1;
+my $email_list = 1;
+my $email_moderated_list = 1;
+my $email_subscriber_list = 0;
+my $email_git = 0;
+my $email_git_all_signature_types = 0;
+my $email_git_blame = 0;
+my $email_git_blame_signatures = 1;
+my $email_git_fallback = 1;
+my $email_git_min_signatures = 1;
+my $email_git_max_maintainers = 5;
+my $email_git_min_percent = 5;
+my $email_git_since = "1-year-ago";
+my $email_hg_since = "-365";
+my $interactive = 0;
+my $email_remove_duplicates = 1;
+my $email_use_mailmap = 1;
+my $output_multiline = 1;
+my $output_separator = ", ";
+my $output_roles = 0;
+my $output_rolestats = 1;
+my $output_section_maxlen = 50;
+my $scm = 0;
+my $tree = 1;
+my $web = 0;
+my $subsystem = 0;
+my $status = 0;
+my $letters = "";
+my $keywords = 1;
+my $sections = 0;
+my $email_file_emails = 0;
+my $from_filename = 0;
+my $pattern_depth = 0;
+my $self_test = undef;
+my $version = 0;
+my $help = 0;
+my $find_maintainer_files = 0;
+my $maintainer_path;
+my $vcs_used = 0;
+
+my $exit = 0;
+
+my @files = ();
+my @fixes = (); # If a patch description includes Fixes: lines
+my @range = ();
+my @keyword_tvi = ();
+my @file_emails = ();
+
+my %commit_author_hash;
+my %commit_signer_hash;
+
+# Signature types of people who are either
+# a) responsible for the code in question, or
+# b) familiar enough with it to give relevant feedback
+my @signature_tags = ();
+push(@signature_tags, "Signed-off-by:");
+push(@signature_tags, "Reviewed-by:");
+push(@signature_tags, "Acked-by:");
+
+my $signature_pattern = "\(" . join("|", @signature_tags) . "\)";
+
+# rfc822 email address - preloaded methods go here.
+my $rfc822_lwsp = "(?:(?:\\r\\n)?[ \\t])";
+my $rfc822_char = '[\\000-\\377]';
+
+# VCS command support: class-like functions and strings
+
+my %VCS_cmds;
+
+my %VCS_cmds_git = (
+ "execute_cmd" => \&git_execute_cmd,
+ "available" => '(which("git") ne "") && (-e ".git")',
+ "find_signers_cmd" =>
+ "git log --no-color --follow --since=\$email_git_since " .
+ '--numstat --no-merges ' .
+ '--format="GitCommit: %H%n' .
+ 'GitAuthor: %an <%ae>%n' .
+ 'GitDate: %aD%n' .
+ 'GitSubject: %s%n' .
+ '%b%n"' .
+ " -- \$file",
+ "find_commit_signers_cmd" =>
+ "git log --no-color " .
+ '--numstat ' .
+ '--format="GitCommit: %H%n' .
+ 'GitAuthor: %an <%ae>%n' .
+ 'GitDate: %aD%n' .
+ 'GitSubject: %s%n' .
+ '%b%n"' .
+ " -1 \$commit",
+ "find_commit_author_cmd" =>
+ "git log --no-color " .
+ '--numstat ' .
+ '--format="GitCommit: %H%n' .
+ 'GitAuthor: %an <%ae>%n' .
+ 'GitDate: %aD%n' .
+ 'GitSubject: %s%n"' .
+ " -1 \$commit",
+ "blame_range_cmd" => "git blame -l -L \$diff_start,+\$diff_length \$file",
+ "blame_file_cmd" => "git blame -l \$file",
+ "commit_pattern" => "^GitCommit: ([0-9a-f]{40,40})",
+ "blame_commit_pattern" => "^([0-9a-f]+) ",
+ "author_pattern" => "^GitAuthor: (.*)",
+ "subject_pattern" => "^GitSubject: (.*)",
+ "stat_pattern" => "^(\\d+)\\t(\\d+)\\t\$file\$",
+ "file_exists_cmd" => "git ls-files \$file",
+ "list_files_cmd" => "git ls-files \$file",
+);
+
+my %VCS_cmds_hg = (
+ "execute_cmd" => \&hg_execute_cmd,
+ "available" => '(which("hg") ne "") && (-d ".hg")',
+ "find_signers_cmd" =>
+ "hg log --date=\$email_hg_since " .
+ "--template='HgCommit: {node}\\n" .
+ "HgAuthor: {author}\\n" .
+ "HgSubject: {desc}\\n'" .
+ " -- \$file",
+ "find_commit_signers_cmd" =>
+ "hg log " .
+ "--template='HgSubject: {desc}\\n'" .
+ " -r \$commit",
+ "find_commit_author_cmd" =>
+ "hg log " .
+ "--template='HgCommit: {node}\\n" .
+ "HgAuthor: {author}\\n" .
+ "HgSubject: {desc|firstline}\\n'" .
+ " -r \$commit",
+ "blame_range_cmd" => "", # not supported
+ "blame_file_cmd" => "hg blame -n \$file",
+ "commit_pattern" => "^HgCommit: ([0-9a-f]{40,40})",
+ "blame_commit_pattern" => "^([ 0-9a-f]+):",
+ "author_pattern" => "^HgAuthor: (.*)",
+ "subject_pattern" => "^HgSubject: (.*)",
+ "stat_pattern" => "^(\\d+)\t(\\d+)\t\$file\$",
+ "file_exists_cmd" => "hg files \$file",
+ "list_files_cmd" => "hg manifest -R \$file",
+);
+
+my $conf = which_conf(".get_maintainer.conf");
+if (-f $conf) {
+ my @conf_args;
+ open(my $conffile, '<', "$conf")
+ or warn "$P: Can't find a readable .get_maintainer.conf file $!\n";
+
+ while (<$conffile>) {
+ my $line = $_;
+
+ $line =~ s/\s*\n?$//g;
+ $line =~ s/^\s*//g;
+ $line =~ s/\s+/ /g;
+
+ next if ($line =~ m/^\s*#/);
+ next if ($line =~ m/^\s*$/);
+
+ my @words = split(" ", $line);
+ foreach my $word (@words) {
+ last if ($word =~ m/^#/);
+ push (@conf_args, $word);
+ }
+ }
+ close($conffile);
+ unshift(@ARGV, @conf_args) if @conf_args;
+}
+
+my @ignore_emails = ();
+my $ignore_file = which_conf(".get_maintainer.ignore");
+if (-f $ignore_file) {
+ open(my $ignore, '<', "$ignore_file")
+ or warn "$P: Can't find a readable .get_maintainer.ignore file $!\n";
+ while (<$ignore>) {
+ my $line = $_;
+
+ $line =~ s/\s*\n?$//;
+ $line =~ s/^\s*//;
+ $line =~ s/\s+$//;
+ $line =~ s/#.*$//;
+
+ next if ($line =~ m/^\s*$/);
+ if (rfc822_valid($line)) {
+ push(@ignore_emails, $line);
+ }
+ }
+ close($ignore);
+}
+
+if ($#ARGV > 0) {
+ foreach (@ARGV) {
+ if ($_ =~ /^-{1,2}self-test(?:=|$)/) {
+ die "$P: using --self-test does not allow any other option or argument\n";
+ }
+ }
+}
+
+if (!GetOptions(
+ 'email!' => \$email,
+ 'git!' => \$email_git,
+ 'git-all-signature-types!' => \$email_git_all_signature_types,
+ 'git-blame!' => \$email_git_blame,
+ 'git-blame-signatures!' => \$email_git_blame_signatures,
+ 'git-fallback!' => \$email_git_fallback,
+ 'git-min-signatures=i' => \$email_git_min_signatures,
+ 'git-max-maintainers=i' => \$email_git_max_maintainers,
+ 'git-min-percent=i' => \$email_git_min_percent,
+ 'git-since=s' => \$email_git_since,
+ 'hg-since=s' => \$email_hg_since,
+ 'i|interactive!' => \$interactive,
+ 'remove-duplicates!' => \$email_remove_duplicates,
+ 'mailmap!' => \$email_use_mailmap,
+ 'm!' => \$email_maintainer,
+ 'r!' => \$email_reviewer,
+ 'n!' => \$email_usename,
+ 'l!' => \$email_list,
+ 'fixes!' => \$email_fixes,
+ 'moderated!' => \$email_moderated_list,
+ 's!' => \$email_subscriber_list,
+ 'multiline!' => \$output_multiline,
+ 'roles!' => \$output_roles,
+ 'rolestats!' => \$output_rolestats,
+ 'separator=s' => \$output_separator,
+ 'subsystem!' => \$subsystem,
+ 'status!' => \$status,
+ 'scm!' => \$scm,
+ 'tree!' => \$tree,
+ 'web!' => \$web,
+ 'letters=s' => \$letters,
+ 'pattern-depth=i' => \$pattern_depth,
+ 'k|keywords!' => \$keywords,
+ 'sections!' => \$sections,
+ 'fe|file-emails!' => \$email_file_emails,
+ 'f|file' => \$from_filename,
+ 'find-maintainer-files' => \$find_maintainer_files,
+ 'mpath|maintainer-path=s' => \$maintainer_path,
+ 'self-test:s' => \$self_test,
+ 'v|version' => \$version,
+ 'h|help|usage' => \$help,
+ )) {
+ die "$P: invalid argument - use --help if necessary\n";
+}
+
+if ($help != 0) {
+ usage();
+ exit 0;
+}
+
+if ($version != 0) {
+ print("${P} ${V}\n");
+ exit 0;
+}
+
+if (defined $self_test) {
+ read_all_maintainer_files();
+ self_test();
+ exit 0;
+}
+
+if (-t STDIN && !@ARGV) {
+ # We're talking to a terminal, but have no command line arguments.
+ die "$P: missing patchfile or -f file - use --help if necessary\n";
+}
+
+$output_multiline = 0 if ($output_separator ne ", ");
+$output_rolestats = 1 if ($interactive);
+$output_roles = 1 if ($output_rolestats);
+
+if ($sections || $letters ne "") {
+ $sections = 1;
+ $email = 0;
+ $email_list = 0;
+ $scm = 0;
+ $status = 0;
+ $subsystem = 0;
+ $web = 0;
+ $keywords = 0;
+ $interactive = 0;
+} else {
+ my $selections = $email + $scm + $status + $subsystem + $web;
+ if ($selections == 0) {
+ die "$P: Missing required option: email, scm, status, subsystem or web\n";
+ }
+}
+
+if ($email &&
+ ($email_maintainer + $email_reviewer +
+ $email_list + $email_subscriber_list +
+ $email_git + $email_git_blame) == 0) {
+ die "$P: Please select at least 1 email option\n";
+}
+
+if ($tree && !top_of_fstests_tree($lk_path)) {
+ die "$P: The current directory does not appear to be "
+ . "a fstests source tree.\n";
+}
+
+## Read MAINTAINERS for type/value pairs
+
+my @typevalue = ();
+my %keyword_hash;
+my @mfiles = ();
+my @self_test_info = ();
+
+sub read_maintainer_file {
+ my ($file) = @_;
+
+ open (my $maint, '<', "$file")
+ or die "$P: Can't open MAINTAINERS file '$file': $!\n";
+ my $i = 1;
+ while (<$maint>) {
+ my $line = $_;
+ chomp $line;
+
+ if ($line =~ m/^([A-Z]):\s*(.*)/) {
+ my $type = $1;
+ my $value = $2;
+
+ ##Filename pattern matching
+ if ($type eq "F" || $type eq "X") {
+ $value =~ s@\.@\\\.@g; ##Convert . to \.
+ $value =~ s/\*/\.\*/g; ##Convert * to .*
+ $value =~ s/\?/\./g; ##Convert ? to .
+ ##if pattern is a directory and it lacks a trailing slash, add one
+ if ((-d $value)) {
+ $value =~ s@([^/])$@$1/@;
+ }
+ } elsif ($type eq "K") {
+ $keyword_hash{@typevalue} = $value;
+ }
+ push(@typevalue, "$type:$value");
+ } elsif (!(/^\s*$/ || /^\s*\#/)) {
+ push(@typevalue, $line);
+ }
+ if (defined $self_test) {
+ push(@self_test_info, {file=>$file, linenr=>$i, line=>$line});
+ }
+ $i++;
+ }
+ close($maint);
+}
+
+sub find_is_maintainer_file {
+ my ($file) = $_;
+ return if ($file !~ m@/MAINTAINERS$@);
+ $file = $File::Find::name;
+ return if (! -f $file);
+ push(@mfiles, $file);
+}
+
+sub find_ignore_git {
+ return grep { $_ !~ /^\.git$/; } @_;
+}
+
+read_all_maintainer_files();
+
+sub read_all_maintainer_files {
+ my $path = "${lk_path}MAINTAINERS";
+ if (defined $maintainer_path) {
+ $path = $maintainer_path;
+ # Perl Cookbook tilde expansion if necessary
+ $path =~ s@^~([^/]*)@ $1 ? (getpwnam($1))[7] : ( $ENV{HOME} || $ENV{LOGDIR} || (getpwuid($<))[7])@ex;
+ }
+
+ if (-d $path) {
+ $path .= '/' if ($path !~ m@/$@);
+ if ($find_maintainer_files) {
+ find( { wanted => \&find_is_maintainer_file,
+ preprocess => \&find_ignore_git,
+ no_chdir => 1,
+ }, "$path");
+ } else {
+ opendir(DIR, "$path") or die $!;
+ my @files = readdir(DIR);
+ closedir(DIR);
+ foreach my $file (@files) {
+ push(@mfiles, "$path$file") if ($file !~ /^\./);
+ }
+ }
+ } elsif (-f "$path") {
+ push(@mfiles, "$path");
+ } else {
+ die "$P: MAINTAINER file not found '$path'\n";
+ }
+ die "$P: No MAINTAINER files found in '$path'\n" if (scalar(@mfiles) == 0);
+ foreach my $file (@mfiles) {
+ read_maintainer_file("$file");
+ }
+}
+
+sub maintainers_in_file {
+ my ($file) = @_;
+
+ return if ($file =~ m@\bMAINTAINERS$@);
+
+ if (-f $file && ($email_file_emails || $file =~ /\.yaml$/)) {
+ open(my $f, '<', $file)
+ or die "$P: Can't open $file: $!\n";
+ my $text = do { local($/) ; <$f> };
+ close($f);
+
+ my @poss_addr = $text =~ m$[A-Za-zÀ-ÿ\"\' \,\.\+-]*\s*[\,]*\s*[\(\<\{]{0,1}[A-Za-z0-9_\.\+-]+\@[A-Za-z0-9\.-]+\.[A-Za-z0-9]+[\)\>\}]{0,1}$g;
+ push(@file_emails, clean_file_emails(@poss_addr));
+ }
+}
+
+#
+# Read mail address map
+#
+
+my $mailmap;
+
+read_mailmap();
+
+sub read_mailmap {
+ $mailmap = {
+ names => {},
+ addresses => {}
+ };
+
+ return if (!$email_use_mailmap || !(-f "${lk_path}.mailmap"));
+
+ open(my $mailmap_file, '<', "${lk_path}.mailmap")
+ or warn "$P: Can't open .mailmap: $!\n";
+
+ while (<$mailmap_file>) {
+ s/#.*$//; #strip comments
+ s/^\s+|\s+$//g; #trim
+
+ next if (/^\s*$/); #skip empty lines
+ #entries have one of the following formats:
+ # name1 <mail1>
+ # <mail1> <mail2>
+ # name1 <mail1> <mail2>
+ # name1 <mail1> name2 <mail2>
+ # (see man git-shortlog)
+
+ if (/^([^<]+)<([^>]+)>$/) {
+ my $real_name = $1;
+ my $address = $2;
+
+ $real_name =~ s/\s+$//;
+ ($real_name, $address) = parse_email("$real_name <$address>");
+ $mailmap->{names}->{$address} = $real_name;
+
+ } elsif (/^<([^>]+)>\s*<([^>]+)>$/) {
+ my $real_address = $1;
+ my $wrong_address = $2;
+
+ $mailmap->{addresses}->{$wrong_address} = $real_address;
+
+ } elsif (/^(.+)<([^>]+)>\s*<([^>]+)>$/) {
+ my $real_name = $1;
+ my $real_address = $2;
+ my $wrong_address = $3;
+
+ $real_name =~ s/\s+$//;
+ ($real_name, $real_address) =
+ parse_email("$real_name <$real_address>");
+ $mailmap->{names}->{$wrong_address} = $real_name;
+ $mailmap->{addresses}->{$wrong_address} = $real_address;
+
+ } elsif (/^(.+)<([^>]+)>\s*(.+)\s*<([^>]+)>$/) {
+ my $real_name = $1;
+ my $real_address = $2;
+ my $wrong_name = $3;
+ my $wrong_address = $4;
+
+ $real_name =~ s/\s+$//;
+ ($real_name, $real_address) =
+ parse_email("$real_name <$real_address>");
+
+ $wrong_name =~ s/\s+$//;
+ ($wrong_name, $wrong_address) =
+ parse_email("$wrong_name <$wrong_address>");
+
+ my $wrong_email = format_email($wrong_name, $wrong_address, 1);
+ $mailmap->{names}->{$wrong_email} = $real_name;
+ $mailmap->{addresses}->{$wrong_email} = $real_address;
+ }
+ }
+ close($mailmap_file);
+}
+
+## use the filenames on the command line or find the filenames in the patchfiles
+
+if (!@ARGV) {
+ push(@ARGV, "&STDIN");
+}
+
+foreach my $file (@ARGV) {
+ if ($file ne "&STDIN") {
+ $file = canonpath($file);
+ ##if $file is a directory and it lacks a trailing slash, add one
+ if ((-d $file)) {
+ $file =~ s@([^/])$@$1/@;
+ } elsif (!(-f $file)) {
+ die "$P: file '${file}' not found\n";
+ }
+ }
+ if ($from_filename && (vcs_exists() && !vcs_file_exists($file))) {
+ warn "$P: file '$file' not found in version control $!\n";
+ }
+ if ($from_filename || ($file ne "&STDIN" && vcs_file_exists($file))) {
+ $file =~ s/^\Q${cur_path}\E//; #strip any absolute path
+ $file =~ s/^\Q${lk_path}\E//; #or the path to the lk tree
+ push(@files, $file);
+ if ($file ne "MAINTAINERS" && -f $file && $keywords) {
+ open(my $f, '<', $file)
+ or die "$P: Can't open $file: $!\n";
+ my $text = do { local($/) ; <$f> };
+ close($f);
+ if ($keywords) {
+ foreach my $line (keys %keyword_hash) {
+ if ($text =~ m/$keyword_hash{$line}/x) {
+ push(@keyword_tvi, $line);
+ }
+ }
+ }
+ }
+ } else {
+ my $file_cnt = @files;
+ my $lastfile;
+
+ open(my $patch, "< $file")
+ or die "$P: Can't open $file: $!\n";
+
+ # We can check arbitrary information before the patch
+ # like the commit message, mail headers, etc...
+ # This allows us to match arbitrary keywords against any part
+ # of a git format-patch generated file (subject tags, etc...)
+
+ my $patch_prefix = ""; #Parsing the intro
+
+ while (<$patch>) {
+ my $patch_line = $_;
+ if (m/^ mode change [0-7]+ => [0-7]+ (\S+)\s*$/) {
+ my $filename = $1;
+ push(@files, $filename);
+ } elsif (m/^rename (?:from|to) (\S+)\s*$/) {
+ my $filename = $1;
+ push(@files, $filename);
+ } elsif (m/^diff --git a\/(\S+) b\/(\S+)\s*$/) {
+ my $filename1 = $1;
+ my $filename2 = $2;
+ push(@files, $filename1);
+ push(@files, $filename2);
+ } elsif (m/^Fixes:\s+([0-9a-fA-F]{6,40})/) {
+ push(@fixes, $1) if ($email_fixes);
+ } elsif (m/^\+\+\+\s+(\S+)/ or m/^---\s+(\S+)/) {
+ my $filename = $1;
+ $filename =~ s@^[^/]*/@@;
+ $filename =~ s@\n@@;
+ $lastfile = $filename;
+ push(@files, $filename);
+ $patch_prefix = "^[+-].*"; #Now parsing the actual patch
+ } elsif (m/^\@\@ -(\d+),(\d+)/) {
+ if ($email_git_blame) {
+ push(@range, "$lastfile:$1:$2");
+ }
+ } elsif ($keywords) {
+ foreach my $line (keys %keyword_hash) {
+ if ($patch_line =~ m/${patch_prefix}$keyword_hash{$line}/x) {
+ push(@keyword_tvi, $line);
+ }
+ }
+ }
+ }
+ close($patch);
+
+ if ($file_cnt == @files) {
+ warn "$P: file '${file}' doesn't appear to be a patch. "
+ . "Add -f to options?\n";
+ }
+ @files = sort_and_uniq(@files);
+ }
+}
+
+@file_emails = uniq(@file_emails);
+@fixes = uniq(@fixes);
+
+my %email_hash_name;
+my %email_hash_address;
+my @email_to = ();
+my %hash_list_to;
+my @list_to = ();
+my @scm = ();
+my @web = ();
+my @subsystem = ();
+my @status = ();
+my %deduplicate_name_hash = ();
+my %deduplicate_address_hash = ();
+
+my @maintainers = get_maintainers();
+if (@maintainers) {
+ @maintainers = merge_email(@maintainers);
+ output(@maintainers);
+}
+
+if ($scm) {
+ @scm = uniq(@scm);
+ output(@scm);
+}
+
+if ($status) {
+ @status = uniq(@status);
+ output(@status);
+}
+
+if ($subsystem) {
+ @subsystem = uniq(@subsystem);
+ output(@subsystem);
+}
+
+if ($web) {
+ @web = uniq(@web);
+ output(@web);
+}
+
+exit($exit);
+
+sub self_test {
+ my @lsfiles = ();
+ my @good_links = ();
+ my @bad_links = ();
+ my @section_headers = ();
+ my $index = 0;
+
+ @lsfiles = vcs_list_files($lk_path);
+
+ for my $x (@self_test_info) {
+ $index++;
+
+ ## Section header duplication and missing section content
+ if (($self_test eq "" || $self_test =~ /\bsections\b/) &&
+ $x->{line} =~ /^\S[^:]/ &&
+ defined $self_test_info[$index] &&
+ $self_test_info[$index]->{line} =~ /^([A-Z]):\s*\S/) {
+ my $has_S = 0;
+ my $has_F = 0;
+ my $has_ML = 0;
+ my $status = "";
+ if (grep(m@^\Q$x->{line}\E@, @section_headers)) {
+ print("$x->{file}:$x->{linenr}: warning: duplicate section header\t$x->{line}\n");
+ } else {
+ push(@section_headers, $x->{line});
+ }
+ my $nextline = $index;
+ while (defined $self_test_info[$nextline] &&
+ $self_test_info[$nextline]->{line} =~ /^([A-Z]):\s*(\S.*)/) {
+ my $type = $1;
+ my $value = $2;
+ if ($type eq "S") {
+ $has_S = 1;
+ $status = $value;
+ } elsif ($type eq "F" || $type eq "N") {
+ $has_F = 1;
+ } elsif ($type eq "M" || $type eq "R" || $type eq "L") {
+ $has_ML = 1;
+ }
+ $nextline++;
+ }
+ if (!$has_ML && $status !~ /orphan|obsolete/i) {
+ print("$x->{file}:$x->{linenr}: warning: section without email address\t$x->{line}\n");
+ }
+ if (!$has_S) {
+ print("$x->{file}:$x->{linenr}: warning: section without status \t$x->{line}\n");
+ }
+ if (!$has_F) {
+ print("$x->{file}:$x->{linenr}: warning: section without file pattern\t$x->{line}\n");
+ }
+ }
+
+ next if ($x->{line} !~ /^([A-Z]):\s*(.*)/);
+
+ my $type = $1;
+ my $value = $2;
+
+ ## Filename pattern matching
+ if (($type eq "F" || $type eq "X") &&
+ ($self_test eq "" || $self_test =~ /\bpatterns\b/)) {
+ $value =~ s@\.@\\\.@g; ##Convert . to \.
+ $value =~ s/\*/\.\*/g; ##Convert * to .*
+ $value =~ s/\?/\./g; ##Convert ? to .
+ ##if pattern is a directory and it lacks a trailing slash, add one
+ if ((-d $value)) {
+ $value =~ s@([^/])$@$1/@;
+ }
+ if (!grep(m@^$value@, @lsfiles)) {
+ print("$x->{file}:$x->{linenr}: warning: no file matches\t$x->{line}\n");
+ }
+
+ ## Link reachability
+ } elsif (($type eq "W" || $type eq "Q" || $type eq "B") &&
+ $value =~ /^https?:/ &&
+ ($self_test eq "" || $self_test =~ /\blinks\b/)) {
+ next if (grep(m@^\Q$value\E$@, @good_links));
+ my $isbad = 0;
+ if (grep(m@^\Q$value\E$@, @bad_links)) {
+ $isbad = 1;
+ } else {
+ my $output = `wget --spider -q --no-check-certificate --timeout 10 --tries 1 $value`;
+ if ($? == 0) {
+ push(@good_links, $value);
+ } else {
+ push(@bad_links, $value);
+ $isbad = 1;
+ }
+ }
+ if ($isbad) {
+ print("$x->{file}:$x->{linenr}: warning: possible bad link\t$x->{line}\n");
+ }
+
+ ## SCM reachability
+ } elsif ($type eq "T" &&
+ ($self_test eq "" || $self_test =~ /\bscm\b/)) {
+ next if (grep(m@^\Q$value\E$@, @good_links));
+ my $isbad = 0;
+ if (grep(m@^\Q$value\E$@, @bad_links)) {
+ $isbad = 1;
+ } elsif ($value !~ /^(?:git|quilt|hg)\s+\S/) {
+ print("$x->{file}:$x->{linenr}: warning: malformed entry\t$x->{line}\n");
+ } elsif ($value =~ /^git\s+(\S+)(\s+([^\(]+\S+))?/) {
+ my $url = $1;
+ my $branch = "";
+ $branch = $3 if $3;
+ my $output = `git ls-remote --exit-code -h "$url" $branch > /dev/null 2>&1`;
+ if ($? == 0) {
+ push(@good_links, $value);
+ } else {
+ push(@bad_links, $value);
+ $isbad = 1;
+ }
+ } elsif ($value =~ /^(?:quilt|hg)\s+(https?:\S+)/) {
+ my $url = $1;
+ my $output = `wget --spider -q --no-check-certificate --timeout 10 --tries 1 $url`;
+ if ($? == 0) {
+ push(@good_links, $value);
+ } else {
+ push(@bad_links, $value);
+ $isbad = 1;
+ }
+ }
+ if ($isbad) {
+ print("$x->{file}:$x->{linenr}: warning: possible bad link\t$x->{line}\n");
+ }
+ }
+ }
+}
+
+sub ignore_email_address {
+ my ($address) = @_;
+
+ foreach my $ignore (@ignore_emails) {
+ return 1 if ($ignore eq $address);
+ }
+
+ return 0;
+}
+
+sub range_is_maintained {
+ my ($start, $end) = @_;
+
+ for (my $i = $start; $i < $end; $i++) {
+ my $line = $typevalue[$i];
+ if ($line =~ m/^([A-Z]):\s*(.*)/) {
+ my $type = $1;
+ my $value = $2;
+ if ($type eq 'S') {
+ if ($value =~ /(maintain|support)/i) {
+ return 1;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+sub range_has_maintainer {
+ my ($start, $end) = @_;
+
+ for (my $i = $start; $i < $end; $i++) {
+ my $line = $typevalue[$i];
+ if ($line =~ m/^([A-Z]):\s*(.*)/) {
+ my $type = $1;
+ my $value = $2;
+ if ($type eq 'M') {
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+sub get_maintainers {
+ %email_hash_name = ();
+ %email_hash_address = ();
+ %commit_author_hash = ();
+ %commit_signer_hash = ();
+ @email_to = ();
+ %hash_list_to = ();
+ @list_to = ();
+ @scm = ();
+ @web = ();
+ @subsystem = ();
+ @status = ();
+ %deduplicate_name_hash = ();
+ %deduplicate_address_hash = ();
+ if ($email_git_all_signature_types) {
+ $signature_pattern = "(.+?)[Bb][Yy]:";
+ } else {
+ $signature_pattern = "\(" . join("|", @signature_tags) . "\)";
+ }
+
+ # Find responsible parties
+
+ my %exact_pattern_match_hash = ();
+
+ foreach my $file (@files) {
+
+ my %hash;
+ my $tvi = find_first_section();
+ while ($tvi < @typevalue) {
+ my $start = find_starting_index($tvi);
+ my $end = find_ending_index($tvi);
+ my $exclude = 0;
+ my $i;
+
+ #Do not match excluded file patterns
+
+ for ($i = $start; $i < $end; $i++) {
+ my $line = $typevalue[$i];
+ if ($line =~ m/^([A-Z]):\s*(.*)/) {
+ my $type = $1;
+ my $value = $2;
+ if ($type eq 'X') {
+ if (file_match_pattern($file, $value)) {
+ $exclude = 1;
+ last;
+ }
+ }
+ }
+ }
+
+ if (!$exclude) {
+ for ($i = $start; $i < $end; $i++) {
+ my $line = $typevalue[$i];
+ if ($line =~ m/^([A-Z]):\s*(.*)/) {
+ my $type = $1;
+ my $value = $2;
+ if ($type eq 'F') {
+ if (file_match_pattern($file, $value)) {
+ my $value_pd = ($value =~ tr@/@@);
+ my $file_pd = ($file =~ tr@/@@);
+ $value_pd++ if (substr($value,-1,1) ne "/");
+ $value_pd = -1 if ($value =~ /^\.\*/);
+ if ($value_pd >= $file_pd &&
+ range_is_maintained($start, $end) &&
+ range_has_maintainer($start, $end)) {
+ $exact_pattern_match_hash{$file} = 1;
+ }
+ if ($pattern_depth == 0 ||
+ (($file_pd - $value_pd) < $pattern_depth)) {
+ $hash{$tvi} = $value_pd;
+ }
+ }
+ } elsif ($type eq 'N') {
+ if ($file =~ m/$value/x) {
+ $hash{$tvi} = 0;
+ }
+ }
+ }
+ }
+ }
+ $tvi = $end + 1;
+ }
+
+ foreach my $line (sort {$hash{$b} <=> $hash{$a}} keys %hash) {
+ add_categories($line);
+ if ($sections) {
+ my $i;
+ my $start = find_starting_index($line);
+ my $end = find_ending_index($line);
+ for ($i = $start; $i < $end; $i++) {
+ my $line = $typevalue[$i];
+ if ($line =~ /^[FX]:/) { ##Restore file patterns
+ $line =~ s/([^\\])\.([^\*])/$1\?$2/g;
+ $line =~ s/([^\\])\.$/$1\?/g; ##Convert . back to ?
+ $line =~ s/\\\./\./g; ##Convert \. to .
+ $line =~ s/\.\*/\*/g; ##Convert .* to *
+ }
+ my $count = $line =~ s/^([A-Z]):/$1:\t/g;
+ if ($letters eq "" || (!$count || $letters =~ /$1/i)) {
+ print("$line\n");
+ }
+ }
+ print("\n");
+ }
+ }
+
+ maintainers_in_file($file);
+ }
+
+ if ($keywords) {
+ @keyword_tvi = sort_and_uniq(@keyword_tvi);
+ foreach my $line (@keyword_tvi) {
+ add_categories($line);
+ }
+ }
+
+ foreach my $email (@email_to, @list_to) {
+ $email->[0] = deduplicate_email($email->[0]);
+ }
+
+ foreach my $file (@files) {
+ if ($email &&
+ ($email_git ||
+ ($email_git_fallback &&
+ $file !~ /MAINTAINERS$/ &&
+ !$exact_pattern_match_hash{$file}))) {
+ vcs_file_signoffs($file);
+ }
+ if ($email && $email_git_blame) {
+ vcs_file_blame($file);
+ }
+ }
+
+ if ($email) {
+ foreach my $email (@file_emails) {
+ $email = mailmap_email($email);
+ my ($name, $address) = parse_email($email);
+
+ my $tmp_email = format_email($name, $address, $email_usename);
+ push_email_address($tmp_email, '');
+ add_role($tmp_email, 'in file');
+ }
+ }
+
+ foreach my $fix (@fixes) {
+ vcs_add_commit_signers($fix, "blamed_fixes");
+ }
+
+ my @to = ();
+ if ($email || $email_list) {
+ if ($email) {
+ @to = (@to, @email_to);
+ }
+ if ($email_list) {
+ @to = (@to, @list_to);
+ }
+ }
+
+ if ($interactive) {
+ @to = interactive_get_maintainers(\@to);
+ }
+
+ return @to;
+}
+
+sub file_match_pattern {
+ my ($file, $pattern) = @_;
+ if (substr($pattern, -1) eq "/") {
+ if ($file =~ m@^$pattern@) {
+ return 1;
+ }
+ } else {
+ if ($file =~ m@^$pattern@) {
+ my $s1 = ($file =~ tr@/@@);
+ my $s2 = ($pattern =~ tr@/@@);
+ if ($s1 == $s2) {
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+sub usage {
+ print <<EOT;
+usage: $P [options] patchfile
+ $P [options] -f file|directory
+version: $V
+
+MAINTAINER field selection options:
+ --email => print email address(es) if any
+ --git => include recent git \*-by: signers
+ --git-all-signature-types => include signers regardless of signature type
+ or use only ${signature_pattern} signers (default: $email_git_all_signature_types)
+ --git-fallback => use git when no exact MAINTAINERS pattern (default: $email_git_fallback)
+ --git-min-signatures => number of signatures required (default: $email_git_min_signatures)
+ --git-max-maintainers => maximum maintainers to add (default: $email_git_max_maintainers)
+ --git-min-percent => minimum percentage of commits required (default: $email_git_min_percent)
+ --git-blame => use git blame to find modified commits for patch or file
+ --git-blame-signatures => when used with --git-blame, also include all commit signers
+ --git-since => git history to use (default: $email_git_since)
+ --hg-since => hg history to use (default: $email_hg_since)
+ --interactive => display a menu (mostly useful if used with the --git option)
+ --m => include maintainer(s) if any
+ --r => include reviewer(s) if any
+ --n => include name 'Full Name <addr\@domain.tld>'
+ --l => include list(s) if any
+ --moderated => include moderated lists(s) if any (default: true)
+ --s => include subscriber only list(s) if any (default: false)
+ --remove-duplicates => minimize duplicate email names/addresses
+ --roles => show roles (status:subsystem, git-signer, list, etc...)
+ --rolestats => show roles and statistics (commits/total_commits, %)
+ --file-emails => add email addresses found in -f file (default: 0 (off))
+ --fixes => for patches, add signatures of commits with 'Fixes: <commit>' (default: 1 (on))
+ --scm => print SCM tree(s) if any
+ --status => print status if any
+ --subsystem => print subsystem name if any
+ --web => print website(s) if any
+
+Output type options:
+ --separator [, ] => separator for multiple entries on 1 line
+ using --separator also sets --nomultiline if --separator is not [, ]
+ --multiline => print 1 entry per line
+
+Other options:
+ --pattern-depth => Number of pattern directory traversals (default: 0 (all))
+ --keywords => scan patch for keywords (default: $keywords)
+ --sections => print all of the subsystem sections with pattern matches
+ --letters => print all matching 'letter' types from all matching sections
+ --mailmap => use .mailmap file (default: $email_use_mailmap)
+ --no-tree => run without a kernel tree
+ --self-test => show potential issues with MAINTAINERS file content
+ --version => show version
+ --help => show this help information
+
+Default options:
+ [--email --tree --nogit --git-fallback --m --r --n --l --multiline
+ --pattern-depth=0 --remove-duplicates --rolestats]
+
+Notes:
+ Using "-f directory" may give unexpected results:
+ Used with "--git", git signators for _all_ files in and below
+ directory are examined as git recurses directories.
+ Any specified X: (exclude) pattern matches are _not_ ignored.
+ Used with "--nogit", directory is used as a pattern match,
+ no individual file within the directory or subdirectory
+ is matched.
+ Used with "--git-blame", does not iterate all files in directory
+ Using "--git-blame" is slow and may add old committers and authors
+ that are no longer active maintainers to the output.
+ Using "--roles" or "--rolestats" with git send-email --cc-cmd or any
+ other automated tools that expect only ["name"] <email address>
+ may not work because of additional output after <email address>.
+ Using "--rolestats" and "--git-blame" shows the #/total=% commits,
+ not the percentage of the entire file authored. # of commits is
+ not a good measure of amount of code authored. 1 major commit may
+ contain a thousand lines, 5 trivial commits may modify a single line.
+ If git is not installed, but mercurial (hg) is installed and an .hg
+ repository exists, the following options apply to mercurial:
+ --git,
+ --git-min-signatures, --git-max-maintainers, --git-min-percent, and
+ --git-blame
+ Use --hg-since not --git-since to control date selection
+ File ".get_maintainer.conf", if it exists in the fstests source root
+ directory, can change whatever get_maintainer defaults are desired.
+ Entries in this file can be any command line argument.
+ This file is prepended to any additional command line arguments.
+ Multiple lines and # comments are allowed.
+ Most options have both positive and negative forms.
+ The negative forms for --<foo> are --no<foo> and --no-<foo>.
+
+EOT
+}
+
+sub top_of_fstests_tree {
+ my ($lk_path) = @_;
+
+ if ($lk_path ne "" && substr($lk_path,length($lk_path)-1,1) ne "/") {
+ $lk_path .= "/";
+ }
+ if ( (-f "${lk_path}check")
+ && (-f "${lk_path}local.config.example")
+ && (-e "${lk_path}MAINTAINERS")
+ && (-f "${lk_path}Makefile")
+ && (-f "${lk_path}README")
+ && (-f "${lk_path}new")
+ && (-d "${lk_path}LICENSES")
+ && (-d "${lk_path}tests")
+ && (-d "${lk_path}common")
+ && (-d "${lk_path}src")
+ && (-d "${lk_path}tools")
+ && (-d "${lk_path}include")
+ && (-d "${lk_path}m4")
+ && (-d "${lk_path}lib")
+ && (-d "${lk_path}doc")) {
+ return 1;
+ }
+ return 0;
+}
+
+sub parse_email {
+ my ($formatted_email) = @_;
+
+ my $name = "";
+ my $address = "";
+
+ if ($formatted_email =~ /^([^<]+)<(.+\@.*)>.*$/) {
+ $name = $1;
+ $address = $2;
+ } elsif ($formatted_email =~ /^\s*<(.+\@\S*)>.*$/) {
+ $address = $1;
+ } elsif ($formatted_email =~ /^(.+\@\S*).*$/) {
+ $address = $1;
+ }
+
+ $name =~ s/^\s+|\s+$//g;
+ $name =~ s/^\"|\"$//g;
+ $address =~ s/^\s+|\s+$//g;
+
+ if ($name =~ /[^\w \-]/i) { ##has "must quote" chars
+ $name =~ s/(?<!\\)"/\\"/g; ##escape quotes
+ $name = "\"$name\"";
+ }
+
+ return ($name, $address);
+}
+
+sub format_email {
+ my ($name, $address, $usename) = @_;
+
+ my $formatted_email;
+
+ $name =~ s/^\s+|\s+$//g;
+ $name =~ s/^\"|\"$//g;
+ $address =~ s/^\s+|\s+$//g;
+
+ if ($name =~ /[^\w \-]/i) { ##has "must quote" chars
+ $name =~ s/(?<!\\)"/\\"/g; ##escape quotes
+ $name = "\"$name\"";
+ }
+
+ if ($usename) {
+ if ("$name" eq "") {
+ $formatted_email = "$address";
+ } else {
+ $formatted_email = "$name <$address>";
+ }
+ } else {
+ $formatted_email = $address;
+ }
+
+ return $formatted_email;
+}
+
+sub find_first_section {
+ my $index = 0;
+
+ while ($index < @typevalue) {
+ my $tv = $typevalue[$index];
+ if (($tv =~ m/^([A-Z]):\s*(.*)/)) {
+ last;
+ }
+ $index++;
+ }
+
+ return $index;
+}
+
+sub find_starting_index {
+ my ($index) = @_;
+
+ while ($index > 0) {
+ my $tv = $typevalue[$index];
+ if (!($tv =~ m/^([A-Z]):\s*(.*)/)) {
+ last;
+ }
+ $index--;
+ }
+
+ return $index;
+}
+
+sub find_ending_index {
+ my ($index) = @_;
+
+ while ($index < @typevalue) {
+ my $tv = $typevalue[$index];
+ if (!($tv =~ m/^([A-Z]):\s*(.*)/)) {
+ last;
+ }
+ $index++;
+ }
+
+ return $index;
+}
+
+sub get_subsystem_name {
+ my ($index) = @_;
+
+ my $start = find_starting_index($index);
+
+ my $subsystem = $typevalue[$start];
+ if ($output_section_maxlen && length($subsystem) > $output_section_maxlen) {
+ $subsystem = substr($subsystem, 0, $output_section_maxlen - 3);
+ $subsystem =~ s/\s*$//;
+ $subsystem = $subsystem . "...";
+ }
+ return $subsystem;
+}
+
+sub get_maintainer_role {
+ my ($index) = @_;
+
+ my $i;
+ my $start = find_starting_index($index);
+ my $end = find_ending_index($index);
+
+ my $role = "unknown";
+ my $subsystem = get_subsystem_name($index);
+
+ for ($i = $start + 1; $i < $end; $i++) {
+ my $tv = $typevalue[$i];
+ if ($tv =~ m/^([A-Z]):\s*(.*)/) {
+ my $ptype = $1;
+ my $pvalue = $2;
+ if ($ptype eq "S") {
+ $role = $pvalue;
+ }
+ }
+ }
+
+ $role = lc($role);
+ if ($role eq "supported") {
+ $role = "supporter";
+ } elsif ($role eq "maintained") {
+ $role = "maintainer";
+ } elsif ($role eq "odd fixes") {
+ $role = "odd fixer";
+ } elsif ($role eq "orphan") {
+ $role = "orphan minder";
+ } elsif ($role eq "obsolete") {
+ $role = "obsolete minder";
+ }
+
+ return $role . ":" . $subsystem;
+}
+
+sub get_list_role {
+ my ($index) = @_;
+
+ my $subsystem = get_subsystem_name($index);
+
+ if ($subsystem eq "ALL") {
+ $subsystem = "Send To Me";
+ }
+
+ return $subsystem;
+}
+
+sub add_categories {
+ my ($index) = @_;
+
+ my $i;
+ my $start = find_starting_index($index);
+ my $end = find_ending_index($index);
+
+ push(@subsystem, $typevalue[$start]);
+
+ for ($i = $start + 1; $i < $end; $i++) {
+ my $tv = $typevalue[$i];
+ if ($tv =~ m/^([A-Z]):\s*(.*)/) {
+ my $ptype = $1;
+ my $pvalue = $2;
+ if ($ptype eq "L") {
+ my $list_address = $pvalue;
+ my $list_additional = "";
+ my $list_role = get_list_role($i);
+
+ if ($list_role ne "") {
+ $list_role = ":" . $list_role;
+ }
+ if ($list_address =~ m/([^\s]+)\s+(.*)$/) {
+ $list_address = $1;
+ $list_additional = $2;
+ }
+ if ($list_additional =~ m/subscribers-only/) {
+ if ($email_subscriber_list) {
+ if (!$hash_list_to{lc($list_address)}) {
+ $hash_list_to{lc($list_address)} = 1;
+ push(@list_to, [$list_address,
+ "subscriber list${list_role}"]);
+ }
+ }
+ } else {
+ if ($email_list) {
+ if (!$hash_list_to{lc($list_address)}) {
+ if ($list_additional =~ m/moderated/) {
+ if ($email_moderated_list) {
+ $hash_list_to{lc($list_address)} = 1;
+ push(@list_to, [$list_address,
+ "moderated list${list_role}"]);
+ }
+ } else {
+ $hash_list_to{lc($list_address)} = 1;
+ push(@list_to, [$list_address,
+ "open list${list_role}"]);
+ }
+ }
+ }
+ }
+ } elsif ($ptype eq "M") {
+ if ($email_maintainer) {
+ my $role = get_maintainer_role($i);
+ push_email_addresses($pvalue, $role);
+ }
+ } elsif ($ptype eq "R") {
+ if ($email_reviewer) {
+ my $subsystem = get_subsystem_name($i);
+ push_email_addresses($pvalue, "reviewer:$subsystem");
+ }
+ } elsif ($ptype eq "T") {
+ push(@scm, $pvalue);
+ } elsif ($ptype eq "W") {
+ push(@web, $pvalue);
+ } elsif ($ptype eq "S") {
+ push(@status, $pvalue);
+ }
+ }
+ }
+}
+
+sub email_inuse {
+ my ($name, $address) = @_;
+
+ return 1 if (($name eq "") && ($address eq ""));
+ return 1 if (($name ne "") && exists($email_hash_name{lc($name)}));
+ return 1 if (($address ne "") && exists($email_hash_address{lc($address)}));
+
+ return 0;
+}
+
+sub push_email_address {
+ my ($line, $role) = @_;
+
+ my ($name, $address) = parse_email($line);
+
+ if ($address eq "") {
+ return 0;
+ }
+
+ if (!$email_remove_duplicates) {
+ push(@email_to, [format_email($name, $address, $email_usename), $role]);
+ } elsif (!email_inuse($name, $address)) {
+ push(@email_to, [format_email($name, $address, $email_usename), $role]);
+ $email_hash_name{lc($name)}++ if ($name ne "");
+ $email_hash_address{lc($address)}++;
+ }
+
+ return 1;
+}
+
+sub push_email_addresses {
+ my ($address, $role) = @_;
+
+ my @address_list = ();
+
+ if (rfc822_valid($address)) {
+ push_email_address($address, $role);
+ } elsif (@address_list = rfc822_validlist($address)) {
+ my $array_count = shift(@address_list);
+ while (my $entry = shift(@address_list)) {
+ push_email_address($entry, $role);
+ }
+ } else {
+ if (!push_email_address($address, $role)) {
+ warn("Invalid MAINTAINERS address: '" . $address . "'\n");
+ }
+ }
+}
+
+sub add_role {
+ my ($line, $role) = @_;
+
+ my ($name, $address) = parse_email($line);
+ my $email = format_email($name, $address, $email_usename);
+
+ foreach my $entry (@email_to) {
+ if ($email_remove_duplicates) {
+ my ($entry_name, $entry_address) = parse_email($entry->[0]);
+ if (($name eq $entry_name || $address eq $entry_address)
+ && ($role eq "" || !($entry->[1] =~ m/$role/))
+ ) {
+ if ($entry->[1] eq "") {
+ $entry->[1] = "$role";
+ } else {
+ $entry->[1] = "$entry->[1],$role";
+ }
+ }
+ } else {
+ if ($email eq $entry->[0]
+ && ($role eq "" || !($entry->[1] =~ m/$role/))
+ ) {
+ if ($entry->[1] eq "") {
+ $entry->[1] = "$role";
+ } else {
+ $entry->[1] = "$entry->[1],$role";
+ }
+ }
+ }
+ }
+}
+
+sub which {
+ my ($bin) = @_;
+
+ foreach my $path (split(/:/, $ENV{PATH})) {
+ if (-e "$path/$bin") {
+ return "$path/$bin";
+ }
+ }
+
+ return "";
+}
+
+sub which_conf {
+ my ($conf) = @_;
+
+ foreach my $path (split(/:/, ".:$ENV{HOME}:.scripts")) {
+ if (-e "$path/$conf") {
+ return "$path/$conf";
+ }
+ }
+
+ return "";
+}
+
+sub mailmap_email {
+ my ($line) = @_;
+
+ my ($name, $address) = parse_email($line);
+ my $email = format_email($name, $address, 1);
+ my $real_name = $name;
+ my $real_address = $address;
+
+ if (exists $mailmap->{names}->{$email} ||
+ exists $mailmap->{addresses}->{$email}) {
+ if (exists $mailmap->{names}->{$email}) {
+ $real_name = $mailmap->{names}->{$email};
+ }
+ if (exists $mailmap->{addresses}->{$email}) {
+ $real_address = $mailmap->{addresses}->{$email};
+ }
+ } else {
+ if (exists $mailmap->{names}->{$address}) {
+ $real_name = $mailmap->{names}->{$address};
+ }
+ if (exists $mailmap->{addresses}->{$address}) {
+ $real_address = $mailmap->{addresses}->{$address};
+ }
+ }
+ return format_email($real_name, $real_address, 1);
+}
+
+sub mailmap {
+ my (@addresses) = @_;
+
+ my @mapped_emails = ();
+ foreach my $line (@addresses) {
+ push(@mapped_emails, mailmap_email($line));
+ }
+ merge_by_realname(@mapped_emails) if ($email_use_mailmap);
+ return @mapped_emails;
+}
+
+sub merge_by_realname {
+ my %address_map;
+ my (@emails) = @_;
+
+ foreach my $email (@emails) {
+ my ($name, $address) = parse_email($email);
+ if (exists $address_map{$name}) {
+ $address = $address_map{$name};
+ $email = format_email($name, $address, 1);
+ } else {
+ $address_map{$name} = $address;
+ }
+ }
+}
+
+sub git_execute_cmd {
+ my ($cmd) = @_;
+ my @lines = ();
+
+ my $output = `$cmd`;
+ $output =~ s/^\s*//gm;
+ @lines = split("\n", $output);
+
+ return @lines;
+}
+
+sub hg_execute_cmd {
+ my ($cmd) = @_;
+ my @lines = ();
+
+ my $output = `$cmd`;
+ @lines = split("\n", $output);
+
+ return @lines;
+}
+
+sub extract_formatted_signatures {
+ my (@signature_lines) = @_;
+
+ my @type = @signature_lines;
+
+ s/\s*(.*):.*/$1/ for (@type);
+
+ # cut -f2- -d":"
+ s/\s*.*:\s*(.+)\s*/$1/ for (@signature_lines);
+
+## Reformat email addresses (with names) to avoid badly written signatures
+
+ foreach my $signer (@signature_lines) {
+ $signer = deduplicate_email($signer);
+ }
+
+ return (\@type, \@signature_lines);
+}
+
+sub vcs_find_signers {
+ my ($cmd, $file) = @_;
+ my $commits;
+ my @lines = ();
+ my @signatures = ();
+ my @authors = ();
+ my @stats = ();
+
+ @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
+
+ my $pattern = $VCS_cmds{"commit_pattern"};
+ my $author_pattern = $VCS_cmds{"author_pattern"};
+ my $stat_pattern = $VCS_cmds{"stat_pattern"};
+
+ $stat_pattern =~ s/(\$\w+)/$1/eeg; #interpolate $stat_pattern
+
+ $commits = grep(/$pattern/, @lines); # of commits
+
+ @authors = grep(/$author_pattern/, @lines);
+ @signatures = grep(/^[ \t]*${signature_pattern}.*\@.*$/, @lines);
+ @stats = grep(/$stat_pattern/, @lines);
+
+# print("stats: <@stats>\n");
+
+ return (0, \@signatures, \@authors, \@stats) if !@signatures;
+
+ save_commits_by_author(@lines) if ($interactive);
+ save_commits_by_signer(@lines) if ($interactive);
+
+ my ($author_ref, $authors_ref) = extract_formatted_signatures(@authors);
+ my ($types_ref, $signers_ref) = extract_formatted_signatures(@signatures);
+
+ return ($commits, $signers_ref, $authors_ref, \@stats);
+}
+
+sub vcs_find_author {
+ my ($cmd) = @_;
+ my @lines = ();
+
+ @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
+
+ return @lines if !@lines;
+
+ my @authors = ();
+ foreach my $line (@lines) {
+ if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
+ my $author = $1;
+ my ($name, $address) = parse_email($author);
+ $author = format_email($name, $address, 1);
+ push(@authors, $author);
+ }
+ }
+
+ save_commits_by_author(@lines) if ($interactive);
+ save_commits_by_signer(@lines) if ($interactive);
+
+ return @authors;
+}
+
+sub vcs_save_commits {
+ my ($cmd) = @_;
+ my @lines = ();
+ my @commits = ();
+
+ @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
+
+ foreach my $line (@lines) {
+ if ($line =~ m/$VCS_cmds{"blame_commit_pattern"}/) {
+ push(@commits, $1);
+ }
+ }
+
+ return @commits;
+}
+
+sub vcs_blame {
+ my ($file) = @_;
+ my $cmd;
+ my @commits = ();
+
+ return @commits if (!(-f $file));
+
+ if (@range && $VCS_cmds{"blame_range_cmd"} eq "") {
+ my @all_commits = ();
+
+ $cmd = $VCS_cmds{"blame_file_cmd"};
+ $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
+ @all_commits = vcs_save_commits($cmd);
+
+ foreach my $file_range_diff (@range) {
+ next if (!($file_range_diff =~ m/(.+):(.+):(.+)/));
+ my $diff_file = $1;
+ my $diff_start = $2;
+ my $diff_length = $3;
+ next if ("$file" ne "$diff_file");
+ for (my $i = $diff_start; $i < $diff_start + $diff_length; $i++) {
+ push(@commits, $all_commits[$i]);
+ }
+ }
+ } elsif (@range) {
+ foreach my $file_range_diff (@range) {
+ next if (!($file_range_diff =~ m/(.+):(.+):(.+)/));
+ my $diff_file = $1;
+ my $diff_start = $2;
+ my $diff_length = $3;
+ next if ("$file" ne "$diff_file");
+ $cmd = $VCS_cmds{"blame_range_cmd"};
+ $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
+ push(@commits, vcs_save_commits($cmd));
+ }
+ } else {
+ $cmd = $VCS_cmds{"blame_file_cmd"};
+ $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
+ @commits = vcs_save_commits($cmd);
+ }
+
+ foreach my $commit (@commits) {
+ $commit =~ s/^\^//g;
+ }
+
+ return @commits;
+}
+
+my $printed_novcs = 0;
+sub vcs_exists {
+ %VCS_cmds = %VCS_cmds_git;
+ return 1 if eval $VCS_cmds{"available"};
+ %VCS_cmds = %VCS_cmds_hg;
+ return 2 if eval $VCS_cmds{"available"};
+ %VCS_cmds = ();
+ if (!$printed_novcs && $email_git) {
+ warn("$P: No supported VCS found. Add --nogit to options?\n");
+ warn("Using a git repository produces better results.\n");
+ warn("Try Linus Torvalds' latest git repository using:\n");
+ warn("git clone git://git.kernel.org/pub/scm/fs/xfs/xfstests-dev.git\n");
+ $printed_novcs = 1;
+ }
+ return 0;
+}
+
+sub vcs_is_git {
+ vcs_exists();
+ return $vcs_used == 1;
+}
+
+sub vcs_is_hg {
+ return $vcs_used == 2;
+}
+
+sub vcs_add_commit_signers {
+ return if (!vcs_exists());
+
+ my ($commit, $desc) = @_;
+ my $commit_count = 0;
+ my $commit_authors_ref;
+ my $commit_signers_ref;
+ my $stats_ref;
+ my @commit_authors = ();
+ my @commit_signers = ();
+ my $cmd;
+
+ $cmd = $VCS_cmds{"find_commit_signers_cmd"};
+ $cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd
+
+ ($commit_count, $commit_signers_ref, $commit_authors_ref, $stats_ref) = vcs_find_signers($cmd, "");
+ @commit_authors = @{$commit_authors_ref} if defined $commit_authors_ref;
+ @commit_signers = @{$commit_signers_ref} if defined $commit_signers_ref;
+
+ foreach my $signer (@commit_signers) {
+ $signer = deduplicate_email($signer);
+ }
+
+ vcs_assign($desc, 1, @commit_signers);
+}
+
+sub interactive_get_maintainers {
+ my ($list_ref) = @_;
+ my @list = @$list_ref;
+
+ vcs_exists();
+
+ my %selected;
+ my %authored;
+ my %signed;
+ my $count = 0;
+ my $maintained = 0;
+ foreach my $entry (@list) {
+ $maintained = 1 if ($entry->[1] =~ /^(maintainer|supporter)/i);
+ $selected{$count} = 1;
+ $authored{$count} = 0;
+ $signed{$count} = 0;
+ $count++;
+ }
+
+ #menu loop
+ my $done = 0;
+ my $print_options = 0;
+ my $redraw = 1;
+ while (!$done) {
+ $count = 0;
+ if ($redraw) {
+ printf STDERR "\n%1s %2s %-65s",
+ "*", "#", "email/list and role:stats";
+ if ($email_git ||
+ ($email_git_fallback && !$maintained) ||
+ $email_git_blame) {
+ print STDERR "auth sign";
+ }
+ print STDERR "\n";
+ foreach my $entry (@list) {
+ my $email = $entry->[0];
+ my $role = $entry->[1];
+ my $sel = "";
+ $sel = "*" if ($selected{$count});
+ my $commit_author = $commit_author_hash{$email};
+ my $commit_signer = $commit_signer_hash{$email};
+ my $authored = 0;
+ my $signed = 0;
+ $authored++ for (@{$commit_author});
+ $signed++ for (@{$commit_signer});
+ printf STDERR "%1s %2d %-65s", $sel, $count + 1, $email;
+ printf STDERR "%4d %4d", $authored, $signed
+ if ($authored > 0 || $signed > 0);
+ printf STDERR "\n %s\n", $role;
+ if ($authored{$count}) {
+ my $commit_author = $commit_author_hash{$email};
+ foreach my $ref (@{$commit_author}) {
+ print STDERR " Author: @{$ref}[1]\n";
+ }
+ }
+ if ($signed{$count}) {
+ my $commit_signer = $commit_signer_hash{$email};
+ foreach my $ref (@{$commit_signer}) {
+ print STDERR " @{$ref}[2]: @{$ref}[1]\n";
+ }
+ }
+
+ $count++;
+ }
+ }
+ my $date_ref = \$email_git_since;
+ $date_ref = \$email_hg_since if (vcs_is_hg());
+ if ($print_options) {
+ $print_options = 0;
+ if (vcs_exists()) {
+ print STDERR <<EOT
+
+Version Control options:
+g use git history [$email_git]
+gf use git-fallback [$email_git_fallback]
+b use git blame [$email_git_blame]
+bs use blame signatures [$email_git_blame_signatures]
+c# minimum commits [$email_git_min_signatures]
+%# min percent [$email_git_min_percent]
+d# history to use [$$date_ref]
+x# max maintainers [$email_git_max_maintainers]
+t all signature types [$email_git_all_signature_types]
+m use .mailmap [$email_use_mailmap]
+EOT
+ }
+ print STDERR <<EOT
+
+Additional options:
+0 toggle all
+tm toggle maintainers
+tg toggle git entries
+tl toggle open list entries
+ts toggle subscriber list entries
+f emails in file [$email_file_emails]
+k keywords in file [$keywords]
+r remove duplicates [$email_remove_duplicates]
+p# pattern match depth [$pattern_depth]
+EOT
+ }
+ print STDERR
+"\n#(toggle), A#(author), S#(signed) *(all), ^(none), O(options), Y(approve): ";
+
+ my $input = <STDIN>;
+ chomp($input);
+
+ $redraw = 1;
+ my $rerun = 0;
+ my @wish = split(/[, ]+/, $input);
+ foreach my $nr (@wish) {
+ $nr = lc($nr);
+ my $sel = substr($nr, 0, 1);
+ my $str = substr($nr, 1);
+ my $val = 0;
+ $val = $1 if $str =~ /^(\d+)$/;
+
+ if ($sel eq "y") {
+ $interactive = 0;
+ $done = 1;
+ $output_rolestats = 0;
+ $output_roles = 0;
+ last;
+ } elsif ($nr =~ /^\d+$/ && $nr > 0 && $nr <= $count) {
+ $selected{$nr - 1} = !$selected{$nr - 1};
+ } elsif ($sel eq "*" || $sel eq '^') {
+ my $toggle = 0;
+ $toggle = 1 if ($sel eq '*');
+ for (my $i = 0; $i < $count; $i++) {
+ $selected{$i} = $toggle;
+ }
+ } elsif ($sel eq "0") {
+ for (my $i = 0; $i < $count; $i++) {
+ $selected{$i} = !$selected{$i};
+ }
+ } elsif ($sel eq "t") {
+ if (lc($str) eq "m") {
+ for (my $i = 0; $i < $count; $i++) {
+ $selected{$i} = !$selected{$i}
+ if ($list[$i]->[1] =~ /^(maintainer|supporter)/i);
+ }
+ } elsif (lc($str) eq "g") {
+ for (my $i = 0; $i < $count; $i++) {
+ $selected{$i} = !$selected{$i}
+ if ($list[$i]->[1] =~ /^(author|commit|signer)/i);
+ }
+ } elsif (lc($str) eq "l") {
+ for (my $i = 0; $i < $count; $i++) {
+ $selected{$i} = !$selected{$i}
+ if ($list[$i]->[1] =~ /^(open list)/i);
+ }
+ } elsif (lc($str) eq "s") {
+ for (my $i = 0; $i < $count; $i++) {
+ $selected{$i} = !$selected{$i}
+ if ($list[$i]->[1] =~ /^(subscriber list)/i);
+ }
+ }
+ } elsif ($sel eq "a") {
+ if ($val > 0 && $val <= $count) {
+ $authored{$val - 1} = !$authored{$val - 1};
+ } elsif ($str eq '*' || $str eq '^') {
+ my $toggle = 0;
+ $toggle = 1 if ($str eq '*');
+ for (my $i = 0; $i < $count; $i++) {
+ $authored{$i} = $toggle;
+ }
+ }
+ } elsif ($sel eq "s") {
+ if ($val > 0 && $val <= $count) {
+ $signed{$val - 1} = !$signed{$val - 1};
+ } elsif ($str eq '*' || $str eq '^') {
+ my $toggle = 0;
+ $toggle = 1 if ($str eq '*');
+ for (my $i = 0; $i < $count; $i++) {
+ $signed{$i} = $toggle;
+ }
+ }
+ } elsif ($sel eq "o") {
+ $print_options = 1;
+ $redraw = 1;
+ } elsif ($sel eq "g") {
+ if ($str eq "f") {
+ bool_invert(\$email_git_fallback);
+ } else {
+ bool_invert(\$email_git);
+ }
+ $rerun = 1;
+ } elsif ($sel eq "b") {
+ if ($str eq "s") {
+ bool_invert(\$email_git_blame_signatures);
+ } else {
+ bool_invert(\$email_git_blame);
+ }
+ $rerun = 1;
+ } elsif ($sel eq "c") {
+ if ($val > 0) {
+ $email_git_min_signatures = $val;
+ $rerun = 1;
+ }
+ } elsif ($sel eq "x") {
+ if ($val > 0) {
+ $email_git_max_maintainers = $val;
+ $rerun = 1;
+ }
+ } elsif ($sel eq "%") {
+ if ($str ne "" && $val >= 0) {
+ $email_git_min_percent = $val;
+ $rerun = 1;
+ }
+ } elsif ($sel eq "d") {
+ if (vcs_is_git()) {
+ $email_git_since = $str;
+ } elsif (vcs_is_hg()) {
+ $email_hg_since = $str;
+ }
+ $rerun = 1;
+ } elsif ($sel eq "t") {
+ bool_invert(\$email_git_all_signature_types);
+ $rerun = 1;
+ } elsif ($sel eq "f") {
+ bool_invert(\$email_file_emails);
+ $rerun = 1;
+ } elsif ($sel eq "r") {
+ bool_invert(\$email_remove_duplicates);
+ $rerun = 1;
+ } elsif ($sel eq "m") {
+ bool_invert(\$email_use_mailmap);
+ read_mailmap();
+ $rerun = 1;
+ } elsif ($sel eq "k") {
+ bool_invert(\$keywords);
+ $rerun = 1;
+ } elsif ($sel eq "p") {
+ if ($str ne "" && $val >= 0) {
+ $pattern_depth = $val;
+ $rerun = 1;
+ }
+ } elsif ($sel eq "h" || $sel eq "?") {
+ print STDERR <<EOT
+
+Interactive mode allows you to select the various maintainers, submitters,
+commit signers and mailing lists that could be CC'd on a patch.
+
+Any *'d entry is selected.
+
+If you have git or hg installed, you can choose to summarize the commit
+history of files in the patch. Also, each line of the current file can
+be matched to its commit author and that commits signers with blame.
+
+Various knobs exist to control the length of time for active commit
+tracking, the maximum number of commit authors and signers to add,
+and such.
+
+Enter selections at the prompt until you are satisfied that the selected
+maintainers are appropriate. You may enter multiple selections separated
+by either commas or spaces.
+
+EOT
+ } else {
+ print STDERR "invalid option: '$nr'\n";
+ $redraw = 0;
+ }
+ }
+ if ($rerun) {
+ print STDERR "git-blame can be very slow, please have patience..."
+ if ($email_git_blame);
+ goto &get_maintainers;
+ }
+ }
+
+ #drop not selected entries
+ $count = 0;
+ my @new_emailto = ();
+ foreach my $entry (@list) {
+ if ($selected{$count}) {
+ push(@new_emailto, $list[$count]);
+ }
+ $count++;
+ }
+ return @new_emailto;
+}
+
+sub bool_invert {
+ my ($bool_ref) = @_;
+
+ if ($$bool_ref) {
+ $$bool_ref = 0;
+ } else {
+ $$bool_ref = 1;
+ }
+}
+
+sub deduplicate_email {
+ my ($email) = @_;
+
+ my $matched = 0;
+ my ($name, $address) = parse_email($email);
+ $email = format_email($name, $address, 1);
+ $email = mailmap_email($email);
+
+ return $email if (!$email_remove_duplicates);
+
+ ($name, $address) = parse_email($email);
+
+ if ($name ne "" && $deduplicate_name_hash{lc($name)}) {
+ $name = $deduplicate_name_hash{lc($name)}->[0];
+ $address = $deduplicate_name_hash{lc($name)}->[1];
+ $matched = 1;
+ } elsif ($deduplicate_address_hash{lc($address)}) {
+ $name = $deduplicate_address_hash{lc($address)}->[0];
+ $address = $deduplicate_address_hash{lc($address)}->[1];
+ $matched = 1;
+ }
+ if (!$matched) {
+ $deduplicate_name_hash{lc($name)} = [ $name, $address ];
+ $deduplicate_address_hash{lc($address)} = [ $name, $address ];
+ }
+ $email = format_email($name, $address, 1);
+ $email = mailmap_email($email);
+ return $email;
+}
+
+sub save_commits_by_author {
+ my (@lines) = @_;
+
+ my @authors = ();
+ my @commits = ();
+ my @subjects = ();
+
+ foreach my $line (@lines) {
+ if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
+ my $author = $1;
+ $author = deduplicate_email($author);
+ push(@authors, $author);
+ }
+ push(@commits, $1) if ($line =~ m/$VCS_cmds{"commit_pattern"}/);
+ push(@subjects, $1) if ($line =~ m/$VCS_cmds{"subject_pattern"}/);
+ }
+
+ for (my $i = 0; $i < @authors; $i++) {
+ my $exists = 0;
+ foreach my $ref(@{$commit_author_hash{$authors[$i]}}) {
+ if (@{$ref}[0] eq $commits[$i] &&
+ @{$ref}[1] eq $subjects[$i]) {
+ $exists = 1;
+ last;
+ }
+ }
+ if (!$exists) {
+ push(@{$commit_author_hash{$authors[$i]}},
+ [ ($commits[$i], $subjects[$i]) ]);
+ }
+ }
+}
+
+sub save_commits_by_signer {
+ my (@lines) = @_;
+
+ my $commit = "";
+ my $subject = "";
+
+ foreach my $line (@lines) {
+ $commit = $1 if ($line =~ m/$VCS_cmds{"commit_pattern"}/);
+ $subject = $1 if ($line =~ m/$VCS_cmds{"subject_pattern"}/);
+ if ($line =~ /^[ \t]*${signature_pattern}.*\@.*$/) {
+ my @signatures = ($line);
+ my ($types_ref, $signers_ref) = extract_formatted_signatures(@signatures);
+ my @types = @$types_ref;
+ my @signers = @$signers_ref;
+
+ my $type = $types[0];
+ my $signer = $signers[0];
+
+ $signer = deduplicate_email($signer);
+
+ my $exists = 0;
+ foreach my $ref(@{$commit_signer_hash{$signer}}) {
+ if (@{$ref}[0] eq $commit &&
+ @{$ref}[1] eq $subject &&
+ @{$ref}[2] eq $type) {
+ $exists = 1;
+ last;
+ }
+ }
+ if (!$exists) {
+ push(@{$commit_signer_hash{$signer}},
+ [ ($commit, $subject, $type) ]);
+ }
+ }
+ }
+}
+
+sub vcs_assign {
+ my ($role, $divisor, @lines) = @_;
+
+ my %hash;
+ my $count = 0;
+
+ return if (@lines <= 0);
+
+ if ($divisor <= 0) {
+ warn("Bad divisor in " . (caller(0))[3] . ": $divisor\n");
+ $divisor = 1;
+ }
+
+ @lines = mailmap(@lines);
+
+ return if (@lines <= 0);
+
+ @lines = sort(@lines);
+
+ # uniq -c
+ $hash{$_}++ for @lines;
+
+ # sort -rn
+ foreach my $line (sort {$hash{$b} <=> $hash{$a}} keys %hash) {
+ my $sign_offs = $hash{$line};
+ my $percent = $sign_offs * 100 / $divisor;
+
+ $percent = 100 if ($percent > 100);
+ next if (ignore_email_address($line));
+ $count++;
+ last if ($sign_offs < $email_git_min_signatures ||
+ $count > $email_git_max_maintainers ||
+ $percent < $email_git_min_percent);
+ push_email_address($line, '');
+ if ($output_rolestats) {
+ my $fmt_percent = sprintf("%.0f", $percent);
+ add_role($line, "$role:$sign_offs/$divisor=$fmt_percent%");
+ } else {
+ add_role($line, $role);
+ }
+ }
+}
+
+sub vcs_file_signoffs {
+ my ($file) = @_;
+
+ my $authors_ref;
+ my $signers_ref;
+ my $stats_ref;
+ my @authors = ();
+ my @signers = ();
+ my @stats = ();
+ my $commits;
+
+ $vcs_used = vcs_exists();
+ return if (!$vcs_used);
+
+ my $cmd = $VCS_cmds{"find_signers_cmd"};
+ $cmd =~ s/(\$\w+)/$1/eeg; # interpolate $cmd
+
+ ($commits, $signers_ref, $authors_ref, $stats_ref) = vcs_find_signers($cmd, $file);
+
+ @signers = @{$signers_ref} if defined $signers_ref;
+ @authors = @{$authors_ref} if defined $authors_ref;
+ @stats = @{$stats_ref} if defined $stats_ref;
+
+# print("commits: <$commits>\nsigners:<@signers>\nauthors: <@authors>\nstats: <@stats>\n");
+
+ foreach my $signer (@signers) {
+ $signer = deduplicate_email($signer);
+ }
+
+ vcs_assign("commit_signer", $commits, @signers);
+ vcs_assign("authored", $commits, @authors);
+ if ($#authors == $#stats) {
+ my $stat_pattern = $VCS_cmds{"stat_pattern"};
+ $stat_pattern =~ s/(\$\w+)/$1/eeg; #interpolate $stat_pattern
+
+ my $added = 0;
+ my $deleted = 0;
+ for (my $i = 0; $i <= $#stats; $i++) {
+ if ($stats[$i] =~ /$stat_pattern/) {
+ $added += $1;
+ $deleted += $2;
+ }
+ }
+ my @tmp_authors = uniq(@authors);
+ foreach my $author (@tmp_authors) {
+ $author = deduplicate_email($author);
+ }
+ @tmp_authors = uniq(@tmp_authors);
+ my @list_added = ();
+ my @list_deleted = ();
+ foreach my $author (@tmp_authors) {
+ my $auth_added = 0;
+ my $auth_deleted = 0;
+ for (my $i = 0; $i <= $#stats; $i++) {
+ if ($author eq deduplicate_email($authors[$i]) &&
+ $stats[$i] =~ /$stat_pattern/) {
+ $auth_added += $1;
+ $auth_deleted += $2;
+ }
+ }
+ for (my $i = 0; $i < $auth_added; $i++) {
+ push(@list_added, $author);
+ }
+ for (my $i = 0; $i < $auth_deleted; $i++) {
+ push(@list_deleted, $author);
+ }
+ }
+ vcs_assign("added_lines", $added, @list_added);
+ vcs_assign("removed_lines", $deleted, @list_deleted);
+ }
+}
+
+sub vcs_file_blame {
+ my ($file) = @_;
+
+ my @signers = ();
+ my @all_commits = ();
+ my @commits = ();
+ my $total_commits;
+ my $total_lines;
+
+ $vcs_used = vcs_exists();
+ return if (!$vcs_used);
+
+ @all_commits = vcs_blame($file);
+ @commits = uniq(@all_commits);
+ $total_commits = @commits;
+ $total_lines = @all_commits;
+
+ if ($email_git_blame_signatures) {
+ if (vcs_is_hg()) {
+ my $commit_count;
+ my $commit_authors_ref;
+ my $commit_signers_ref;
+ my $stats_ref;
+ my @commit_authors = ();
+ my @commit_signers = ();
+ my $commit = join(" -r ", @commits);
+ my $cmd;
+
+ $cmd = $VCS_cmds{"find_commit_signers_cmd"};
+ $cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd
+
+ ($commit_count, $commit_signers_ref, $commit_authors_ref, $stats_ref) = vcs_find_signers($cmd, $file);
+ @commit_authors = @{$commit_authors_ref} if defined $commit_authors_ref;
+ @commit_signers = @{$commit_signers_ref} if defined $commit_signers_ref;
+
+ push(@signers, @commit_signers);
+ } else {
+ foreach my $commit (@commits) {
+ my $commit_count;
+ my $commit_authors_ref;
+ my $commit_signers_ref;
+ my $stats_ref;
+ my @commit_authors = ();
+ my @commit_signers = ();
+ my $cmd;
+
+ $cmd = $VCS_cmds{"find_commit_signers_cmd"};
+ $cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd
+
+ ($commit_count, $commit_signers_ref, $commit_authors_ref, $stats_ref) = vcs_find_signers($cmd, $file);
+ @commit_authors = @{$commit_authors_ref} if defined $commit_authors_ref;
+ @commit_signers = @{$commit_signers_ref} if defined $commit_signers_ref;
+
+ push(@signers, @commit_signers);
+ }
+ }
+ }
+
+ if ($from_filename) {
+ if ($output_rolestats) {
+ my @blame_signers;
+ if (vcs_is_hg()) {{ # Double brace for last exit
+ my $commit_count;
+ my @commit_signers = ();
+ @commits = uniq(@commits);
+ @commits = sort(@commits);
+ my $commit = join(" -r ", @commits);
+ my $cmd;
+
+ $cmd = $VCS_cmds{"find_commit_author_cmd"};
+ $cmd =~ s/(\$\w+)/$1/eeg; #substitute variables in $cmd
+
+ my @lines = ();
+
+ @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
+
+ last if !@lines;
+
+ my @authors = ();
+ foreach my $line (@lines) {
+ if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
+ my $author = $1;
+ $author = deduplicate_email($author);
+ push(@authors, $author);
+ }
+ }
+
+ save_commits_by_author(@lines) if ($interactive);
+ save_commits_by_signer(@lines) if ($interactive);
+
+ push(@signers, @authors);
+ }}
+ else {
+ foreach my $commit (@commits) {
+ my $i;
+ my $cmd = $VCS_cmds{"find_commit_author_cmd"};
+ $cmd =~ s/(\$\w+)/$1/eeg; #interpolate $cmd
+ my @author = vcs_find_author($cmd);
+ next if !@author;
+
+ my $formatted_author = deduplicate_email($author[0]);
+
+ my $count = grep(/$commit/, @all_commits);
+ for ($i = 0; $i < $count ; $i++) {
+ push(@blame_signers, $formatted_author);
+ }
+ }
+ }
+ if (@blame_signers) {
+ vcs_assign("authored lines", $total_lines, @blame_signers);
+ }
+ }
+ foreach my $signer (@signers) {
+ $signer = deduplicate_email($signer);
+ }
+ vcs_assign("commits", $total_commits, @signers);
+ } else {
+ foreach my $signer (@signers) {
+ $signer = deduplicate_email($signer);
+ }
+ vcs_assign("modified commits", $total_commits, @signers);
+ }
+}
+
+sub vcs_file_exists {
+ my ($file) = @_;
+
+ my $exists;
+
+ my $vcs_used = vcs_exists();
+ return 0 if (!$vcs_used);
+
+ my $cmd = $VCS_cmds{"file_exists_cmd"};
+ $cmd =~ s/(\$\w+)/$1/eeg; # interpolate $cmd
+ $cmd .= " 2>&1";
+ $exists = &{$VCS_cmds{"execute_cmd"}}($cmd);
+
+ return 0 if ($? != 0);
+
+ return $exists;
+}
+
+sub vcs_list_files {
+ my ($file) = @_;
+
+ my @lsfiles = ();
+
+ my $vcs_used = vcs_exists();
+ return 0 if (!$vcs_used);
+
+ my $cmd = $VCS_cmds{"list_files_cmd"};
+ $cmd =~ s/(\$\w+)/$1/eeg; # interpolate $cmd
+ @lsfiles = &{$VCS_cmds{"execute_cmd"}}($cmd);
+
+ return () if ($? != 0);
+
+ return @lsfiles;
+}
+
+sub uniq {
+ my (@parms) = @_;
+
+ my %saw;
+ @parms = grep(!$saw{$_}++, @parms);
+ return @parms;
+}
+
+sub sort_and_uniq {
+ my (@parms) = @_;
+
+ my %saw;
+ @parms = sort @parms;
+ @parms = grep(!$saw{$_}++, @parms);
+ return @parms;
+}
+
+sub clean_file_emails {
+ my (@file_emails) = @_;
+ my @fmt_emails = ();
+
+ foreach my $email (@file_emails) {
+ $email =~ s/[\(\<\{]{0,1}([A-Za-z0-9_\.\+-]+\@[A-Za-z0-9\.-]+)[\)\>\}]{0,1}/\<$1\>/g;
+ my ($name, $address) = parse_email($email);
+ if ($name eq '"[,\.]"') {
+ $name = "";
+ }
+
+ my @nw = split(/[^A-Za-zÀ-ÿ\'\,\.\+-]/, $name);
+ if (@nw > 2) {
+ my $first = $nw[@nw - 3];
+ my $middle = $nw[@nw - 2];
+ my $last = $nw[@nw - 1];
+
+ if (((length($first) == 1 && $first =~ m/[A-Za-z]/) ||
+ (length($first) == 2 && substr($first, -1) eq ".")) ||
+ (length($middle) == 1 ||
+ (length($middle) == 2 && substr($middle, -1) eq "."))) {
+ $name = "$first $middle $last";
+ } else {
+ $name = "$middle $last";
+ }
+ }
+
+ if (substr($name, -1) =~ /[,\.]/) {
+ $name = substr($name, 0, length($name) - 1);
+ } elsif (substr($name, -2) =~ /[,\.]"/) {
+ $name = substr($name, 0, length($name) - 2) . '"';
+ }
+
+ if (substr($name, 0, 1) =~ /[,\.]/) {
+ $name = substr($name, 1, length($name) - 1);
+ } elsif (substr($name, 0, 2) =~ /"[,\.]/) {
+ $name = '"' . substr($name, 2, length($name) - 2);
+ }
+
+ my $fmt_email = format_email($name, $address, $email_usename);
+ push(@fmt_emails, $fmt_email);
+ }
+ return @fmt_emails;
+}
+
+sub merge_email {
+ my @lines;
+ my %saw;
+
+ for (@_) {
+ my ($address, $role) = @$_;
+ if (!$saw{$address}) {
+ if ($output_roles) {
+ push(@lines, "$address ($role)");
+ } else {
+ push(@lines, $address);
+ }
+ $saw{$address} = 1;
+ }
+ }
+
+ return @lines;
+}
+
+sub output {
+ my (@parms) = @_;
+
+ if ($output_multiline) {
+ foreach my $line (@parms) {
+ print("${line}\n");
+ }
+ } else {
+ print(join($output_separator, @parms));
+ print("\n");
+ }
+}
+
+my $rfc822re;
+
+sub make_rfc822re {
+# Basic lexical tokens are specials, domain_literal, quoted_string, atom, and
+# comment. We must allow for rfc822_lwsp (or comments) after each of these.
+# This regexp will only work on addresses which have had comments stripped
+# and replaced with rfc822_lwsp.
+
+ my $specials = '()<>@,;:\\\\".\\[\\]';
+ my $controls = '\\000-\\037\\177';
+
+ my $dtext = "[^\\[\\]\\r\\\\]";
+ my $domain_literal = "\\[(?:$dtext|\\\\.)*\\]$rfc822_lwsp*";
+
+ my $quoted_string = "\"(?:[^\\\"\\r\\\\]|\\\\.|$rfc822_lwsp)*\"$rfc822_lwsp*";
+
+# Use zero-width assertion to spot the limit of an atom. A simple
+# $rfc822_lwsp* causes the regexp engine to hang occasionally.
+ my $atom = "[^$specials $controls]+(?:$rfc822_lwsp+|\\Z|(?=[\\[\"$specials]))";
+ my $word = "(?:$atom|$quoted_string)";
+ my $localpart = "$word(?:\\.$rfc822_lwsp*$word)*";
+
+ my $sub_domain = "(?:$atom|$domain_literal)";
+ my $domain = "$sub_domain(?:\\.$rfc822_lwsp*$sub_domain)*";
+
+ my $addr_spec = "$localpart\@$rfc822_lwsp*$domain";
+
+ my $phrase = "$word*";
+ my $route = "(?:\@$domain(?:,\@$rfc822_lwsp*$domain)*:$rfc822_lwsp*)";
+ my $route_addr = "\\<$rfc822_lwsp*$route?$addr_spec\\>$rfc822_lwsp*";
+ my $mailbox = "(?:$addr_spec|$phrase$route_addr)";
+
+ my $group = "$phrase:$rfc822_lwsp*(?:$mailbox(?:,\\s*$mailbox)*)?;\\s*";
+ my $address = "(?:$mailbox|$group)";
+
+ return "$rfc822_lwsp*$address";
+}
+
+sub rfc822_strip_comments {
+ my $s = shift;
+# Recursively remove comments, and replace with a single space. The simpler
+# regexps in the Email Addressing FAQ are imperfect - they will miss escaped
+# chars in atoms, for example.
+
+ while ($s =~ s/^((?:[^"\\]|\\.)*
+ (?:"(?:[^"\\]|\\.)*"(?:[^"\\]|\\.)*)*)
+ \((?:[^()\\]|\\.)*\)/$1 /osx) {}
+ return $s;
+}
+
+# valid: returns true if the parameter is an RFC822 valid address
+#
+sub rfc822_valid {
+ my $s = rfc822_strip_comments(shift);
+
+ if (!$rfc822re) {
+ $rfc822re = make_rfc822re();
+ }
+
+ return $s =~ m/^$rfc822re$/so && $s =~ m/^$rfc822_char*$/;
+}
+
+# validlist: In scalar context, returns true if the parameter is an RFC822
+# valid list of addresses.
+#
+# In list context, returns an empty list on failure (an invalid
+# address was found); otherwise a list whose first element is the
+# number of addresses found and whose remaining elements are the
+# addresses. This is needed to disambiguate failure (invalid)
+# from success with no addresses found, because an empty string is
+# a valid list.
+
+sub rfc822_validlist {
+ my $s = rfc822_strip_comments(shift);
+
+ if (!$rfc822re) {
+ $rfc822re = make_rfc822re();
+ }
+ # * null list items are valid according to the RFC
+ # * the '1' business is to aid in distinguishing failure from no results
+
+ my @r;
+ if ($s =~ m/^(?:$rfc822re)?(?:,(?:$rfc822re)?)*$/so &&
+ $s =~ m/^$rfc822_char*$/) {
+ while ($s =~ m/(?:^|,$rfc822_lwsp*)($rfc822re)/gos) {
+ push(@r, $1);
+ }
+ return wantarray ? (scalar(@r), @r) : 1;
+ }
+ return wantarray ? () : 0;
+}
# Aggregate the groups each test belongs to for the group file
grep -I -R "^_begin_fstest" $test_dir/ | \
- sed -e "s/^.*\/\($VALID_TEST_NAME\):_begin_fstest/\1/" \
- >> $new_groups
+ sed -e "s/^.*\/\($VALID_TEST_NAME\):_begin_fstest/\1/" | \
+ sort -ug >> $new_groups
# Create the list of unique groups for existence checking
grep -I -R "^_begin_fstest" $test_dir/ | \
git mv "tests/${src}" "tests/${dest}"
git mv "tests/${src}.out" "tests/${dest}.out"
+# make sure testcase is executable
+chmod a+x "tests/${dest}"
sed -e "s/^# FS[[:space:]]*QA.*Test.*[0-9]\+$/# FS QA Test No. ${did}/g" -i "tests/${dest}"
sed -e "s/^QA output created by ${sid}$/QA output created by ${did}/g" -i "tests/${dest}.out"
sed -e "s/test-${sid}/test-${did}/g" -i "tests/${dest}.out"