blob: 57a797f0f5c83f74fc47a0878584ab4de6244135 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
|
#! /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
|