add patch replace-open-coded-nofail-allocation
[ext4-patch-queue.git] / be-more-strict-when-migrating-to-non-extent-based-file
blobdf4e789ba655912417a410d4a5a6e6a6d2278ae4
1 ext4: be more strict when migrating to non-extent based file
3 From: Eryu Guan <guaneryu@gmail.com>
5 Currently the check in ext4_ind_migrate() is not enough before doing the
6 real conversion:
8 a) delayed allocated extents could bypass the check on eh->eh_entries
9    and eh->eh_depth
11 This can be demonstrated by this script
13   xfs_io -fc "pwrite 0 4k" -c "pwrite 8k 4k" /mnt/ext4/testfile
14   chattr -e /mnt/ext4/testfile
16 where testfile has two extents but still be converted to non-extent
17 based file format.
19 b) only extent length is checked but not the offset, which would result
20    in data lose (delalloc) or fs corruption (nodelalloc), because
21    non-extent based file only supports at most (12 + 2^10 + 2^20 + 2^30)
22    blocks
24 This can be demostrated by
26   xfs_io -fc "pwrite 5T 4k" /mnt/ext4/testfile
27   chattr -e /mnt/ext4/testfile
28   sync
30 If delalloc is enabled, dmesg prints
31   EXT4-fs warning (device dm-4): ext4_block_to_path:105: block 1342177280 > max in inode 53
32   EXT4-fs (dm-4): Delayed block allocation failed for inode 53 at logical offset 1342177280 with max blocks 1 with error 5
33   EXT4-fs (dm-4): This should not happen!! Data will be lost
35 If delalloc is disabled, e2fsck -nf shows corruption
36   Inode 53, i_size is 5497558142976, should be 4096.  Fix? no
38 Fix the two issues by
40 a) forcing all delayed allocation blocks to be allocated before checking
41    eh->eh_depth and eh->eh_entries
42 b) limiting the last logical block of the extent is within direct map
44 Signed-off-by: Eryu Guan <guaneryu@gmail.com>
45 Signed-off-by: Theodore Ts'o <tytso@mit.edu>
46 Cc: stable@vger.kernel.org
47 ---
48  fs/ext4/migrate.c | 12 +++++++++++-
49  1 file changed, 11 insertions(+), 1 deletion(-)
51 diff --git a/fs/ext4/migrate.c b/fs/ext4/migrate.c
52 index b52374e..3651141 100644
53 --- a/fs/ext4/migrate.c
54 +++ b/fs/ext4/migrate.c
55 @@ -620,6 +620,7 @@ int ext4_ind_migrate(struct inode *inode)
56         struct ext4_inode_info          *ei = EXT4_I(inode);
57         struct ext4_extent              *ex;
58         unsigned int                    i, len;
59 +       ext4_lblk_t                     end;
60         ext4_fsblk_t                    blk;
61         handle_t                        *handle;
62         int                             ret;
63 @@ -633,6 +634,14 @@ int ext4_ind_migrate(struct inode *inode)
64                                        EXT4_FEATURE_RO_COMPAT_BIGALLOC))
65                 return -EOPNOTSUPP;
67 +       /*
68 +        * In order to get correct extent info, force all delayed allocation
69 +        * blocks to be allocated, otherwise delayed allocation blocks may not
70 +        * be reflected and bypass the checks on extent header.
71 +        */
72 +       if (test_opt(inode->i_sb, DELALLOC))
73 +               ext4_alloc_da_blocks(inode);
75         handle = ext4_journal_start(inode, EXT4_HT_MIGRATE, 1);
76         if (IS_ERR(handle))
77                 return PTR_ERR(handle);
78 @@ -654,7 +663,8 @@ int ext4_ind_migrate(struct inode *inode)
79         else {
80                 len = le16_to_cpu(ex->ee_len);
81                 blk = ext4_ext_pblock(ex);
82 -               if (len > EXT4_NDIR_BLOCKS) {
83 +               end = le32_to_cpu(ex->ee_block) + len - 1;
84 +               if (end >= EXT4_NDIR_BLOCKS) {
85                         ret = -EOPNOTSUPP;
86                         goto errout;
87                 }
88 -- 
89 1.8.3.1
92 To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
93 the body of a message to majordomo@vger.kernel.org
94 More majordomo info at  http://vger.kernel.org/majordomo-info.html