4 # This test is motivated by an fsync issue discovered in btrfs.
5 # The issue in btrfs was that adding a new hard link to an inode that already
6 # had a large number of hardlinks and fsync the inode, would make the fsync
7 # log replay code update the inode with a wrong link count (smaller than the
8 # correct value). This resulted later in dangling directory index entries,
9 # after removing most of the hard links (correct_value - wrong_value), that
10 # were visible to user space but it was impossible to delete them or do
11 # any other operation on them (since they pointed to an inode that didn't
12 # exist anymore, resulting in -ESTALE errors).
14 # The btrfs issue was fixed by the following linux kernel patch:
16 # Btrfs: fix fsync when extend references are added to an inode
18 # This issue was present in btrfs since the extrefs (extend references)
19 # feature was added (2012).
21 #-----------------------------------------------------------------------
22 # Copyright (C) 2015 SUSE Linux Products GmbH. All Rights Reserved.
23 # Author: Filipe Manana <fdmanana@suse.com>
25 # This program is free software; you can redistribute it and/or
26 # modify it under the terms of the GNU General Public License as
27 # published by the Free Software Foundation.
29 # This program is distributed in the hope that it would be useful,
30 # but WITHOUT ANY WARRANTY; without even the implied warranty of
31 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
32 # GNU General Public License for more details.
34 # You should have received a copy of the GNU General Public License
35 # along with this program; if not, write the Free Software Foundation,
36 # Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
37 #-----------------------------------------------------------------------
41 seqres=$RESULT_DIR/$seq
42 echo "QA output created by $seq"
46 status=1 # failure is the default!
52 trap "_cleanup; exit \$status" 0 1 2 3 15
54 # get standard environment, filters and checks
59 # real QA test starts here
64 _require_dm_target flakey
65 _require_metadata_journaling $SCRATCH_DEV
69 # If the test filesystem is btrfs, make sure we create a filesystem with
70 # the extend references (extrefs) feature enabled (it's enabled by default
71 # in recent versions of btrfs-progs).
72 if [ "$FSTYP" = "btrfs" ]; then
73 _scratch_mkfs "-O extref" >> $seqres.full 2>&1
75 _scratch_mkfs >> $seqres.full 2>&1
81 # Create a test file with 3001 hard links. This number is large enough to
82 # make btrfs start using extrefs at some point even if the fs has the maximum
83 # possible leaf/node size (64Kb).
84 echo "hello world" > $SCRATCH_MNT/foo
85 for i in `seq 1 3000`; do
86 ln $SCRATCH_MNT/foo $SCRATCH_MNT/foo_link_`printf "%04d" $i`
89 # Make sure all metadata and data are durably persisted.
92 # Add one more link to the inode that ends up being a btrfs extref and fsync
94 ln $SCRATCH_MNT/foo $SCRATCH_MNT/foo_link_3001
95 $XFS_IO_PROG -c "fsync" $SCRATCH_MNT/foo
97 _flakey_drop_and_remount
99 # Now after the fsync log replay btrfs left our inode with a wrong link count N,
100 # which was smaller than the correct link count M (N < M).
101 # So after removing N hard links, the remaining M - N directory entries were
102 # still visible to user space but it was impossible to do anything with them
103 # because they pointed to an inode that didn't exist anymore. This resulted in
104 # stale file handle errors (-ESTALE) when accessing those dentries for example.
106 # So remove all hard links except the first one and then attempt to read the
107 # file, to verify we don't get an -ESTALE error when accessing the inode.
109 # The btrfs fsck tool also detected the incorrect inode link count and it
110 # reported an error message like the following:
112 # root 5 inode 257 errors 2001, no inode item, link count wrong
113 # unresolved ref dir 256 index 2978 namelen 13 name foo_link_2976 filetype 1 errors 4, no inode ref
115 # The fstests framework automatically calls fsck after a test is run, so we
116 # don't need to call fsck explicitly here.
118 echo "Link count before rm foo_link_*: $(stat -c %h $SCRATCH_MNT/foo)"
119 rm -f $SCRATCH_MNT/foo_link_*
120 echo "Link count after rm foo_link_*: $(stat -c %h $SCRATCH_MNT/foo)"