summaryrefslogtreecommitdiff
path: root/tests/xfs/191
blob: 7a02f1be217cb8442e0cf78b5b40e78fca7605dd (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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#! /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