Skip to content

Commit 080ecbd

Browse files
robbieko-aikdave
authored andcommitted
btrfs: mark file extent range dirty after converting prealloc extents
When writing into a preallocated extent, ordered extent completion calls btrfs_mark_extent_written() to convert the file extent item from the BTRFS_FILE_EXTENT_PREALLOC type to the BTRFS_FILE_EXTENT_REG type. If the preallocated extent was created beyond i_size with fallocate keep-size, and the inode is evicted and loaded again before the write, the inode's file_extent_tree is initialized only up to i_size. The beyond i_size prealloc extent is therefore not tracked there. After a write into that extent extends i_size, btrfs_mark_extent_written() updates the file extent item, but the corresponding range is not marked dirty in the inode's file_extent_tree. This can leave disk_i_size stale when the filesystem does not use the no-holes feature, so after remount the file size can go back to the old value. The following reproducer triggers the problem: $ cat test.sh #!/bin/bash DEV=/dev/sdi MNT=/mnt/sdi mkfs.btrfs -f -O ^no-holes $DEV mount $DEV $MNT touch $MNT/file fallocate -n -l 2M $MNT/file umount $MNT mount $DEV $MNT dd if=/dev/zero of=$MNT/file bs=1M count=1 conv=notrunc ls -lh $MNT/file umount $MNT mount $DEV $MNT ls -lh $MNT/file umount $MNT Running the reproducer gives the following result: $ ./test.sh (...) 1048576 bytes (1.0 MB, 1.0 MiB) copied, 0.000596024 s, 1.8 GB/s -rw-rw-r-- 1 root root 1.0M May 8 16:34 /mnt/sdi/file -rw-rw-r-- 1 root root 0 May 8 16:34 /mnt/sdi/file Fix this by marking the written range dirty in the inode's file_extent_tree after successfully converting the prealloc extent to a regular extent. Fixes: 9ddc959 ("btrfs: use the file extent tree infrastructure") Reviewed-by: Filipe Manana <fdmanana@suse.com> Signed-off-by: Robbie Ko <robbieko@synology.com> [ Minor change log updates ] Signed-off-by: Filipe Manana <fdmanana@suse.com> Signed-off-by: David Sterba <dsterba@suse.com>
1 parent c562ba6 commit 080ecbd

1 file changed

Lines changed: 8 additions & 3 deletions

File tree

fs/btrfs/file.c

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -633,7 +633,7 @@ int btrfs_mark_extent_written(struct btrfs_trans_handle *trans,
633633
trans->transid);
634634
btrfs_set_file_extent_num_bytes(leaf, fi,
635635
end - other_start);
636-
return 0;
636+
goto mark_dirty;
637637
}
638638
}
639639

@@ -661,7 +661,7 @@ int btrfs_mark_extent_written(struct btrfs_trans_handle *trans,
661661
other_end - start);
662662
btrfs_set_file_extent_offset(leaf, fi,
663663
start - orig_offset);
664-
return 0;
664+
goto mark_dirty;
665665
}
666666
}
667667

@@ -788,7 +788,12 @@ int btrfs_mark_extent_written(struct btrfs_trans_handle *trans,
788788
}
789789
}
790790

791-
return 0;
791+
mark_dirty:
792+
ret = btrfs_inode_set_file_extent_range(inode, start, end - start);
793+
if (ret)
794+
btrfs_abort_transaction(trans, ret);
795+
796+
return ret;
792797
}
793798

794799
/*

0 commit comments

Comments
 (0)