new file mode 100755
@@ -0,0 +1,122 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023-2024 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 1866
+#
+# Functional testing for online fsck of a directory loop that is not accessible
+# from the root directory.
+#
+. ./common/preamble
+_begin_fstest auto online_repair
+
+# Import common functions.
+. ./common/filter
+. ./common/inject
+. ./common/fuzzy
+. ./common/populate
+
+# real QA test starts here
+
+# Modify as appropriate.
+_supported_fs xfs
+_require_xfs_db_command "link"
+_require_xfs_db_command "unlink"
+_require_scratch
+_require_xfs_stress_online_repair
+
+prepare_fs() {
+ _scratch_mkfs >> $seqres.full
+ _scratch_mount
+ __stress_scrub_check_commands "%dir%" '' '' 'scrub dirtree'
+
+ # Begin by creating the following directory tree:
+ # root["A"]->A
+ # A["B"]->B
+ # B["C"]->C
+ mkdir -p "$SCRATCH_MNT/A/B/C"
+
+ root_inum="$(stat -c '%i' "$SCRATCH_MNT/")"
+ a_inum="$(stat -c '%i' "$SCRATCH_MNT/A")"
+ b_inum="$(stat -c '%i' "$SCRATCH_MNT/A/B")"
+ c_inum="$(stat -c '%i' "$SCRATCH_MNT/A/B/C")"
+
+ echo "root: $root_inum; a: $a_inum; b: $b_inum; c: $c_inum" >> $seqres.full
+
+ # Next, we complete the loop by creating C["A"]->A and deleting root["A"]->A.
+ # Directory tree is now:
+ # A["B"]->B
+ # B["C"]->C
+ # C["A"]->A
+ _scratch_unmount
+
+ root_gen=$(_scratch_xfs_get_metadata_field core.gen "inode $root_inum")
+ a_gen=$(_scratch_xfs_get_metadata_field core.gen "inode $a_inum")
+ b_gen=$(_scratch_xfs_get_metadata_field core.gen "inode $b_inum")
+ c_gen=$(_scratch_xfs_get_metadata_field core.gen "inode $c_inum")
+
+ _scratch_xfs_db \
+ -c "echo before root $root_inum" -c "inode $root_inum" -c 'print core.nlinkv2' -c "ls" \
+ -c "echo before C $c_inum" -c "inode $c_inum" -c 'print core.nlinkv2' -c "ls" \
+ -c "echo before A $a_inum" -c "inode $a_inum" -c 'print core.nlinkv2' -c "parent" \
+ >> $seqres.full
+
+ _scratch_xfs_db -x \
+ -c "inode $c_inum" -c "link -i $a_inum A" \
+ -c "inode $root_inum" -c "unlink A" \
+ >> $seqres.full
+
+ _scratch_xfs_db \
+ -c "echo after root $root_inum" -c "inode $root_inum" -c 'print core.nlinkv2' -c "ls" \
+ -c "echo after C $c_inum" -c "inode $c_inum" -c 'print core.nlinkv2' -c "ls" \
+ -c "echo after A $a_inum" -c "inode $a_inum" -c 'print core.nlinkv2' -c "parent" \
+ >> $seqres.full
+}
+
+simple_online_repair() {
+ echo "check root"
+ $XFS_IO_PROG -c "scrub dirtree $root_inum $root_gen" $SCRATCH_MNT
+ echo "check A"
+ $XFS_IO_PROG -c "scrub dirtree $a_inum $a_gen" $SCRATCH_MNT
+ echo "check B"
+ $XFS_IO_PROG -c "scrub dirtree $b_inum $b_gen" $SCRATCH_MNT
+ echo "check C"
+ $XFS_IO_PROG -c "scrub dirtree $c_inum $c_gen" $SCRATCH_MNT
+
+ echo "repair root"
+ $XFS_IO_PROG -x -c "repair dirtree $root_inum $root_gen" $SCRATCH_MNT
+ echo "repair A"
+ $XFS_IO_PROG -x -c "repair dirtree $a_inum $a_gen" $SCRATCH_MNT
+ echo "repair B"
+ $XFS_IO_PROG -x -c "repair dirtree $b_inum $b_gen" $SCRATCH_MNT
+ echo "repair C"
+ $XFS_IO_PROG -x -c "repair dirtree $c_inum $c_gen" $SCRATCH_MNT
+
+ echo "check root"
+ $XFS_IO_PROG -c "scrub dirtree $root_inum $root_gen" $SCRATCH_MNT
+ echo "check A"
+ $XFS_IO_PROG -c "scrub dirtree $a_inum $a_gen" $SCRATCH_MNT
+ echo "check B"
+ $XFS_IO_PROG -c "scrub dirtree $b_inum $b_gen" $SCRATCH_MNT
+ echo "check C"
+ $XFS_IO_PROG -c "scrub dirtree $c_inum $c_gen" $SCRATCH_MNT
+}
+
+# Part 1: Use raw ioctls to detect the loop and fix it.
+prepare_fs
+_scratch_mount
+simple_online_repair
+_check_scratch_fs
+_scratch_unmount
+
+# Part 2: Use xfs_scrub to detect the loop and fix it.
+prepare_fs
+_scratch_mount
+_scratch_scrub &>> $seqres.full
+echo "xfs_scrub returned $?" >> $seqres.full
+_check_scratch_fs
+_scratch_unmount
+
+# success, all done
+status=0
+exit
new file mode 100644
@@ -0,0 +1,19 @@
+QA output created by 1866
+check root
+check A
+Corruption detected.
+check B
+Corruption detected.
+check C
+Corruption detected.
+repair root
+Metadata did not need repair or optimization.
+repair A
+repair B
+Metadata did not need repair or optimization.
+repair C
+Metadata did not need repair or optimization.
+check root
+check A
+check B
+check C
new file mode 100755
@@ -0,0 +1,133 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023-2024 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 1867
+#
+# Functional testing for online fsck of a directory loop that is accessible
+# from the root directory.
+#
+. ./common/preamble
+_begin_fstest auto online_repair
+
+# Import common functions.
+. ./common/filter
+. ./common/inject
+. ./common/fuzzy
+. ./common/populate
+
+# real QA test starts here
+
+# Modify as appropriate.
+_supported_fs xfs
+_require_xfs_db_command "link"
+_require_xfs_db_command "unlink"
+_require_scratch
+_require_xfs_stress_online_repair
+
+prepare_fs() {
+ _scratch_mkfs >> $seqres.full
+ _scratch_mount
+ __stress_scrub_check_commands "%dir%" '' '' 'scrub dirtree'
+
+ # Begin by creating the following directory tree:
+ # root["A"]->A
+ # A["B"]->B
+ # B["C"]->C
+ # C["D"]->D
+ mkdir -p "$SCRATCH_MNT/A/B/C/D"
+
+ root_inum="$(stat -c '%i' "$SCRATCH_MNT/")"
+ a_inum="$(stat -c '%i' "$SCRATCH_MNT/A")"
+ b_inum="$(stat -c '%i' "$SCRATCH_MNT/A/B")"
+ c_inum="$(stat -c '%i' "$SCRATCH_MNT/A/B/C")"
+ d_inum="$(stat -c '%i' "$SCRATCH_MNT/A/B/C/D")"
+
+ echo "root: $root_inum; a: $a_inum; b: $b_inum; c: $c_inum; d: $d_inum" >> $seqres.full
+
+ # Next, we complete the loop by creating D["B1"]->B. Directory tree is now:
+ # root["A"]->A
+ # A["B"]->B
+ # B["C"]->C
+ # C["D"]->D
+ # D["B1"]->B
+ _scratch_unmount
+
+ root_gen=$(_scratch_xfs_get_metadata_field core.gen "inode $root_inum")
+ a_gen=$(_scratch_xfs_get_metadata_field core.gen "inode $a_inum")
+ b_gen=$(_scratch_xfs_get_metadata_field core.gen "inode $b_inum")
+ c_gen=$(_scratch_xfs_get_metadata_field core.gen "inode $c_inum")
+ d_gen=$(_scratch_xfs_get_metadata_field core.gen "inode $d_inum")
+
+ _scratch_xfs_db \
+ -c "echo before root $root_inum" -c "inode $root_inum" -c 'print core.nlinkv2' -c "ls" \
+ -c "echo before D $d_inum" -c "inode $d_inum" -c 'print core.nlinkv2' -c "ls" \
+ -c "echo before B $b_inum" -c "inode $b_inum" -c 'print core.nlinkv2' -c "parent" \
+ >> $seqres.full
+
+ _scratch_xfs_db -x \
+ -c "inode $d_inum" -c "link -i $b_inum B1" \
+ >> $seqres.full
+
+ _scratch_xfs_db \
+ -c "echo after root $root_inum" -c "inode $root_inum" -c 'print core.nlinkv2' -c "ls" \
+ -c "echo after D $c_inum" -c "inode $d_inum" -c 'print core.nlinkv2' -c "ls" \
+ -c "echo after B $a_inum" -c "inode $b_inum" -c 'print core.nlinkv2' -c "parent" \
+ >> $seqres.full
+}
+
+simple_online_repair() {
+ echo "check root"
+ $XFS_IO_PROG -c "scrub dirtree $root_inum $root_gen" $SCRATCH_MNT
+ echo "check A"
+ $XFS_IO_PROG -c "scrub dirtree $a_inum $a_gen" $SCRATCH_MNT
+ echo "check B"
+ $XFS_IO_PROG -c "scrub dirtree $b_inum $b_gen" $SCRATCH_MNT
+ echo "check C"
+ $XFS_IO_PROG -c "scrub dirtree $c_inum $c_gen" $SCRATCH_MNT
+ echo "check D"
+ $XFS_IO_PROG -c "scrub dirtree $d_inum $d_gen" $SCRATCH_MNT
+
+ echo "repair root"
+ $XFS_IO_PROG -x -c "repair dirtree $root_inum $root_gen" $SCRATCH_MNT
+ echo "repair A"
+ $XFS_IO_PROG -x -c "repair dirtree $a_inum $a_gen" $SCRATCH_MNT
+ echo "repair D"
+ $XFS_IO_PROG -x -c "repair dirtree $d_inum $d_gen" $SCRATCH_MNT
+ echo "repair B"
+ $XFS_IO_PROG -x -c "repair dirtree $b_inum $b_gen" $SCRATCH_MNT
+ echo "repair C"
+ $XFS_IO_PROG -x -c "repair dirtree $c_inum $c_gen" $SCRATCH_MNT
+ echo "repair D"
+ $XFS_IO_PROG -x -c "repair dirtree $d_inum $d_gen" $SCRATCH_MNT
+
+ echo "check root"
+ $XFS_IO_PROG -c "scrub dirtree $root_inum $root_gen" $SCRATCH_MNT
+ echo "check A"
+ $XFS_IO_PROG -c "scrub dirtree $a_inum $a_gen" $SCRATCH_MNT
+ echo "check B"
+ $XFS_IO_PROG -c "scrub dirtree $b_inum $b_gen" $SCRATCH_MNT
+ echo "check C"
+ $XFS_IO_PROG -c "scrub dirtree $c_inum $c_gen" $SCRATCH_MNT
+ echo "check D"
+ $XFS_IO_PROG -c "scrub dirtree $d_inum $d_gen" $SCRATCH_MNT
+}
+
+# Part 1: Use raw ioctls to detect the loop and fix it.
+prepare_fs
+_scratch_mount
+simple_online_repair
+_check_scratch_fs
+_scratch_unmount
+
+# Part 2: Use xfs_scrub to detect the loop and fix it.
+prepare_fs
+_scratch_mount
+_scratch_scrub &>> $seqres.full
+echo "xfs_scrub returned $?" >> $seqres.full
+_check_scratch_fs
+_scratch_unmount
+
+# success, all done
+status=0
+exit
new file mode 100644
@@ -0,0 +1,25 @@
+QA output created by 1867
+check root
+check A
+check B
+Corruption detected.
+check C
+Corruption detected during cross-referencing.
+check D
+Corruption detected during cross-referencing.
+repair root
+Metadata did not need repair or optimization.
+repair A
+Metadata did not need repair or optimization.
+repair D
+Corruption still detected during cross-referencing.
+repair B
+repair C
+Metadata did not need repair or optimization.
+repair D
+Metadata did not need repair or optimization.
+check root
+check A
+check B
+check C
+check D
new file mode 100755
@@ -0,0 +1,121 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023-2024 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 1868
+#
+# Functional testing for online fsck of a directory chain that is not
+# accessible from the root directory.
+#
+. ./common/preamble
+_begin_fstest auto online_repair
+
+# Import common functions.
+. ./common/filter
+. ./common/inject
+. ./common/fuzzy
+. ./common/populate
+
+# real QA test starts here
+
+# Modify as appropriate.
+_supported_fs xfs
+_require_xfs_db_command "link"
+_require_xfs_db_command "unlink"
+_require_scratch
+_require_xfs_stress_online_repair
+
+prepare_fs() {
+ _scratch_mkfs >> $seqres.full
+ _scratch_mount
+ __stress_scrub_check_commands "%dir%" '' '' 'scrub dirtree'
+
+ # Begin by creating the following directory tree:
+ # root["A"]->A
+ # A["B"]->B
+ # B["C"]->C
+ mkdir -p "$SCRATCH_MNT/A/B/C"
+
+ root_inum="$(stat -c '%i' "$SCRATCH_MNT/")"
+ a_inum="$(stat -c '%i' "$SCRATCH_MNT/A")"
+ b_inum="$(stat -c '%i' "$SCRATCH_MNT/A/B")"
+ c_inum="$(stat -c '%i' "$SCRATCH_MNT/A/B/C")"
+
+ echo "root: $root_inum; a: $a_inum; b: $b_inum; c: $c_inum" >> $seqres.full
+
+ # Next, we sever the tree by deleting root["A"]->A. Directory tree is now:
+ # A["B"]->B
+ # B["C"]->C
+ _scratch_unmount
+
+ root_gen=$(_scratch_xfs_get_metadata_field core.gen "inode $root_inum")
+ a_gen=$(_scratch_xfs_get_metadata_field core.gen "inode $a_inum")
+ b_gen=$(_scratch_xfs_get_metadata_field core.gen "inode $b_inum")
+ c_gen=$(_scratch_xfs_get_metadata_field core.gen "inode $c_inum")
+
+ _scratch_xfs_db \
+ -c "echo before root $root_inum" -c "inode $root_inum" -c 'print core.nlinkv2' -c "ls" \
+ -c "echo before C $c_inum" -c "inode $c_inum" -c 'print core.nlinkv2' -c "ls" \
+ -c "echo before A $a_inum" -c "inode $a_inum" -c 'print core.nlinkv2' -c "parent" \
+ >> $seqres.full
+
+ _scratch_xfs_db -x \
+ -c "inode $root_inum" -c "unlink A" \
+ >> $seqres.full
+
+ _scratch_xfs_db \
+ -c "echo after root $root_inum" -c "inode $root_inum" -c 'print core.nlinkv2' -c "ls" \
+ -c "echo after C $c_inum" -c "inode $c_inum" -c 'print core.nlinkv2' -c "ls" \
+ -c "echo after A $a_inum" -c "inode $a_inum" -c 'print core.nlinkv2' -c "parent" \
+ >> $seqres.full
+}
+
+simple_online_repair() {
+ echo "check root"
+ $XFS_IO_PROG -c "scrub dirtree $root_inum $root_gen" $SCRATCH_MNT
+ echo "check A"
+ $XFS_IO_PROG -c "scrub dirtree $a_inum $a_gen" $SCRATCH_MNT
+ echo "check B"
+ $XFS_IO_PROG -c "scrub dirtree $b_inum $b_gen" $SCRATCH_MNT
+ echo "check C"
+ $XFS_IO_PROG -c "scrub dirtree $c_inum $c_gen" $SCRATCH_MNT
+
+ echo "repair C"
+ $XFS_IO_PROG -x -c "repair dirtree $c_inum $c_gen" $SCRATCH_MNT
+ echo "repair root"
+ $XFS_IO_PROG -x -c "repair dirtree $root_inum $root_gen" $SCRATCH_MNT
+ echo "repair A"
+ $XFS_IO_PROG -x -c "repair dirtree $a_inum $a_gen" $SCRATCH_MNT
+ echo "repair B"
+ $XFS_IO_PROG -x -c "repair dirtree $b_inum $b_gen" $SCRATCH_MNT
+ echo "repair C"
+ $XFS_IO_PROG -x -c "repair dirtree $c_inum $c_gen" $SCRATCH_MNT
+
+ echo "check root"
+ $XFS_IO_PROG -c "scrub dirtree $root_inum $root_gen" $SCRATCH_MNT
+ echo "check A"
+ $XFS_IO_PROG -c "scrub dirtree $a_inum $a_gen" $SCRATCH_MNT
+ echo "check B"
+ $XFS_IO_PROG -c "scrub dirtree $b_inum $b_gen" $SCRATCH_MNT
+ echo "check C"
+ $XFS_IO_PROG -c "scrub dirtree $c_inum $c_gen" $SCRATCH_MNT
+}
+
+# Part 1: Use raw ioctls to detect the chain and fix it.
+prepare_fs
+_scratch_mount
+simple_online_repair
+_check_scratch_fs
+_scratch_unmount
+
+# Part 2: Use xfs_scrub to detect the chain and fix it.
+prepare_fs
+_scratch_mount
+_scratch_scrub &>> $seqres.full
+echo "xfs_scrub returned $?" >> $seqres.full
+_check_scratch_fs
+_scratch_unmount
+
+# success, all done
+status=0
+exit
new file mode 100644
@@ -0,0 +1,21 @@
+QA output created by 1868
+check root
+check A
+Corruption detected.
+check B
+Corruption detected during cross-referencing.
+check C
+Corruption detected during cross-referencing.
+repair C
+Corruption still detected during cross-referencing.
+repair root
+Metadata did not need repair or optimization.
+repair A
+repair B
+Metadata did not need repair or optimization.
+repair C
+Metadata did not need repair or optimization.
+check root
+check A
+check B
+check C
new file mode 100755
@@ -0,0 +1,157 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023-2024 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 1869
+#
+# Functional testing for online fsck of a multiply-owned directory that is
+# accessible from the root directory.
+#
+. ./common/preamble
+_begin_fstest auto online_repair
+
+# Import common functions.
+. ./common/filter
+. ./common/inject
+. ./common/fuzzy
+. ./common/populate
+
+# real QA test starts here
+
+# Modify as appropriate.
+_supported_fs xfs
+_require_xfs_db_command "link"
+_require_xfs_db_command "unlink"
+_require_scratch
+_require_xfs_stress_online_repair
+
+prepare_fs() {
+ _scratch_mkfs >> $seqres.full
+ _scratch_mount
+ __stress_scrub_check_commands "%dir%" '' '' 'scrub dirtree'
+
+ # Begin by creating the following directory tree:
+ # root["A"]->A
+ # A["B"]->B
+ # B["C"]->C
+ # C["D"]->D
+ # root["Z"]->Z
+ # Z["Y"]->Y
+ mkdir -p "$SCRATCH_MNT/A/B/C/D" "$SCRATCH_MNT/Z/Y"
+
+ root_inum="$(stat -c '%i' "$SCRATCH_MNT/")"
+ a_inum="$(stat -c '%i' "$SCRATCH_MNT/A")"
+ b_inum="$(stat -c '%i' "$SCRATCH_MNT/A/B")"
+ c_inum="$(stat -c '%i' "$SCRATCH_MNT/A/B/C")"
+ d_inum="$(stat -c '%i' "$SCRATCH_MNT/A/B/C/D")"
+ z_inum="$(stat -c '%i' "$SCRATCH_MNT/Z")"
+ y_inum="$(stat -c '%i' "$SCRATCH_MNT/Z/Y")"
+
+ echo "root: $root_inum; a: $a_inum; b: $b_inum; c: $c_inum; d: $d_inum" >> $seqres.full
+ echo "root: $root_inum; z: $z_inum; y: $y_inum" >> $seqres.full
+
+ # Next, we create the multiply-owned directory by creating Y["C1"]->C.
+ # Directory tree is now:
+ # root["A"]->A
+ # A["B"]->B
+ # B["C"]->C
+ # C["D"]->D
+ # root["Z"]->Z
+ # Z["Y"]->Y
+ # Y["C1"]->C
+ _scratch_unmount
+
+ root_gen=$(_scratch_xfs_get_metadata_field core.gen "inode $root_inum")
+ a_gen=$(_scratch_xfs_get_metadata_field core.gen "inode $a_inum")
+ b_gen=$(_scratch_xfs_get_metadata_field core.gen "inode $b_inum")
+ c_gen=$(_scratch_xfs_get_metadata_field core.gen "inode $c_inum")
+ d_gen=$(_scratch_xfs_get_metadata_field core.gen "inode $d_inum")
+ z_gen=$(_scratch_xfs_get_metadata_field core.gen "inode $z_inum")
+ y_gen=$(_scratch_xfs_get_metadata_field core.gen "inode $y_inum")
+
+ _scratch_xfs_db \
+ -c "echo before root $root_inum" -c "inode $root_inum" -c 'print core.nlinkv2' -c "ls" \
+ -c "echo before Y $y_inum" -c "inode $y_inum" -c 'print core.nlinkv2' -c "ls" \
+ -c "echo before B $b_inum" -c "inode $b_inum" -c 'print core.nlinkv2' -c "ls" \
+ -c "echo before C $c_inum" -c "inode $c_inum" -c 'print core.nlinkv2' -c "parent" \
+ >> $seqres.full
+
+ _scratch_xfs_db -x \
+ -c "inode $y_inum" -c "link -i $c_inum C1" \
+ >> $seqres.full
+
+ _scratch_xfs_db \
+ -c "echo before root $root_inum" -c "inode $root_inum" -c 'print core.nlinkv2' -c "ls" \
+ -c "echo before Y $y_inum" -c "inode $y_inum" -c 'print core.nlinkv2' -c "ls" \
+ -c "echo before B $b_inum" -c "inode $b_inum" -c 'print core.nlinkv2' -c "ls" \
+ -c "echo before C $c_inum" -c "inode $c_inum" -c 'print core.nlinkv2' -c "parent" \
+ >> $seqres.full
+}
+
+simple_online_repair() {
+ echo "check root"
+ $XFS_IO_PROG -c "scrub dirtree $root_inum $root_gen" $SCRATCH_MNT
+ echo "check A"
+ $XFS_IO_PROG -c "scrub dirtree $a_inum $a_gen" $SCRATCH_MNT
+ echo "check B"
+ $XFS_IO_PROG -c "scrub dirtree $b_inum $b_gen" $SCRATCH_MNT
+ echo "check C"
+ $XFS_IO_PROG -c "scrub dirtree $c_inum $c_gen" $SCRATCH_MNT
+ echo "check D"
+ $XFS_IO_PROG -c "scrub dirtree $d_inum $d_gen" $SCRATCH_MNT
+ echo "check Z"
+ $XFS_IO_PROG -c "scrub dirtree $z_inum $z_gen" $SCRATCH_MNT
+ echo "check Y"
+ $XFS_IO_PROG -c "scrub dirtree $y_inum $y_gen" $SCRATCH_MNT
+
+ echo "repair D"
+ $XFS_IO_PROG -x -c "repair dirtree $d_inum $d_gen" $SCRATCH_MNT
+ echo "repair root"
+ $XFS_IO_PROG -x -c "repair dirtree $root_inum $root_gen" $SCRATCH_MNT
+ echo "repair A"
+ $XFS_IO_PROG -x -c "repair dirtree $a_inum $a_gen" $SCRATCH_MNT
+ echo "repair B"
+ $XFS_IO_PROG -x -c "repair dirtree $b_inum $b_gen" $SCRATCH_MNT
+ echo "repair C"
+ $XFS_IO_PROG -x -c "repair dirtree $c_inum $c_gen" $SCRATCH_MNT
+ echo "repair D"
+ $XFS_IO_PROG -x -c "repair dirtree $d_inum $d_gen" $SCRATCH_MNT
+ echo "repair Z"
+ $XFS_IO_PROG -x -c "repair dirtree $z_inum $z_gen" $SCRATCH_MNT
+ echo "repair Y"
+ $XFS_IO_PROG -x -c "repair dirtree $y_inum $y_gen" $SCRATCH_MNT
+
+ echo "check root"
+ $XFS_IO_PROG -c "scrub dirtree $root_inum $root_gen" $SCRATCH_MNT
+ echo "check A"
+ $XFS_IO_PROG -c "scrub dirtree $a_inum $a_gen" $SCRATCH_MNT
+ echo "check B"
+ $XFS_IO_PROG -c "scrub dirtree $b_inum $b_gen" $SCRATCH_MNT
+ echo "check C"
+ $XFS_IO_PROG -c "scrub dirtree $c_inum $c_gen" $SCRATCH_MNT
+ echo "check D"
+ $XFS_IO_PROG -c "scrub dirtree $d_inum $d_gen" $SCRATCH_MNT
+ echo "check Z"
+ $XFS_IO_PROG -c "scrub dirtree $z_inum $z_gen" $SCRATCH_MNT
+ echo "check Y"
+ $XFS_IO_PROG -c "scrub dirtree $y_inum $y_gen" $SCRATCH_MNT
+}
+
+# Part 1: Use raw ioctls to detect the multi-parent dir and fix it.
+prepare_fs
+_scratch_mount
+simple_online_repair
+_check_scratch_fs
+_scratch_unmount
+
+# Part 2: Use xfs_scrub to detect the multi-parent dir and fix it.
+prepare_fs
+_scratch_mount
+_scratch_scrub &>> $seqres.full
+echo "xfs_scrub returned $?" >> $seqres.full
+_check_scratch_fs
+_scratch_unmount
+
+# success, all done
+status=0
+exit
new file mode 100644
@@ -0,0 +1,32 @@
+QA output created by 1869
+check root
+check A
+check B
+check C
+Corruption detected.
+check D
+Corruption detected during cross-referencing.
+check Z
+check Y
+repair D
+Corruption still detected during cross-referencing.
+repair root
+Metadata did not need repair or optimization.
+repair A
+Metadata did not need repair or optimization.
+repair B
+Metadata did not need repair or optimization.
+repair C
+repair D
+Metadata did not need repair or optimization.
+repair Z
+Metadata did not need repair or optimization.
+repair Y
+Metadata did not need repair or optimization.
+check root
+check A
+check B
+check C
+check D
+check Z
+check Y
new file mode 100755
@@ -0,0 +1,146 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023-2024 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 1870
+#
+# Functional testing for online fsck of a directory loop that is inaccessible
+# from the root directory and has subdirectories.
+#
+. ./common/preamble
+_begin_fstest auto online_repair
+
+# Import common functions.
+. ./common/filter
+. ./common/inject
+. ./common/fuzzy
+. ./common/populate
+
+# real QA test starts here
+
+# Modify as appropriate.
+_supported_fs xfs
+_require_xfs_db_command "link"
+_require_xfs_db_command "unlink"
+_require_scratch
+_require_xfs_stress_online_repair
+
+prepare_fs() {
+ _scratch_mkfs >> $seqres.full
+ _scratch_mount
+ __stress_scrub_check_commands "%dir%" '' '' 'scrub dirtree'
+
+ # Begin by creating the following directory tree:
+ # root["A"]->A
+ # A["B"]->B
+ # B["C"]->C
+ # C["D"]->D
+ # D["E"]->E
+ mkdir -p "$SCRATCH_MNT/A/B/C/D/E"
+
+ root_inum="$(stat -c '%i' "$SCRATCH_MNT/")"
+ a_inum="$(stat -c '%i' "$SCRATCH_MNT/A")"
+ b_inum="$(stat -c '%i' "$SCRATCH_MNT/A/B")"
+ c_inum="$(stat -c '%i' "$SCRATCH_MNT/A/B/C")"
+ d_inum="$(stat -c '%i' "$SCRATCH_MNT/A/B/C/D")"
+ e_inum="$(stat -c '%i' "$SCRATCH_MNT/A/B/C/D/E")"
+
+ echo "root: $root_inum; a: $a_inum; b: $b_inum; c: $c_inum; d: $d_inum; e: $e_inum" >> $seqres.full
+
+ # Complete the loop by creating D["B1"]->B and severing A["B"]->B. Directory
+ # tree is now:
+ # root["A"]->A
+ # B["C"]->C
+ # C["D"]->D
+ # D["E"]->E
+ # D["B1"]->B
+ _scratch_unmount
+
+ root_gen=$(_scratch_xfs_get_metadata_field core.gen "inode $root_inum")
+ a_gen=$(_scratch_xfs_get_metadata_field core.gen "inode $a_inum")
+ b_gen=$(_scratch_xfs_get_metadata_field core.gen "inode $b_inum")
+ c_gen=$(_scratch_xfs_get_metadata_field core.gen "inode $c_inum")
+ d_gen=$(_scratch_xfs_get_metadata_field core.gen "inode $d_inum")
+ e_gen=$(_scratch_xfs_get_metadata_field core.gen "inode $e_inum")
+
+ _scratch_xfs_db \
+ -c "echo before root $root_inum" -c "inode $root_inum" -c 'print core.nlinkv2' -c "ls" \
+ -c "echo before A $d_inum" -c "inode $a_inum" -c 'print core.nlinkv2' -c "ls" \
+ -c "echo before D $d_inum" -c "inode $d_inum" -c 'print core.nlinkv2' -c "ls" \
+ -c "echo before B $b_inum" -c "inode $b_inum" -c 'print core.nlinkv2' -c "parent" \
+ >> $seqres.full
+
+ _scratch_xfs_db -x \
+ -c "inode $d_inum" -c "link -i $b_inum B1" \
+ -c "inode $a_inum" -c "unlink B" \
+ >> $seqres.full
+
+ _scratch_xfs_db \
+ -c "echo before root $root_inum" -c "inode $root_inum" -c 'print core.nlinkv2' -c "ls" \
+ -c "echo before A $d_inum" -c "inode $a_inum" -c 'print core.nlinkv2' -c "ls" \
+ -c "echo before D $d_inum" -c "inode $d_inum" -c 'print core.nlinkv2' -c "ls" \
+ -c "echo before B $b_inum" -c "inode $b_inum" -c 'print core.nlinkv2' -c "parent" \
+ >> $seqres.full
+}
+
+simple_online_repair() {
+ echo "check root"
+ $XFS_IO_PROG -c "scrub dirtree $root_inum $root_gen" $SCRATCH_MNT
+ echo "check A"
+ $XFS_IO_PROG -c "scrub dirtree $a_inum $a_gen" $SCRATCH_MNT
+ echo "check B"
+ $XFS_IO_PROG -c "scrub dirtree $b_inum $b_gen" $SCRATCH_MNT
+ echo "check C"
+ $XFS_IO_PROG -c "scrub dirtree $c_inum $c_gen" $SCRATCH_MNT
+ echo "check D"
+ $XFS_IO_PROG -c "scrub dirtree $d_inum $d_gen" $SCRATCH_MNT
+ echo "check E"
+ $XFS_IO_PROG -c "scrub dirtree $e_inum $e_gen" $SCRATCH_MNT
+
+ echo "repair root"
+ $XFS_IO_PROG -x -c "repair dirtree $root_inum $root_gen" $SCRATCH_MNT
+ echo "repair A"
+ $XFS_IO_PROG -x -c "repair dirtree $a_inum $a_gen" $SCRATCH_MNT
+ echo "repair E"
+ $XFS_IO_PROG -x -c "repair dirtree $e_inum $e_gen" $SCRATCH_MNT
+ echo "repair B"
+ $XFS_IO_PROG -x -c "repair dirtree $b_inum $b_gen" $SCRATCH_MNT
+ echo "repair C"
+ $XFS_IO_PROG -x -c "repair dirtree $c_inum $c_gen" $SCRATCH_MNT
+ echo "repair D"
+ $XFS_IO_PROG -x -c "repair dirtree $d_inum $d_gen" $SCRATCH_MNT
+ echo "repair E"
+ $XFS_IO_PROG -x -c "repair dirtree $e_inum $e_gen" $SCRATCH_MNT
+
+ echo "check root"
+ $XFS_IO_PROG -c "scrub dirtree $root_inum $root_gen" $SCRATCH_MNT
+ echo "check A"
+ $XFS_IO_PROG -c "scrub dirtree $a_inum $a_gen" $SCRATCH_MNT
+ echo "check B"
+ $XFS_IO_PROG -c "scrub dirtree $b_inum $b_gen" $SCRATCH_MNT
+ echo "check C"
+ $XFS_IO_PROG -c "scrub dirtree $c_inum $c_gen" $SCRATCH_MNT
+ echo "check D"
+ $XFS_IO_PROG -c "scrub dirtree $d_inum $d_gen" $SCRATCH_MNT
+ echo "check E"
+ $XFS_IO_PROG -c "scrub dirtree $e_inum $e_gen" $SCRATCH_MNT
+}
+
+# Part 1: Use raw ioctls to detect the loop and fix it.
+prepare_fs
+_scratch_mount
+simple_online_repair
+_check_scratch_fs
+_scratch_unmount
+
+# Part 2: Use xfs_scrub to detect the loop and fix it.
+prepare_fs
+_scratch_mount
+_scratch_scrub &>> $seqres.full
+echo "xfs_scrub returned $?" >> $seqres.full
+_check_scratch_fs
+_scratch_unmount
+
+# success, all done
+status=0
+exit
new file mode 100644
@@ -0,0 +1,30 @@
+QA output created by 1870
+check root
+check A
+check B
+Corruption detected.
+check C
+Corruption detected.
+check D
+Corruption detected.
+check E
+Corruption detected during cross-referencing.
+repair root
+Metadata did not need repair or optimization.
+repair A
+Metadata did not need repair or optimization.
+repair E
+Corruption still detected during cross-referencing.
+repair B
+repair C
+Metadata did not need repair or optimization.
+repair D
+Metadata did not need repair or optimization.
+repair E
+Metadata did not need repair or optimization.
+check root
+check A
+check B
+check C
+check D
+check E
new file mode 100755
@@ -0,0 +1,78 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023-2024 Oracle. All Rights Reserved.
+#
+# FS QA Test No. 1871
+#
+# Race rename and directory tree structure corruption detector for a while to
+# exercise the dirtree code's directory path invalidation and its ability to
+# handle unlinked directories.
+#
+. ./common/preamble
+_begin_fstest scrub dangerous_fsstress_scrub
+
+# 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
+__stress_scrub_check_commands "%dir%" '' '' 'scrub dirtree'
+
+parentA="$SCRATCH_MNT/a"
+parentB="$SCRATCH_MNT/b"
+child="$parentA/c/d/e/f/g/h/i/j/k/l/m/n/o/p"
+unlinked="$SCRATCH_MNT/unlinked"
+
+mkdir -p "$parentA" "$parentB" "$child" "$unlinked"
+
+# Find handle info for the child so that we can scrub by handle
+child_inum="$(stat -c '%i' "$child")"
+_scratch_unmount
+child_gen=$(_scratch_xfs_get_metadata_field core.gen "inode $child_inum")
+_scratch_mount
+
+# Queue up a bunch of scrub requests per invocation
+ioargs=()
+for ((i = 0; i < 100; i++)); do
+ ioargs+=('-c' "scrub dirtree $child_inum $child_gen")
+done
+
+renamer() {
+ # Make sure the scrubber handles unlinked directories correctly
+ # by squatting on an empty directory
+ cd "$unlinked"
+ rm -r -f "$unlinked"
+
+ # Bounce the second level directory between parents to stress the
+ # invalidation detector
+ while [ -e $RUNNING_FILE ]; do
+ mv "$parentA/c" "$parentB/"
+ mv "$parentB/c" "$parentA/"
+ done
+}
+
+RUNNING_FILE="$SCRATCH_MNT/run"
+touch $RUNNING_FILE
+renamer &
+
+# Exercise the directory tree scrubber in two ways -- scrubbing the lowest
+# subdir by handle, and running xfs_scrub on the entire fs.
+while _soak_loop_running $((10 * TIME_FACTOR)); do
+ $XFS_IO_PROG "${ioargs[@]}" "$SCRATCH_MNT"
+ XFS_SCRUB_PHASE=5 _scratch_scrub -n >> $seqres.full
+done
+rm -f $RUNNING_FILE
+wait
+
+# success, all done
+echo Silence is golden
+status=0
+exit
new file mode 100644
@@ -0,0 +1,2 @@
+QA output created by 1871
+Silence is golden