new helper: iterate_supers()
[safe/jmp/linux-2.6] / fs / ext4 / move_extent.c
index 5a106e0..d1fc662 100644 (file)
@@ -15,6 +15,7 @@
 
 #include <linux/fs.h>
 #include <linux/quotaops.h>
+#include <linux/slab.h>
 #include "ext4_jbd2.h"
 #include "ext4_extents.h"
 #include "ext4.h"
@@ -152,12 +153,12 @@ mext_check_null_inode(struct inode *inode1, struct inode *inode2,
        int ret = 0;
 
        if (inode1 == NULL) {
-               ext4_error(inode2->i_sb, function,
+               __ext4_error(inode2->i_sb, function,
                        "Both inodes should not be NULL: "
                        "inode1 NULL inode2 %lu", inode2->i_ino);
                ret = -EIO;
        } else if (inode2 == NULL) {
-               ext4_error(inode1->i_sb, function,
+               __ext4_error(inode1->i_sb, function,
                        "Both inodes should not be NULL: "
                        "inode1 %lu inode2 NULL", inode1->i_ino);
                ret = -EIO;
@@ -252,6 +253,7 @@ mext_insert_across_blocks(handle_t *handle, struct inode *orig_inode,
                }
 
                o_start->ee_len = start_ext->ee_len;
+               eblock = le32_to_cpu(start_ext->ee_block);
                new_flag = 1;
 
        } else if (start_ext->ee_len && new_ext->ee_len &&
@@ -262,6 +264,7 @@ mext_insert_across_blocks(handle_t *handle, struct inode *orig_inode,
                 * orig  |------------------------------|
                 */
                o_start->ee_len = start_ext->ee_len;
+               eblock = le32_to_cpu(start_ext->ee_block);
                new_flag = 1;
 
        } else if (!start_ext->ee_len && new_ext->ee_len &&
@@ -475,7 +478,6 @@ mext_leaf_block(handle_t *handle, struct inode *orig_inode,
        struct ext4_extent *oext, *o_start, *o_end, *prev_ext;
        struct ext4_extent new_ext, start_ext, end_ext;
        ext4_lblk_t new_ext_end;
-       ext4_fsblk_t new_phys_end;
        int oext_alen, new_ext_alen, end_ext_alen;
        int depth = ext_depth(orig_inode);
        int ret;
@@ -489,7 +491,6 @@ mext_leaf_block(handle_t *handle, struct inode *orig_inode,
        new_ext.ee_len = dext->ee_len;
        new_ext_alen = ext4_ext_get_actual_len(&new_ext);
        new_ext_end = le32_to_cpu(new_ext.ee_block) + new_ext_alen - 1;
-       new_phys_end = ext_pblock(&new_ext) + new_ext_alen - 1;
 
        /*
         * Case: original extent is first
@@ -502,6 +503,7 @@ mext_leaf_block(handle_t *handle, struct inode *orig_inode,
                le32_to_cpu(oext->ee_block) + oext_alen) {
                start_ext.ee_len = cpu_to_le16(le32_to_cpu(new_ext.ee_block) -
                                               le32_to_cpu(oext->ee_block));
+               start_ext.ee_block = oext->ee_block;
                copy_extent_status(oext, &start_ext);
        } else if (oext > EXT_FIRST_EXTENT(orig_path[depth].p_hdr)) {
                prev_ext = oext - 1;
@@ -515,6 +517,7 @@ mext_leaf_block(handle_t *handle, struct inode *orig_inode,
                        start_ext.ee_len = cpu_to_le16(
                                ext4_ext_get_actual_len(prev_ext) +
                                new_ext_alen);
+                       start_ext.ee_block = oext->ee_block;
                        copy_extent_status(prev_ext, &start_ext);
                        new_ext.ee_len = 0;
                }
@@ -526,7 +529,7 @@ mext_leaf_block(handle_t *handle, struct inode *orig_inode,
         * new_ext       |-------|
         */
        if (le32_to_cpu(oext->ee_block) + oext_alen - 1 < new_ext_end) {
-               ext4_error(orig_inode->i_sb, __func__,
+               ext4_error(orig_inode->i_sb,
                        "new_ext_end(%u) should be less than or equal to "
                        "oext->ee_block(%u) + oext_alen(%d) - 1",
                        new_ext_end, le32_to_cpu(oext->ee_block),
@@ -660,6 +663,9 @@ mext_replace_branches(handle_t *handle, struct inode *orig_inode,
        int replaced_count = 0;
        int dext_alen;
 
+       /* Protect extent trees against block allocations via delalloc */
+       double_down_write_data_sem(orig_inode, donor_inode);
+
        /* Get the original extent for the block "orig_off" */
        *err = get_ext_path(orig_inode, orig_off, &orig_path);
        if (*err)
@@ -686,12 +692,12 @@ mext_replace_branches(handle_t *handle, struct inode *orig_inode,
        while (1) {
                /* The extent for donor must be found. */
                if (!dext) {
-                       ext4_error(donor_inode->i_sb, __func__,
+                       ext4_error(donor_inode->i_sb,
                                   "The extent for donor must be found");
                        *err = -EIO;
                        goto out;
                } else if (donor_off != le32_to_cpu(tmp_dext.ee_block)) {
-                       ext4_error(donor_inode->i_sb, __func__,
+                       ext4_error(donor_inode->i_sb,
                                "Donor offset(%u) and the first block of donor "
                                "extent(%u) should be equal",
                                donor_off,
@@ -755,6 +761,11 @@ out:
                kfree(donor_path);
        }
 
+       ext4_ext_invalidate_cache(orig_inode);
+       ext4_ext_invalidate_cache(donor_inode);
+
+       double_up_write_data_sem(orig_inode, donor_inode);
+
        return replaced_count;
 }
 
@@ -820,19 +831,9 @@ move_extent_per_page(struct file *o_filp, struct inode *donor_inode,
         * Just swap data blocks between orig and donor.
         */
        if (uninit) {
-               /*
-                * Protect extent trees against block allocations
-                * via delalloc
-                */
-               double_down_write_data_sem(orig_inode, donor_inode);
                replaced_count = mext_replace_branches(handle, orig_inode,
                                                donor_inode, orig_blk_offset,
                                                block_len_in_page, err);
-
-               /* Clear the inode cache not to refer to the old data */
-               ext4_ext_invalidate_cache(orig_inode);
-               ext4_ext_invalidate_cache(donor_inode);
-               double_up_write_data_sem(orig_inode, donor_inode);
                goto out2;
        }
 
@@ -880,8 +881,6 @@ move_extent_per_page(struct file *o_filp, struct inode *donor_inode,
        /* Release old bh and drop refs */
        try_to_release_page(page, 0);
 
-       /* Protect extent trees against block allocations via delalloc */
-       double_down_write_data_sem(orig_inode, donor_inode);
        replaced_count = mext_replace_branches(handle, orig_inode, donor_inode,
                                        orig_blk_offset, block_len_in_page,
                                        &err2);
@@ -890,18 +889,10 @@ move_extent_per_page(struct file *o_filp, struct inode *donor_inode,
                        block_len_in_page = replaced_count;
                        replaced_size =
                                block_len_in_page << orig_inode->i_blkbits;
-               } else {
-                       double_up_write_data_sem(orig_inode, donor_inode);
+               } else
                        goto out;
-               }
        }
 
-       /* Clear the inode cache not to refer to the old data */
-       ext4_ext_invalidate_cache(orig_inode);
-       ext4_ext_invalidate_cache(donor_inode);
-
-       double_up_write_data_sem(orig_inode, donor_inode);
-
        if (!page_has_buffers(page))
                create_empty_buffers(page, 1 << orig_inode->i_blkbits, 0);
 
@@ -940,14 +931,13 @@ out2:
 }
 
 /**
- * mext_check_argumants - Check whether move extent can be done
+ * mext_check_arguments - Check whether move extent can be done
  *
  * @orig_inode:                original inode
  * @donor_inode:       donor inode
  * @orig_start:                logical start offset in block for orig
  * @donor_start:       logical start offset in block for donor
  * @len:               the number of blocks to be moved
- * @moved_len:         moved block length
  *
  * Check the arguments of ext4_move_extents() whether the files can be
  * exchanged with each other.
@@ -955,18 +945,17 @@ out2:
  */
 static int
 mext_check_arguments(struct inode *orig_inode,
-                         struct inode *donor_inode, __u64 orig_start,
-                         __u64 donor_start, __u64 *len, __u64 moved_len)
+                    struct inode *donor_inode, __u64 orig_start,
+                    __u64 donor_start, __u64 *len)
 {
        ext4_lblk_t orig_blocks, donor_blocks;
        unsigned int blkbits = orig_inode->i_blkbits;
        unsigned int blocksize = 1 << blkbits;
 
-       /* Regular file check */
-       if (!S_ISREG(orig_inode->i_mode) || !S_ISREG(donor_inode->i_mode)) {
-               ext4_debug("ext4 move extent: The argument files should be "
-                       "regular file [ino:orig %lu, donor %lu]\n",
-                       orig_inode->i_ino, donor_inode->i_ino);
+       if (donor_inode->i_mode & (S_ISUID|S_ISGID)) {
+               ext4_debug("ext4 move extent: suid or sgid is set"
+                          " to donor file [ino:orig %lu, donor %lu]\n",
+                          orig_inode->i_ino, donor_inode->i_ino);
                return -EINVAL;
        }
 
@@ -1010,13 +999,6 @@ mext_check_arguments(struct inode *orig_inode,
                return -EINVAL;
        }
 
-       if (moved_len) {
-               ext4_debug("ext4 move extent: moved_len should be 0 "
-                       "[ino:orig %lu, donor %lu]\n", orig_inode->i_ino,
-                       donor_inode->i_ino);
-               return -EINVAL;
-       }
-
        if ((orig_start > EXT_MAX_BLOCK) ||
            (donor_start > EXT_MAX_BLOCK) ||
            (*len > EXT_MAX_BLOCK) ||
@@ -1217,6 +1199,14 @@ ext4_move_extents(struct file *o_filp, struct file *d_filp,
                return -EINVAL;
        }
 
+       /* Regular file check */
+       if (!S_ISREG(orig_inode->i_mode) || !S_ISREG(donor_inode->i_mode)) {
+               ext4_debug("ext4 move extent: The argument files should be "
+                       "regular file [ino:orig %lu, donor %lu]\n",
+                       orig_inode->i_ino, donor_inode->i_ino);
+               return -EINVAL;
+       }
+
        /* Protect orig and donor inodes against a truncate */
        ret1 = mext_inode_double_lock(orig_inode, donor_inode);
        if (ret1 < 0)
@@ -1226,7 +1216,7 @@ ext4_move_extents(struct file *o_filp, struct file *d_filp,
        double_down_write_data_sem(orig_inode, donor_inode);
        /* Check the filesystem environment whether move_extent can be done */
        ret1 = mext_check_arguments(orig_inode, donor_inode, orig_start,
-                                       donor_start, &len, *moved_len);
+                                   donor_start, &len);
        if (ret1)
                goto out;
 
@@ -1289,10 +1279,6 @@ ext4_move_extents(struct file *o_filp, struct file *d_filp,
                         ext4_ext_get_actual_len(ext_cur), block_end + 1) -
                     max(le32_to_cpu(ext_cur->ee_block), block_start);
 
-       /* Discard preallocations of two inodes */
-       ext4_discard_preallocations(orig_inode);
-       ext4_discard_preallocations(donor_inode);
-
        while (!last_extent && le32_to_cpu(ext_cur->ee_block) <= block_end) {
                seq_blocks += add_blocks;
 
@@ -1368,7 +1354,7 @@ ext4_move_extents(struct file *o_filp, struct file *d_filp,
                        if (ret1 < 0)
                                break;
                        if (*moved_len > len) {
-                               ext4_error(orig_inode->i_sb, __func__,
+                               ext4_error(orig_inode->i_sb,
                                        "We replaced blocks too much! "
                                        "sum of replaced: %llu requested: %llu",
                                        *moved_len, len);
@@ -1410,6 +1396,11 @@ ext4_move_extents(struct file *o_filp, struct file *d_filp,
 
        }
 out:
+       if (*moved_len) {
+               ext4_discard_preallocations(orig_inode);
+               ext4_discard_preallocations(donor_inode);
+       }
+
        if (orig_path) {
                ext4_ext_drop_refs(orig_path);
                kfree(orig_path);