ext4: check for extents that wrap around
[pandora-kernel.git] / fs / ext4 / extents.c
index b48e0dc..bb40a70 100644 (file)
@@ -318,8 +318,14 @@ static int ext4_valid_extent(struct inode *inode, struct ext4_extent *ext)
 {
        ext4_fsblk_t block = ext4_ext_pblock(ext);
        int len = ext4_ext_get_actual_len(ext);
+       ext4_lblk_t lblock = le32_to_cpu(ext->ee_block);
 
-       if (len == 0)
+       /*
+        * We allow neither:
+        *  - zero length
+        *  - overflow/wrap-around
+        */
+       if (lblock + len <= lblock)
                return 0;
        return ext4_data_block_valid(EXT4_SB(inode->i_sb), block, len);
 }
@@ -345,11 +351,26 @@ static int ext4_valid_extent_entries(struct inode *inode,
        if (depth == 0) {
                /* leaf entries */
                struct ext4_extent *ext = EXT_FIRST_EXTENT(eh);
+               struct ext4_super_block *es = EXT4_SB(inode->i_sb)->s_es;
+               ext4_fsblk_t pblock = 0;
+               ext4_lblk_t lblock = 0;
+               ext4_lblk_t prev = 0;
+               int len = 0;
                while (entries) {
                        if (!ext4_valid_extent(inode, ext))
                                return 0;
+
+                       /* Check for overlapping extents */
+                       lblock = le32_to_cpu(ext->ee_block);
+                       len = ext4_ext_get_actual_len(ext);
+                       if ((lblock <= prev) && prev) {
+                               pblock = ext4_ext_pblock(ext);
+                               es->s_last_error_block = cpu_to_le64(pblock);
+                               return 0;
+                       }
                        ext++;
                        entries--;
+                       prev = lblock + len - 1;
                }
        } else {
                struct ext4_extent_idx *ext_idx = EXT_FIRST_INDEX(eh);
@@ -683,7 +704,8 @@ ext4_ext_find_extent(struct inode *inode, ext4_lblk_t block,
                path[ppos].p_depth = i;
                path[ppos].p_ext = NULL;
 
-               bh = sb_getblk(inode->i_sb, path[ppos].p_block);
+               bh = sb_getblk_gfp(inode->i_sb, path[ppos].p_block,
+                                  __GFP_MOVABLE | GFP_NOFS);
                if (unlikely(!bh)) {
                        ret = -ENOMEM;
                        goto err;
@@ -888,7 +910,7 @@ static int ext4_ext_split(handle_t *handle, struct inode *inode,
                err = -EIO;
                goto cleanup;
        }
-       bh = sb_getblk(inode->i_sb, newblock);
+       bh = sb_getblk_gfp(inode->i_sb, newblock, __GFP_MOVABLE | GFP_NOFS);
        if (!bh) {
                err = -ENOMEM;
                goto cleanup;
@@ -1072,7 +1094,7 @@ static int ext4_ext_grow_indepth(handle_t *handle, struct inode *inode,
        if (newblock == 0)
                return err;
 
-       bh = sb_getblk(inode->i_sb, newblock);
+       bh = sb_getblk_gfp(inode->i_sb, newblock, __GFP_MOVABLE | GFP_NOFS);
        if (!bh)
                return -ENOMEM;
        lock_buffer(bh);
@@ -1642,8 +1664,7 @@ static unsigned int ext4_ext_check_overlap(struct ext4_sb_info *sbi,
        depth = ext_depth(inode);
        if (!path[depth].p_ext)
                goto out;
-       b2 = le32_to_cpu(path[depth].p_ext->ee_block);
-       b2 &= ~(sbi->s_cluster_ratio - 1);
+       b2 = EXT4_LBLK_CMASK(sbi, le32_to_cpu(path[depth].p_ext->ee_block));
 
        /*
         * get the next allocated block if the extent in the path
@@ -1653,7 +1674,7 @@ static unsigned int ext4_ext_check_overlap(struct ext4_sb_info *sbi,
                b2 = ext4_ext_next_allocated_block(path);
                if (b2 == EXT_MAX_BLOCKS)
                        goto out;
-               b2 &= ~(sbi->s_cluster_ratio - 1);
+               b2 = EXT4_LBLK_CMASK(sbi, b2);
        }
 
        /* check for wrap through zero on extent logical start block*/
@@ -2288,7 +2309,7 @@ static int ext4_remove_blocks(handle_t *handle, struct inode *inode,
                 * truncate operation has removed all of the blocks in
                 * the cluster.
                 */
-               if (pblk & (sbi->s_cluster_ratio - 1) &&
+               if (EXT4_PBLK_COFF(sbi, pblk) &&
                    (ee_len == num))
                        *partial_cluster = EXT4_B2C(sbi, pblk);
                else
@@ -2356,6 +2377,27 @@ ext4_ext_rm_leaf(handle_t *handle, struct inode *inode,
        ex_ee_block = le32_to_cpu(ex->ee_block);
        ex_ee_len = ext4_ext_get_actual_len(ex);
 
+       /*
+        * If we're starting with an extent other than the last one in the
+        * node, we need to see if it shares a cluster with the extent to
+        * the right (towards the end of the file). If its leftmost cluster
+        * is this extent's rightmost cluster and it is not cluster aligned,
+        * we'll mark it as a partial that is not to be deallocated.
+        */
+
+       if (ex != EXT_LAST_EXTENT(eh)) {
+               ext4_fsblk_t current_pblk, right_pblk;
+               long long current_cluster, right_cluster;
+
+               current_pblk = ext4_ext_pblock(ex) + ex_ee_len - 1;
+               current_cluster = (long long)EXT4_B2C(sbi, current_pblk);
+               right_pblk = ext4_ext_pblock(ex + 1);
+               right_cluster = (long long)EXT4_B2C(sbi, right_pblk);
+               if (current_cluster == right_cluster &&
+                       EXT4_PBLK_COFF(sbi, right_pblk))
+                       *partial_cluster = -right_cluster;
+       }
+
        trace_ext4_ext_rm_leaf(inode, start, ex, *partial_cluster);
 
        while (ex >= EXT_FIRST_EXTENT(eh) &&
@@ -2960,6 +3002,7 @@ static int ext4_split_extent(handle_t *handle,
        int err = 0;
        int uninitialized;
        int split_flag1, flags1;
+       int allocated = map->m_len;
 
        depth = ext_depth(inode);
        ex = path[depth].p_ext;
@@ -2979,6 +3022,8 @@ static int ext4_split_extent(handle_t *handle,
                                map->m_lblk + map->m_len, split_flag1, flags1);
                if (err)
                        goto out;
+       } else {
+               allocated = ee_len - (map->m_lblk - ee_block);
        }
 
        ext4_ext_drop_refs(path);
@@ -3001,7 +3046,7 @@ static int ext4_split_extent(handle_t *handle,
 
        ext4_ext_show_leaf(inode, path);
 out:
-       return err ? err : map->m_len;
+       return err ? err : allocated;
 }
 
 #define EXT4_EXT_ZERO_LEN 7
@@ -3488,7 +3533,7 @@ int ext4_find_delalloc_cluster(struct inode *inode, ext4_lblk_t lblk,
 {
        struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
        ext4_lblk_t lblk_start, lblk_end;
-       lblk_start = lblk & (~(sbi->s_cluster_ratio - 1));
+       lblk_start = EXT4_LBLK_CMASK(sbi, lblk);
        lblk_end = lblk_start + sbi->s_cluster_ratio - 1;
 
        return ext4_find_delalloc_range(inode, lblk_start, lblk_end,
@@ -3548,9 +3593,9 @@ get_reserved_cluster_alloc(struct inode *inode, ext4_lblk_t lblk_start,
        trace_ext4_get_reserved_cluster_alloc(inode, lblk_start, num_blks);
 
        /* Check towards left side */
-       c_offset = lblk_start & (sbi->s_cluster_ratio - 1);
+       c_offset = EXT4_LBLK_COFF(sbi, lblk_start);
        if (c_offset) {
-               lblk_from = lblk_start & (~(sbi->s_cluster_ratio - 1));
+               lblk_from = EXT4_LBLK_CMASK(sbi, lblk_start);
                lblk_to = lblk_from + c_offset - 1;
 
                if (ext4_find_delalloc_range(inode, lblk_from, lblk_to, 0))
@@ -3558,7 +3603,7 @@ get_reserved_cluster_alloc(struct inode *inode, ext4_lblk_t lblk_start,
        }
 
        /* Now check towards right. */
-       c_offset = (lblk_start + num_blks) & (sbi->s_cluster_ratio - 1);
+       c_offset = EXT4_LBLK_COFF(sbi, lblk_start + num_blks);
        if (allocated_clusters && c_offset) {
                lblk_from = lblk_start + num_blks;
                lblk_to = lblk_from + (sbi->s_cluster_ratio - c_offset) - 1;
@@ -3663,6 +3708,7 @@ out:
                                        allocated - map->m_len);
                allocated = map->m_len;
        }
+       map->m_len = allocated;
 
        /*
         * If we have done fallocate with the offset that is already
@@ -3750,7 +3796,7 @@ static int get_implied_cluster_alloc(struct super_block *sb,
                                     struct ext4_ext_path *path)
 {
        struct ext4_sb_info *sbi = EXT4_SB(sb);
-       ext4_lblk_t c_offset = map->m_lblk & (sbi->s_cluster_ratio-1);
+       ext4_lblk_t c_offset = EXT4_LBLK_COFF(sbi, map->m_lblk);
        ext4_lblk_t ex_cluster_start, ex_cluster_end;
        ext4_lblk_t rr_cluster_start, rr_cluster_end;
        ext4_lblk_t ee_block = le32_to_cpu(ex->ee_block);
@@ -3769,8 +3815,7 @@ static int get_implied_cluster_alloc(struct super_block *sb,
            (rr_cluster_start == ex_cluster_start)) {
                if (rr_cluster_start == ex_cluster_end)
                        ee_start += ee_len - 1;
-               map->m_pblk = (ee_start & ~(sbi->s_cluster_ratio - 1)) +
-                       c_offset;
+               map->m_pblk = EXT4_PBLK_CMASK(sbi, ee_start) + c_offset;
                map->m_len = min(map->m_len,
                                 (unsigned) sbi->s_cluster_ratio - c_offset);
                /*
@@ -4048,7 +4093,7 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,
         */
        map->m_flags &= ~EXT4_MAP_FROM_CLUSTER;
        newex.ee_block = cpu_to_le32(map->m_lblk);
-       cluster_offset = map->m_lblk & (sbi->s_cluster_ratio-1);
+       cluster_offset = EXT4_LBLK_CMASK(sbi, map->m_lblk);
 
        /*
         * If we are doing bigalloc, check to see if the extent returned
@@ -4116,7 +4161,7 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,
         * needed so that future calls to get_implied_cluster_alloc()
         * work correctly.
         */
-       offset = map->m_lblk & (sbi->s_cluster_ratio - 1);
+       offset = EXT4_LBLK_COFF(sbi, map->m_lblk);
        ar.len = EXT4_NUM_B2C(sbi, offset+allocated);
        ar.goal -= offset;
        ar.logical -= offset;
@@ -4430,13 +4475,6 @@ long ext4_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
        struct ext4_map_blocks map;
        unsigned int credits, blkbits = inode->i_blkbits;
 
-       /*
-        * currently supporting (pre)allocate mode for extent-based
-        * files _only_
-        */
-       if (!(ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)))
-               return -EOPNOTSUPP;
-
        /* Return error if mode is not supported */
        if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE))
                return -EOPNOTSUPP;
@@ -4457,6 +4495,15 @@ long ext4_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
         */
        credits = ext4_chunk_trans_blocks(inode, max_blocks);
        mutex_lock(&inode->i_mutex);
+
+       /*
+        * We only support preallocation for extent-based files only
+        */
+       if (!(ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))) {
+               ret = -EOPNOTSUPP;
+               goto out;
+       }
+
        ret = inode_newsize_ok(inode, (len + offset));
        if (ret) {
                mutex_unlock(&inode->i_mutex);
@@ -4513,6 +4560,7 @@ retry:
                ret = 0;
                goto retry;
        }
+out:
        mutex_unlock(&inode->i_mutex);
        trace_ext4_fallocate_exit(inode, offset, max_blocks,
                                ret > 0 ? ret2 : ret);
@@ -4797,7 +4845,7 @@ static int ext4_xattr_fiemap(struct inode *inode,
                error = ext4_get_inode_loc(inode, &iloc);
                if (error)
                        return error;
-               physical = iloc.bh->b_blocknr << blockbits;
+               physical = (__u64)iloc.bh->b_blocknr << blockbits;
                offset = EXT4_GOOD_OLD_INODE_SIZE +
                                EXT4_I(inode)->i_extra_isize;
                physical += offset;
@@ -4805,7 +4853,7 @@ static int ext4_xattr_fiemap(struct inode *inode,
                flags |= FIEMAP_EXTENT_DATA_INLINE;
                brelse(iloc.bh);
        } else { /* external block */
-               physical = EXT4_I(inode)->i_file_acl << blockbits;
+               physical = (__u64)EXT4_I(inode)->i_file_acl << blockbits;
                length = inode->i_sb->s_blocksize;
        }