• Filipe Manana's avatar
    Btrfs: fix fsync xattr loss in the fast fsync path · 36283bf7
    Filipe Manana authored
    After commit 4f764e51 ("Btrfs: remove deleted xattrs on fsync log
    replay"), we can end up in a situation where during log replay we end up
    deleting xattrs that were never deleted when their file was last fsynced.
    
    This happens in the fast fsync path (flag BTRFS_INODE_NEEDS_FULL_SYNC is
    not set in the inode) if the inode has the flag BTRFS_INODE_COPY_EVERYTHING
    set, the xattr was added in a past transaction and the leaf where the
    xattr is located was not updated (COWed or created) in the current
    transaction. In this scenario the xattr item never ends up in the log
    tree and therefore at log replay time, which makes the replay code delete
    the xattr from the fs/subvol tree as it thinks that xattr was deleted
    prior to the last fsync.
    
    Fix this by always logging all xattrs, which is the simplest and most
    reliable way to detect deleted xattrs and replay the deletes at log replay
    time.
    
    This issue is reproducible with the following test case for fstests:
    
      seq=`basename $0`
      seqres=$RESULT_DIR/$seq
      echo "QA output created by $seq"
    
      here=`pwd`
      tmp=/tmp/$$
      status=1	# failure is the default!
    
      _cleanup()
      {
          _cleanup_flakey
          rm -f $tmp.*
      }
      trap "_cleanup; exit \$status" 0 1 2 3 15
    
      # get standard environment, filters and checks
      . ./common/rc
      . ./common/filter
      . ./common/dmflakey
      . ./common/attr
    
      # real QA test starts here
    
      # We create a lot of xattrs for a single file. Only btrfs and xfs are currently
      # able to store such a large mount of xattrs per file, other filesystems such
      # as ext3/4 and f2fs for example, fail with ENOSPC even if we attempt to add
      # less than 1000 xattrs with very small values.
      _supported_fs btrfs xfs
      _supported_os Linux
      _need_to_be_root
      _require_scratch
      _require_dm_flakey
      _require_attrs
      _require_metadata_journaling $SCRATCH_DEV
    
      rm -f $seqres.full
    
      _scratch_mkfs >> $seqres.full 2>&1
      _init_flakey
      _mount_flakey
    
      # Create the test file with some initial data and make sure everything is
      # durably persisted.
      $XFS_IO_PROG -f -c "pwrite -S 0xaa 0 32k" $SCRATCH_MNT/foo | _filter_xfs_io
      sync
    
      # Add many small xattrs to our file.
      # We create such a large amount because it's needed to trigger the issue found
      # in btrfs - we need to have an amount that causes the fs to have at least 3
      # btree leafs with xattrs stored in them, and it must work on any leaf size
      # (maximum leaf/node size is 64Kb).
      num_xattrs=2000
      for ((i = 1; i <= $num_xattrs; i++)); do
          name="user.attr_$(printf "%04d" $i)"
          $SETFATTR_PROG -n $name -v "val_$(printf "%04d" $i)" $SCRATCH_MNT/foo
      done
    
      # Sync the filesystem to force a commit of the current btrfs transaction, this
      # is a necessary condition to trigger the bug on btrfs.
      sync
    
      # Now update our file's data and fsync the file.
      # After a successful fsync, if the fsync log/journal is replayed we expect to
      # see all the xattrs we added before with the same values (and the updated file
      # data of course). Btrfs used to delete some of these xattrs when it replayed
      # its fsync log/journal.
      $XFS_IO_PROG -c "pwrite -S 0xbb 8K 16K" \
                   -c "fsync" \
                   $SCRATCH_MNT/foo | _filter_xfs_io
    
      # Simulate a crash/power loss.
      _load_flakey_table $FLAKEY_DROP_WRITES
      _unmount_flakey
    
      # Allow writes again and mount. This makes the fs replay its fsync log.
      _load_flakey_table $FLAKEY_ALLOW_WRITES
      _mount_flakey
    
      echo "File content after crash and log replay:"
      od -t x1 $SCRATCH_MNT/foo
    
      echo "File xattrs after crash and log replay:"
      for ((i = 1; i <= $num_xattrs; i++)); do
          name="user.attr_$(printf "%04d" $i)"
          echo -n "$name="
          $GETFATTR_PROG --absolute-names -n $name --only-values $SCRATCH_MNT/foo
          echo
      done
    
      status=0
      exit
    
    The golden output expects all xattrs to be available, and with the correct
    values, after the fsync log is replayed.
    Signed-off-by: default avatarFilipe Manana <fdmanana@suse.com>
    Signed-off-by: default avatarChris Mason <clm@fb.com>
    36283bf7
tree-log.c 136 KB