| #! /bin/bash |
| # SPDX-License-Identifier: GPL-2.0 |
| # Copyright (c) 2022 Meta Platforms, Inc. All Rights Reserved. |
| # |
| # FS QA Test 299 |
| # |
| # Given a file with extents: |
| # [0 : 4096) (inline) |
| # [4096 : N] (prealloc) |
| # if a user uses the ioctl BTRFS_IOC_LOGICAL_INO[_V2] asking for the file of the |
| # non-inline extent, it results in reading the offset field of the inline |
| # extent, which is meaningless (it is full of user data..). If we are |
| # particularly lucky, it can be past the end of the extent buffer, resulting in |
| # a crash. This test creates that circumstance and asserts that logical inode |
| # resolution is still successful. |
| # |
| . ./common/preamble |
| _begin_fstest auto quick preallocrw logical_resolve |
| |
| _require_scratch |
| _require_xfs_io_command "falloc" "-k" |
| _require_btrfs_command inspect-internal logical-resolve |
| # Can't run with nodatacow because we need to be able to create an inline extent |
| # in a range with a prealloc extent, which can only happen with COW enabled. |
| _require_btrfs_no_nodatacow |
| _fixed_by_kernel_commit 560840afc3e6 \ |
| "btrfs: fix resolving backrefs for inline extent followed by prealloc" |
| |
| # This test needs to create conditions s.t. the special inode's inline extent |
| # is the first item in a leaf. Therefore, fix a leaf size and add |
| # items that are otherwise not necessary to reproduce the inline-prealloc |
| # condition to get to such a state. |
| # |
| # Roughly, the idea for getting the right item fill is to: |
| # 1. create extra inline items to cause leaf splitting. |
| # 2. put the target item in the middle so it is likely to catch the split |
| # 3. add an extra variable inline item to tweak any final adjustments |
| # |
| # It took a bit of trial and error to hit working counts of inline items, since |
| # it also had to account for dir and index items all going to the front. |
| |
| # use a 64k nodesize so that an fs with 64k pages and no subpage sector size |
| # support will correctly reproduce the problem. |
| _scratch_mkfs "--nodesize 64k" >> $seqres.full || _fail "mkfs failed" |
| _scratch_mount |
| |
| f=$SCRATCH_MNT/f |
| # write extra files before the evil file to use up the leaf and |
| # help trick leaf balancing |
| for i in {1..41}; do |
| $XFS_IO_PROG -fc "pwrite -q 0 1024" $f.inl.$i |
| done |
| |
| # write a variable inline file to help pad the preceeding leaf |
| $XFS_IO_PROG -fc "pwrite -q 0 1" $f.inl-var.$i |
| |
| # falloc the evil file whose inline extent will start a leaf |
| $XFS_IO_PROG -fc "falloc -k 0 1m" $f.evil |
| $XFS_IO_PROG -fc fsync $f.evil |
| |
| # write extra files after the evil file to use up the leaf and |
| # help trick leaf balancing |
| for i in {1..42}; do |
| $XFS_IO_PROG -fc "pwrite -q 0 1024" $f.inl.2.$i |
| done |
| |
| # grab the prealloc offset from dump tree while it's still the only |
| # extent data item for the inode |
| logical=$(_btrfs_get_file_extent_item_bytenr $f.evil 0) |
| |
| # do the "small write; fsync; small write" pattern which reproduces the desired |
| # item pattern of an inline extent followed by a preallocated extent. The 23 |
| # size is somewhat arbitrary, but ensures that the offset field is past the eb |
| # when we are item 0 (borrowed from the actual crash this reproduces). |
| $XFS_IO_PROG -fc "pwrite -q 0 23" $f.evil |
| $XFS_IO_PROG -fc fsync $f.evil |
| $XFS_IO_PROG -fc "pwrite -q 0 23" $f.evil |
| |
| # ensure we have all the extent_data items for when we do logical to inode |
| # resolution |
| sync |
| |
| # trigger the backref walk which accesses the bad inline extent |
| btrfs inspect-internal logical-resolve $logical $SCRATCH_MNT |
| |
| echo "Silence is golden" |
| status=0 |
| exit |