#! /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