lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:	Tue, 1 Mar 2011 15:38:09 +0800
From:	Dave Young <hidave.darkstar@...il.com>
To:	Allison Henderson <achender@...ux.vnet.ibm.com>
Cc:	linux-ext4@...r.kernel.org
Subject: Re: [Ext4 punch hole 3/5] Ext4 Punch Hole Support: Punch out extents

On Tue, Mar 1, 2011 at 11:08 AM, Allison Henderson
<achender@...ux.vnet.ibm.com> wrote:
> This patch modifes the truncate routines to support hole punching
> Below is a brief summary of the pacthes changes:
>
> - Added new function "ext_ext4_rm_leaf_punch_hole".
>        This routine is very similar to ext_ext4_rm_leaf except that
>        it punches a hole in a leaf instead of just removing the tail
>        or removing the entire leaf all together

These two functions are almost duplicate,
merge them as one function would be better

Also as Andreas Dilger said coding style problems need fix as well

>
> - Implemented the "remove head" case in the ext_remove_blocks routine
>        This routine is used by ext_ext4_rm_leaf to remove the tail
>        of an extent during a truncate.  The new ext_ext4_rm_leaf_punch_hole
>        routine will now also use it to remove the head of an extent in the
>        case that the hole covers a region of blocks at the beginning
>        of an extent.
>
> - Added "stop" param to ext4_ext_remove_space routine
>        This function has been modified to accept a stop parameter.  If stop
>        is not EXT_MAX_BLOCK, the routine will call ext_ext4_rm_leaf_punch_hole
>        instead of ext_ext4_rm_leaf.
>
> - Added new "ext4_ext_release_blocks" routine
>        This routine is basically the ext4_ext_truncate routine, but
>        modified to accept a "stop" param in addition to "start".  The existing
>        ext4_ext_truncate routine has now become a wrapper to this
>        function.  The stop parameter just passed through to ext4_ext_remove_space
>
> Signed-off-by: Allison Henderson <achender@...ibm.com>
> ---
> :100644 100644 ab2e42e... efbc3ef... M  fs/ext4/extents.c
>  fs/ext4/extents.c |  265 +++++++++++++++++++++++++++++++++++++++++++++++++++--
>  1 files changed, 256 insertions(+), 9 deletions(-)
>
> diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
> index ab2e42e..efbc3ef 100644
> --- a/fs/ext4/extents.c
> +++ b/fs/ext4/extents.c
> @@ -2159,8 +2159,16 @@ static int ext4_remove_blocks(handle_t *handle, struct inode *inode,
>                ext4_free_blocks(handle, inode, 0, start, num, flags);
>        } else if (from == le32_to_cpu(ex->ee_block)
>                   && to <= le32_to_cpu(ex->ee_block) + ee_len - 1) {
> -               printk(KERN_INFO "strange request: removal %u-%u from %u:%u\n",
> -                       from, to, le32_to_cpu(ex->ee_block), ee_len);
> +               /* head removal */
> +               ext4_lblk_t num;
> +               ext4_fsblk_t start;
> +
> +               num = to - from;
> +               start = ext4_ext_pblock(ex);
> +
> +               ext_debug("free first %u blocks starting %llu\n", num, start);
> +               ext4_free_blocks(handle, inode, 0, start, num, flags);
> +
>        } else {
>                printk(KERN_INFO "strange request: removal(2) "
>                                "%u-%u from %u:%u\n",
> @@ -2401,6 +2409,214 @@ out:
>  }
>
>  /*
> + * ext4_ext_rm_leaf_punch_hole() Removes the extents associated with the
> + * blocks appearing between "start" and "stop", and splits the extents
> + * if "start" and "stop" appear in the same extent
> + */
> +static int
> +ext4_ext_rm_leaf_punch_hole(handle_t *handle, struct inode *inode,
> +               struct ext4_ext_path *path, ext4_lblk_t start, ext4_lblk_t stop)
> +{
> +       int err = 0, correct_index = 0;
> +       int depth = ext_depth(inode), credits;
> +       struct ext4_extent_header *eh;
> +       ext4_lblk_t a, b, block;
> +       unsigned int num;
> +       ext4_lblk_t ex_ee_block;
> +       unsigned short ex_ee_len;
> +       unsigned int uninitialized = 0;
> +       struct ext4_extent *ex, *i_ex;
> +
> +       /* the header must be checked already in ext4_ext_remove_space() */
> +       ext_debug("truncate since %u in leaf\n", start);
> +       if (!path[depth].p_hdr)
> +               path[depth].p_hdr = ext_block_hdr(path[depth].p_bh);
> +
> +       eh = path[depth].p_hdr;
> +       if (unlikely(path[depth].p_hdr == NULL)) {
> +               EXT4_ERROR_INODE(inode, "path[%d].p_hdr == NULL", depth);
> +               return -EIO;
> +       }
> +
> +       /* find where to start removing */
> +       ex = EXT_LAST_EXTENT(eh);
> +
> +       ex_ee_block = le32_to_cpu(ex->ee_block);
> +       ex_ee_len = ext4_ext_get_actual_len(ex);
> +
> +       while (ex >= EXT_FIRST_EXTENT(eh) &&
> +                       ex_ee_block + ex_ee_len > start ) {
> +               if (ext4_ext_is_uninitialized(ex))
> +                       uninitialized = 1;
> +               else
> +                       uninitialized = 0;
> +
> +               ext_debug("remove ext %u:[%d]%d\n", ex_ee_block,
> +                        uninitialized, ex_ee_len);
> +               path[depth].p_ext = ex;
> +
> +               a = ex_ee_block > start ? ex_ee_block : start;
> +               b = ex_ee_block+ex_ee_len - 1 < stop ? ex_ee_block+ex_ee_len - 1 : stop;
> +
> +               ext_debug("  border %u:%u\n", a, b);
> +
> +               /* If this extent is beyond the end of the hole, skip it  */
> +               if(stop <= ex_ee_block){
> +                       ex--;
> +                       ex_ee_block = le32_to_cpu(ex->ee_block);
> +                       ex_ee_len = ext4_ext_get_actual_len(ex);
> +                       continue;
> +               }
> +               else if (a != ex_ee_block && b != ex_ee_block + ex_ee_len - 1) {
> +                       /*
> +                        * If this is a truncate, then this condition should
> +                        * never happen becase at least one of the end points
> +                        * needs to be on the edge of the extent.
> +                        */
> +                       if(stop == EXT_MAX_BLOCK){
> +                               ext_debug("  bad truncate %u:%u\n", start, stop);
> +                               block = 0;
> +                               num = 0;
> +                               err = -EIO;
> +                               goto out;
> +                       }
> +                       /*
> +                        * else this is a hole punch, so the extent needs to
> +                        * be split since neither edge of the hole is on the
> +                        * extent edge
> +                        */
> +                       else{
> +                               err = ext4_split_extents(handle,inode, b,path,0);
> +                               if(err)
> +                                       goto out;
> +
> +                               ex_ee_len = ext4_ext_get_actual_len(ex);
> +
> +                               b = ex_ee_block+ex_ee_len - 1 < stop ? ex_ee_block+ex_ee_len - 1 : stop;
> +
> +                               /* Then remove tail of this extent */
> +                               block = ex_ee_block;
> +                               num = a - block;
> +                       }
> +               }
> +               else if (a != ex_ee_block) {
> +                       /* remove tail of the extent */
> +                       block = ex_ee_block;
> +                       num = a - block;
> +               }
> +               else if (b != ex_ee_block + ex_ee_len - 1) {
> +                       /* remove head of the extent */
> +                       block = b;
> +                       num =  ex_ee_block+ex_ee_len - b;
> +
> +                       /* If this is a truncate, this condition should never happen */
> +                       if(stop == EXT_MAX_BLOCK){
> +                               err = -EIO;
> +                               goto out;
> +                       }
> +               }
> +               else {
> +                       /* remove whole extent: excellent! */
> +                       block = ex_ee_block;
> +                       num = 0;
> +                       if (a != ex_ee_block){
> +                               err = -EIO;
> +                               goto out;
> +                       }
> +
> +                       if (b != ex_ee_block + ex_ee_len - 1){
> +                               err = -EIO;
> +                               goto out;
> +                       }
> +               }
> +
> +               /*
> +                * 3 for leaf, sb, and inode plus 2 (bmap and group
> +                * descriptor) for each block group; assume two block
> +                * groups plus ex_ee_len/blocks_per_block_group for
> +                * the worst case
> +                */
> +               credits = 7 + 2*(ex_ee_len/EXT4_BLOCKS_PER_GROUP(inode->i_sb));
> +               if (ex == EXT_FIRST_EXTENT(eh)) {
> +                       correct_index = 1;
> +                       credits += (ext_depth(inode)) + 1;
> +               }
> +               credits += EXT4_MAXQUOTAS_TRANS_BLOCKS(inode->i_sb);
> +               err = ext4_ext_truncate_extend_restart(handle, inode, credits);
> +               if (err)
> +                       goto out;
> +
> +               err = ext4_ext_get_access(handle, inode, path + depth);
> +               if (err)
> +                       goto out;
> +
> +               err = ext4_remove_blocks(handle, inode, ex, a, b);
> +               if (err)
> +                       goto out;
> +
> +               ex->ee_block = cpu_to_le32(block);
> +               ex->ee_len = cpu_to_le16(num);
> +
> +               /*
> +                * If this was a head removal, then we need to update
> +                * the physical block since it is now at a different
> +                * location
> +                */
> +               if (block != ex_ee_block)
> +                       ext4_ext_store_pblock(ex, ext4_ext_pblock(ex)+(b-a) );
> +
> +               /*
> +                * Do not mark uninitialized if all the blocks in the
> +                * extent have been removed.
> +                */
> +               if (uninitialized && num)
> +                       ext4_ext_mark_uninitialized(ex);
> +
> +               err = ext4_ext_dirty(handle, inode, path + depth);
> +               if (err)
> +                       goto out;
> +
> +               ext_debug("new extent: %u:%u:%llu\n", block, num,
> +                               ext4_ext_pblock(ex));
> +
> +               if (num == 0) {
> +
> +                       /*
> +                        * For hole punching, we need to scoot all the
> +                        * extents up so that we dont have blank extents
> +                        * in the middle
> +                        */
> +                       for(i_ex = ex; i_ex < EXT_LAST_EXTENT(eh); i_ex++){
> +                               memcpy(i_ex, i_ex+1, sizeof(struct ext4_extent));
> +                       }
> +
> +                       /* now get rid of the extent at the end  */
> +                       memset(i_ex, 0, sizeof(struct ext4_extent));
> +
> +                       le16_add_cpu(&eh->eh_entries, -1);
> +               }
> +
> +               ex--;
> +               ex_ee_block = le32_to_cpu(ex->ee_block);
> +               ex_ee_len = ext4_ext_get_actual_len(ex);
> +
> +       }
> +
> +       if (correct_index && eh->eh_entries)
> +               err = ext4_ext_correct_indexes(handle, inode, path);
> +
> +       /*
> +        * If this leaf is free, then we should
> +        * remove it from index block above
> +        */
> +       if (err == 0 && eh->eh_entries == 0 && path[depth].p_bh != NULL)
> +               err = ext4_ext_rm_idx(handle, inode, path + depth);
> +
> +out:
> +       return err;
> +}
> +
> +/*
>  * ext4_ext_more_to_rm:
>  * returns 1 if current index has to be freed (even partial)
>  */
> @@ -2421,7 +2637,7 @@ ext4_ext_more_to_rm(struct ext4_ext_path *path)
>        return 1;
>  }
>
> -static int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start)
> +static int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start, ext4_lblk_t stop)
>  {
>        struct super_block *sb = inode->i_sb;
>        int depth = ext_depth(inode);
> @@ -2460,7 +2676,10 @@ again:
>        while (i >= 0 && err == 0) {
>                if (i == depth) {
>                        /* this is leaf block */
> -                       err = ext4_ext_rm_leaf(handle, inode, path, start);
> +                       if(stop == EXT_MAX_BLOCK)
> +                               err = ext4_ext_rm_leaf(handle, inode, path, start);
> +                       else
> +                               err = ext4_ext_rm_leaf_punch_hole(handle, inode, path, start, stop);
>                        /* root level has p_bh == NULL, brelse() eats this */
>                        brelse(path[i].p_bh);
>                        path[i].p_bh = NULL;
> @@ -3627,11 +3846,23 @@ out2:
>        return err ? err : allocated;
>  }
>
> -void ext4_ext_truncate(struct inode *inode)
> +/*
> + * ext4_ext_release_blocks
> + *
> + * Releases the blocks in a file starting at block "start"
> + * and ending at block "stop".  Pass EXT_MAX_BLOCK
> + * for "stop" to just truncate the file to the
> + * "start" block
> + *
> + * @inode: The inode of the file to release blocks from
> + * @start: The starting block of the hole
> + * @stop:  The ending block of the hole
> + *
> + */
> +static void ext4_ext_release_blocks(struct inode *inode, ext4_lblk_t start, ext4_lblk_t stop)
>  {
>        struct address_space *mapping = inode->i_mapping;
>        struct super_block *sb = inode->i_sb;
> -       ext4_lblk_t last_block;
>        handle_t *handle;
>        int err = 0;
>
> @@ -3670,9 +3901,7 @@ void ext4_ext_truncate(struct inode *inode)
>        EXT4_I(inode)->i_disksize = inode->i_size;
>        ext4_mark_inode_dirty(handle, inode);
>
> -       last_block = (inode->i_size + sb->s_blocksize - 1)
> -                       >> EXT4_BLOCK_SIZE_BITS(sb);
> -       err = ext4_ext_remove_space(inode, last_block);
> +       err = ext4_ext_remove_space(inode, start, stop);
>
>        /* In a multi-transaction truncate, we only make the final
>         * transaction synchronous.
> @@ -3698,6 +3927,24 @@ out_stop:
>  }
>
>  /*
> + * ext4_ext_truncate
> + *
> + * Truncate the file to the current i_size
> + *
> + * @inode: The file inode
> + */
> +void ext4_ext_truncate(struct inode *inode)
> +{
> +       struct super_block *sb = inode->i_sb;
> +       ext4_lblk_t last_block;
> +
> +       last_block = (inode->i_size + sb->s_blocksize - 1)
> +                       >> EXT4_BLOCK_SIZE_BITS(sb);
> +
> +       ext4_ext_release_blocks(inode, last_block, EXT_MAX_BLOCK);
> +
> +}
> +/*
>  * ext4_ext_convert_blocks_uninit()
>  * Converts a range of blocks to uninitialized
>  *
> --
> 1.7.1
>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
> the body of a message to majordomo@...r.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>



-- 
Regards
dave

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ