ext4: teach ext4_ext_find_extent() to realloc path if necessary
authorTheodore Ts'o <tytso@mit.edu>
Mon, 1 Sep 2014 18:40:09 +0000 (14:40 -0400)
committerTheodore Ts'o <tytso@mit.edu>
Mon, 1 Sep 2014 18:40:09 +0000 (14:40 -0400)
This adds additional safety in case for some reason we end reusing a
path structure which isn't big enough for current depth of the inode.

Signed-off-by: Theodore Ts'o <tytso@mit.edu>
fs/ext4/ext4_extents.h
fs/ext4/extents.c

index a867f5ca99919d05e2e6709f80f08566a33c435d..3c938154709478ba39f5eb18d8d2b53678932ee8 100644 (file)
@@ -123,6 +123,7 @@ find_ext4_extent_tail(struct ext4_extent_header *eh)
 struct ext4_ext_path {
        ext4_fsblk_t                    p_block;
        __u16                           p_depth;
+       __u16                           p_maxdepth;
        struct ext4_extent              *p_ext;
        struct ext4_extent_idx          *p_idx;
        struct ext4_extent_header       *p_hdr;
index 538f9a4d96ffbd090c8da30822664042f93c7f81..c94c7480053e9e2ba97121fc0933d8f5a10ff590 100644 (file)
@@ -869,14 +869,20 @@ ext4_ext_find_extent(struct inode *inode, ext4_lblk_t block,
        eh = ext_inode_hdr(inode);
        depth = ext_depth(inode);
 
-       if (path)
+       if (path) {
                ext4_ext_drop_refs(path);
-       else {
+               if (depth > path[0].p_maxdepth) {
+                       kfree(path);
+                       *orig_path = path = NULL;
+               }
+       }
+       if (!path) {
                /* account possible depth increase */
                path = kzalloc(sizeof(struct ext4_ext_path) * (depth + 2),
                                GFP_NOFS);
                if (unlikely(!path))
                        return ERR_PTR(-ENOMEM);
+               path[0].p_maxdepth = depth + 1;
        }
        path[0].p_hdr = eh;
        path[0].p_bh = NULL;
@@ -1820,6 +1826,7 @@ static void ext4_ext_try_to_merge_up(handle_t *handle,
                sizeof(struct ext4_extent_idx);
        s += sizeof(struct ext4_extent_header);
 
+       path[1].p_maxdepth = path[0].p_maxdepth;
        memcpy(path[0].p_hdr, path[1].p_hdr, s);
        path[0].p_depth = 0;
        path[0].p_ext = EXT_FIRST_EXTENT(path[0].p_hdr) +
@@ -2150,12 +2157,6 @@ static int ext4_fill_fiemap_extents(struct inode *inode,
                /* find extent for this block */
                down_read(&EXT4_I(inode)->i_data_sem);
 
-               if (path && ext_depth(inode) != depth) {
-                       /* depth was changed. we have to realloc path */
-                       kfree(path);
-                       path = NULL;
-               }
-
                path = ext4_ext_find_extent(inode, block, &path, 0);
                if (IS_ERR(path)) {
                        up_read(&EXT4_I(inode)->i_data_sem);
@@ -2173,7 +2174,6 @@ static int ext4_fill_fiemap_extents(struct inode *inode,
                }
                ex = path[depth].p_ext;
                next = ext4_ext_next_allocated_block(path);
-               ext4_ext_drop_refs(path);
 
                flags = 0;
                exists = 0;
@@ -2897,7 +2897,7 @@ again:
                        ext4_journal_stop(handle);
                        return -ENOMEM;
                }
-               path[0].p_depth = depth;
+               path[0].p_maxdepth = path[0].p_depth = depth;
                path[0].p_hdr = ext_inode_hdr(inode);
                i = 0;