1. 23 Aug, 2018 2 commits
    • Filipe Manana's avatar
      Btrfs: fix data corruption when deduplicating between different files · de02b9f6
      Filipe Manana authored
      If we deduplicate extents between two different files we can end up
      corrupting data if the source range ends at the size of the source file,
      the source file's size is not aligned to the filesystem's block size
      and the destination range does not go past the size of the destination
      file size.
      
      Example:
      
        $ mkfs.btrfs -f /dev/sdb
        $ mount /dev/sdb /mnt
      
        $ xfs_io -f -c "pwrite -S 0x6b 0 2518890" /mnt/foo
        # The first byte with a value of 0xae starts at an offset (2518890)
        # which is not a multiple of the sector size.
        $ xfs_io -c "pwrite -S 0xae 2518890 102398" /mnt/foo
      
        # Confirm the file content is full of bytes with values 0x6b and 0xae.
        $ od -t x1 /mnt/foo
        0000000 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b
        *
        11467540 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b ae ae ae ae ae ae
        11467560 ae ae ae ae ae ae ae ae ae ae ae ae ae ae ae ae
        *
        11777540 ae ae ae ae ae ae ae ae
        11777550
      
        # Create a second file with a length not aligned to the sector size,
        # whose bytes all have the value 0x6b, so that its extent(s) can be
        # deduplicated with the first file.
        $ xfs_io -f -c "pwrite -S 0x6b 0 557771" /mnt/bar
      
        # Now deduplicate the entire second file into a range of the first file
        # that also has all bytes with the value 0x6b. The destination range's
        # end offset must not be aligned to the sector size and must be less
        # then the offset of the first byte with the value 0xae (byte at offset
        # 2518890).
        $ xfs_io -c "dedupe /mnt/bar 0 1957888 557771" /mnt/foo
      
        # The bytes in the range starting at offset 2515659 (end of the
        # deduplication range) and ending at offset 2519040 (start offset
        # rounded up to the block size) must all have the value 0xae (and not
        # replaced with 0x00 values). In other words, we should have exactly
        # the same data we had before we asked for deduplication.
        $ od -t x1 /mnt/foo
        0000000 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b
        *
        11467540 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b ae ae ae ae ae ae
        11467560 ae ae ae ae ae ae ae ae ae ae ae ae ae ae ae ae
        *
        11777540 ae ae ae ae ae ae ae ae
        11777550
      
        # Unmount the filesystem and mount it again. This guarantees any file
        # data in the page cache is dropped.
        $ umount /dev/sdb
        $ mount /dev/sdb /mnt
      
        $ od -t x1 /mnt/foo
        0000000 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b
        *
        11461300 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 00 00 00 00 00
        11461320 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
        *
        11470000 ae ae ae ae ae ae ae ae ae ae ae ae ae ae ae ae
        *
        11777540 ae ae ae ae ae ae ae ae
        11777550
      
        # The bytes in range 2515659 to 2519040 have a value of 0x00 and not a
        # value of 0xae, data corruption happened due to the deduplication
        # operation.
      
      So fix this by rounding down, to the sector size, the length used for the
      deduplication when the following conditions are met:
      
        1) Source file's range ends at its i_size;
        2) Source file's i_size is not aligned to the sector size;
        3) Destination range does not cross the i_size of the destination file.
      
      Fixes: e1d227a4 ("btrfs: Handle unaligned length in extent_same")
      CC: stable@vger.kernel.org # 4.2+
      Signed-off-by: default avatarFilipe Manana <fdmanana@suse.com>
      Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
      de02b9f6
    • Filipe Manana's avatar
      Btrfs: sync log after logging new name · d4682ba0
      Filipe Manana authored
      When we add a new name for an inode which was logged in the current
      transaction, we update the inode in the log so that its new name and
      ancestors are added to the log. However when we do this we do not persist
      the log, so the changes remain in memory only, and as a consequence, any
      ancestors that were created in the current transaction are updated such
      that future calls to btrfs_inode_in_log() return true. This leads to a
      subsequent fsync against such new ancestor directories returning
      immediately, without persisting the log, therefore after a power failure
      the new ancestor directories do not exist, despite fsync being called
      against them explicitly.
      
      Example:
      
        $ mkfs.btrfs -f /dev/sdb
        $ mount /dev/sdb /mnt
      
        $ mkdir /mnt/A
        $ mkdir /mnt/B
        $ mkdir /mnt/A/C
        $ touch /mnt/B/foo
        $ xfs_io -c "fsync" /mnt/B/foo
        $ ln /mnt/B/foo /mnt/A/C/foo
        $ xfs_io -c "fsync" /mnt/A
        <power failure>
      
      After the power failure, directory "A" does not exist, despite the explicit
      fsync on it.
      
      Instead of fixing this by changing the behaviour of the explicit fsync on
      directory "A" to persist the log instead of doing nothing, make the logging
      of the new file name (which happens when creating a hard link or renaming)
      persist the log. This approach not only is simpler, not requiring addition
      of new fields to the inode in memory structure, but also gives us the same
      behaviour as ext4, xfs and f2fs (possibly other filesystems too).
      
      A test case for fstests follows soon.
      
      Fixes: 12fcfd22 ("Btrfs: tree logging unlink/rename fixes")
      Reported-by: default avatarVijay Chidambaram <vvijay03@gmail.com>
      Signed-off-by: default avatarFilipe Manana <fdmanana@suse.com>
      Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
      d4682ba0
  2. 17 Aug, 2018 1 commit
    • Robbie Ko's avatar
      Btrfs: fix unexpected failure of nocow buffered writes after snapshotting when low on space · 8ecebf4d
      Robbie Ko authored
      Commit e9894fd3 ("Btrfs: fix snapshot vs nocow writting") forced
      nocow writes to fallback to COW, during writeback, when a snapshot is
      created. This resulted in writes made before creating the snapshot to
      unexpectedly fail with ENOSPC during writeback when success (0) was
      returned to user space through the write system call.
      
      The steps leading to this problem are:
      
      1. When it's not possible to allocate data space for a write, the
         buffered write path checks if a NOCOW write is possible.  If it is,
         it will not reserve space and success (0) is returned to user space.
      
      2. Then when a snapshot is created, the root's will_be_snapshotted
         atomic is incremented and writeback is triggered for all inode's that
         belong to the root being snapshotted. Incrementing that atomic forces
         all previous writes to fallback to COW during writeback (running
         delalloc).
      
      3. This results in the writeback for the inodes to fail and therefore
         setting the ENOSPC error in their mappings, so that a subsequent
         fsync on them will report the error to user space. So it's not a
         completely silent data loss (since fsync will report ENOSPC) but it's
         a very unexpected and undesirable behaviour, because if a clean
         shutdown/unmount of the filesystem happens without previous calls to
         fsync, it is expected to have the data present in the files after
         mounting the filesystem again.
      
      So fix this by adding a new atomic named snapshot_force_cow to the
      root structure which prevents this behaviour and works the following way:
      
      1. It is incremented when we start to create a snapshot after triggering
         writeback and before waiting for writeback to finish.
      
      2. This new atomic is now what is used by writeback (running delalloc)
         to decide whether we need to fallback to COW or not. Because we
         incremented this new atomic after triggering writeback in the
         snapshot creation ioctl, we ensure that all buffered writes that
         happened before snapshot creation will succeed and not fallback to
         COW (which would make them fail with ENOSPC).
      
      3. The existing atomic, will_be_snapshotted, is kept because it is used
         to force new buffered writes, that start after we started
         snapshotting, to reserve data space even when NOCOW is possible.
         This makes these writes fail early with ENOSPC when there's no
         available space to allocate, preventing the unexpected behaviour of
         writeback later failing with ENOSPC due to a fallback to COW mode.
      
      Fixes: e9894fd3 ("Btrfs: fix snapshot vs nocow writting")
      Signed-off-by: default avatarRobbie Ko <robbieko@synology.com>
      Reviewed-by: default avatarFilipe Manana <fdmanana@suse.com>
      Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
      8ecebf4d
  3. 06 Aug, 2018 37 commits