• Filipe Manana's avatar
    Btrfs: fix mount failure after fsync due to hard link recreation · 0d836392
    Filipe Manana authored
    If we end up with logging an inode reference item which has the same name
    but different index from the one we have persisted, we end up failing when
    replaying the log with an errno value of -EEXIST. The error comes from
    btrfs_add_link(), which is called from add_inode_ref(), when we are
    replaying an inode reference item.
    Example scenario where this happens:
      $ mkfs.btrfs -f /dev/sdb
      $ mount /dev/sdb /mnt
      $ touch /mnt/foo
      $ ln /mnt/foo /mnt/bar
      $ sync
      # Rename the first hard link (foo) to a new name and rename the second
      # hard link (bar) to the old name of the first hard link (foo).
      $ mv /mnt/foo /mnt/qwerty
      $ mv /mnt/bar /mnt/foo
      # Create a new file, in the same parent directory, with the old name of
      # the second hard link (bar) and fsync this new file.
      # We do this instead of calling fsync on foo/qwerty because if we did
      # that the fsync resulted in a full transaction commit, not triggering
      # the problem.
      $ touch /mnt/bar
      $ xfs_io -c "fsync" /mnt/bar
      <power fail>
      $ mount /dev/sdb /mnt
      mount: mount /dev/sdb on /mnt failed: File exists
    So fix this by checking if a conflicting inode reference exists (same
    name, same parent but different index), removing it (and the associated
    dir index entries from the parent inode) if it exists, before attempting
    to add the new reference.
    A test case for fstests follows soon.
    CC: stable@vger.kernel.org # 4.4+
    Signed-off-by: default avatarFilipe Manana <fdmanana@suse.com>
    Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>