ext4: Extent overlap bugfix This patch adds a check for overlap of extents and cuts short the new extent to be inserted, if there is a chance of overlap. Signed-off-by: Amit Arora Changes from Take3 to Take4: - no change - Changes from Take2 to Take3: 1) Patch rebased to 2.6.22-rc1 kernel. Changes from Take1 to Take2: 1) As suggested by Andrew, a check for wrap though zero has been added. --- fs/ext4/extents.c | 60 ++++++++++++++++++++++++++++++++++++++-- include/linux/ext4_fs_extents.h | 1 2 files changed, 59 insertions(+), 2 deletions(-) Index: linux-2.6.22-rc1/fs/ext4/extents.c =================================================================== --- linux-2.6.22-rc1.orig/fs/ext4/extents.c 2007-05-18 16:32:32.000000000 -0700 +++ linux-2.6.22-rc1/fs/ext4/extents.c 2007-05-18 16:32:48.000000000 -0700 @@ -1155,6 +1155,55 @@ } /* + * check if a portion of the "newext" extent overlaps with an + * existing extent. + * + * If there is an overlap discovered, it updates the length of the newext + * such that there will be no overlap, and then returns 1. + * If there is no overlap found, it returns 0. + */ +unsigned int ext4_ext_check_overlap(struct inode *inode, + struct ext4_extent *newext, + struct ext4_ext_path *path) +{ + unsigned long b1, b2; + unsigned int depth, len1; + unsigned int ret = 0; + + b1 = le32_to_cpu(newext->ee_block); + len1 = le16_to_cpu(newext->ee_len); + depth = ext_depth(inode); + if (!path[depth].p_ext) + goto out; + b2 = le32_to_cpu(path[depth].p_ext->ee_block); + + /* + * get the next allocated block if the extent in the path + * is before the requested block(s) + */ + if (b2 < b1) { + b2 = ext4_ext_next_allocated_block(path); + if (b2 == EXT_MAX_BLOCK) + goto out; + } + + /* check for wrap through zero */ + if (b1 + len1 < b1) { + len1 = EXT_MAX_BLOCK - b1; + newext->ee_len = cpu_to_le16(len1); + ret = 1; + } + + /* check for overlap */ + if (b1 + len1 > b2) { + newext->ee_len = cpu_to_le16(b2 - b1); + ret = 1; + } +out: + return ret; +} + +/* * ext4_ext_insert_extent: * tries to merge requsted extent into the existing extent or * inserts requested extent as new one into the tree, @@ -2055,7 +2104,15 @@ /* allocate new block */ goal = ext4_ext_find_goal(inode, path, iblock); - allocated = max_blocks; + + /* Check if we can really insert (iblock)::(iblock+max_blocks) extent */ + newex.ee_block = cpu_to_le32(iblock); + newex.ee_len = cpu_to_le16(max_blocks); + err = ext4_ext_check_overlap(inode, &newex, path); + if (err) + allocated = le16_to_cpu(newex.ee_len); + else + allocated = max_blocks; newblock = ext4_new_blocks(handle, inode, goal, &allocated, &err); if (!newblock) goto out2; @@ -2063,7 +2120,6 @@ goal, newblock, allocated); /* try to insert new extent into found leaf and return */ - newex.ee_block = cpu_to_le32(iblock); ext4_ext_store_pblock(&newex, newblock); newex.ee_len = cpu_to_le16(allocated); err = ext4_ext_insert_extent(handle, inode, path, &newex); Index: linux-2.6.22-rc1/include/linux/ext4_fs_extents.h =================================================================== --- linux-2.6.22-rc1.orig/include/linux/ext4_fs_extents.h 2007-05-18 16:32:09.000000000 -0700 +++ linux-2.6.22-rc1/include/linux/ext4_fs_extents.h 2007-05-18 16:32:48.000000000 -0700 @@ -190,6 +190,7 @@ extern int ext4_extent_tree_init(handle_t *, struct inode *); extern int ext4_ext_calc_credits_for_insert(struct inode *, struct ext4_ext_path *); +extern unsigned int ext4_ext_check_overlap(struct inode *, struct ext4_extent *, struct ext4_ext_path *); extern int ext4_ext_insert_extent(handle_t *, struct inode *, struct ext4_ext_path *, struct ext4_extent *); extern int ext4_ext_walk_space(struct inode *, unsigned long, unsigned long, ext_prepare_callback, void *); extern struct ext4_ext_path * ext4_ext_find_extent(struct inode *, int, struct ext4_ext_path *);