Add a zerofree mount option to the ext2 filesystem. This causes freed blocks to be filled with zeros. ext2_zero_blocks has an additional argument to specify whether or not zeroing is required: there's no point in zeroing blocks that have just come from the free list. Some rerrangement of code in xattr.c is required to ensure that ext2_zero_blocks is never called with a locked buffer. Signed-off-by: Ron Yorston <rmy at tigress.co.uk> --- --- linux-2.6.17/Documentation/filesystems/ext2.txt.zerofree2 2006-06-18 02:49:35.000000000 +0100 +++ linux-2.6.17/Documentation/filesystems/ext2.txt 2006-08-25 20:08:06.000000000 +0100 @@ -58,6 +58,8 @@ nobh Do not attach buffer_heads to fi xip Use execute in place (no caching) if possible +zerofree Zero data blocks when they are freed. + grpquota,noquota,quota,usrquota Quota options are silently ignored by ext2. --- linux-2.6.17/fs/ext2/balloc.c.zerofree2 2006-06-18 02:49:35.000000000 +0100 +++ linux-2.6.17/fs/ext2/balloc.c 2006-08-25 20:08:26.000000000 +0100 @@ -174,9 +174,28 @@ static void group_release_blocks(struct } } +static void ext2_zero_blocks(struct super_block *sb, unsigned long block, + unsigned long count) +{ + unsigned long i; + struct buffer_head * bh; + + for (i = 0; i < count; i++) { + bh = sb_getblk(sb, block+i); + if (!bh) + continue; + + lock_buffer(bh); + memset(bh->b_data, 0, bh->b_size); + mark_buffer_dirty(bh); + unlock_buffer(bh); + brelse(bh); + } +} + /* Free given blocks, update quota and i_blocks field */ void ext2_free_blocks (struct inode * inode, unsigned long block, - unsigned long count) + unsigned long count, int zero) { struct buffer_head *bitmap_bh = NULL; struct buffer_head * bh2; @@ -201,6 +220,9 @@ void ext2_free_blocks (struct inode * in ext2_debug ("freeing block(s) %lu-%lu\n", block, block + count - 1); + if (test_opt(sb, ZEROFREE) && zero) + ext2_zero_blocks(sb, block, count); + do_more: overflow = 0; block_group = (block - le32_to_cpu(es->s_first_data_block)) / --- linux-2.6.17/fs/ext2/super.c.zerofree2 2006-06-18 02:49:35.000000000 +0100 +++ linux-2.6.17/fs/ext2/super.c 2006-08-25 20:08:06.000000000 +0100 @@ -287,7 +287,7 @@ enum { Opt_err_ro, Opt_nouid32, Opt_nocheck, Opt_debug, Opt_oldalloc, Opt_orlov, Opt_nobh, Opt_user_xattr, Opt_nouser_xattr, Opt_acl, Opt_noacl, Opt_xip, Opt_ignore, Opt_err, Opt_quota, - Opt_usrquota, Opt_grpquota + Opt_usrquota, Opt_grpquota, Opt_zerofree }; static match_table_t tokens = { @@ -310,6 +310,7 @@ static match_table_t tokens = { {Opt_oldalloc, "oldalloc"}, {Opt_orlov, "orlov"}, {Opt_nobh, "nobh"}, + {Opt_zerofree, "zerofree"}, {Opt_user_xattr, "user_xattr"}, {Opt_nouser_xattr, "nouser_xattr"}, {Opt_acl, "acl"}, @@ -393,6 +394,9 @@ static int parse_options (char * options case Opt_nobh: set_opt (sbi->s_mount_opt, NOBH); break; + case Opt_zerofree: + set_opt (sbi->s_mount_opt, ZEROFREE); + break; #ifdef CONFIG_EXT2_FS_XATTR case Opt_user_xattr: set_opt (sbi->s_mount_opt, XATTR_USER); --- linux-2.6.17/fs/ext2/xattr.c.zerofree2 2006-06-18 02:49:35.000000000 +0100 +++ linux-2.6.17/fs/ext2/xattr.c 2006-08-25 20:08:06.000000000 +0100 @@ -676,7 +676,7 @@ ext2_xattr_set2(struct inode *inode, str new_bh = sb_getblk(sb, block); if (!new_bh) { - ext2_free_blocks(inode, block, 1); + ext2_free_blocks(inode, block, 1, 0); error = -EIO; goto cleanup; } @@ -715,25 +715,26 @@ ext2_xattr_set2(struct inode *inode, str error = 0; if (old_bh && old_bh != new_bh) { + unsigned long block = old_bh->b_blocknr; struct mb_cache_entry *ce; /* * If there was an old block and we are no longer using it, * release the old block. */ - ce = mb_cache_entry_get(ext2_xattr_cache, old_bh->b_bdev, - old_bh->b_blocknr); + ce = mb_cache_entry_get(ext2_xattr_cache, old_bh->b_bdev, block); lock_buffer(old_bh); if (HDR(old_bh)->h_refcount == cpu_to_le32(1)) { /* Free the old block. */ if (ce) mb_cache_entry_free(ce); ea_bdebug(old_bh, "freeing"); - ext2_free_blocks(inode, old_bh->b_blocknr, 1); + unlock_buffer(old_bh); /* We let our caller release old_bh, so we * need to duplicate the buffer before. */ get_bh(old_bh); bforget(old_bh); + ext2_free_blocks(inode, block, 1, 1); } else { /* Decrement the refcount only. */ HDR(old_bh)->h_refcount = cpu_to_le32( @@ -744,8 +745,8 @@ ext2_xattr_set2(struct inode *inode, str mark_buffer_dirty(old_bh); ea_bdebug(old_bh, "refcount now=%d", le32_to_cpu(HDR(old_bh)->h_refcount)); + unlock_buffer(old_bh); } - unlock_buffer(old_bh); } cleanup: @@ -789,10 +790,10 @@ ext2_xattr_delete_inode(struct inode *in if (HDR(bh)->h_refcount == cpu_to_le32(1)) { if (ce) mb_cache_entry_free(ce); - ext2_free_blocks(inode, EXT2_I(inode)->i_file_acl, 1); + unlock_buffer(bh); get_bh(bh); bforget(bh); - unlock_buffer(bh); + ext2_free_blocks(inode, EXT2_I(inode)->i_file_acl, 1, 1); } else { HDR(bh)->h_refcount = cpu_to_le32( le32_to_cpu(HDR(bh)->h_refcount) - 1); --- linux-2.6.17/fs/ext2/inode.c.zerofree2 2006-06-18 02:49:35.000000000 +0100 +++ linux-2.6.17/fs/ext2/inode.c 2006-08-25 20:08:06.000000000 +0100 @@ -100,7 +100,7 @@ void ext2_discard_prealloc (struct inode ei->i_prealloc_count = 0; ei->i_prealloc_block = 0; write_unlock(&ei->i_meta_lock); - ext2_free_blocks (inode, block, total); + ext2_free_blocks (inode, block, total, 0); return; } else write_unlock(&ei->i_meta_lock); @@ -467,7 +467,7 @@ static int ext2_alloc_branch(struct inod for (i = 1; i < n; i++) bforget(branch[i].bh); for (i = 0; i < n; i++) - ext2_free_blocks(inode, le32_to_cpu(branch[i].key), 1); + ext2_free_blocks(inode, le32_to_cpu(branch[i].key), 1, 0); return err; } @@ -527,7 +527,7 @@ changed: for (i = 1; i < num; i++) bforget(where[i].bh); for (i = 0; i < num; i++) - ext2_free_blocks(inode, le32_to_cpu(where[i].key), 1); + ext2_free_blocks(inode, le32_to_cpu(where[i].key), 1, 1); return -EAGAIN; } @@ -837,7 +837,7 @@ static inline void ext2_free_data(struct count++; else { mark_inode_dirty(inode); - ext2_free_blocks (inode, block_to_free, count); + ext2_free_blocks (inode, block_to_free, count, 1); free_this: block_to_free = nr; count = 1; @@ -846,7 +846,7 @@ static inline void ext2_free_data(struct } if (count > 0) { mark_inode_dirty(inode); - ext2_free_blocks (inode, block_to_free, count); + ext2_free_blocks (inode, block_to_free, count, 1); } } @@ -889,7 +889,7 @@ static void ext2_free_branches(struct in (__le32*)bh->b_data + addr_per_block, depth); bforget(bh); - ext2_free_blocks(inode, nr, 1); + ext2_free_blocks(inode, nr, 1, 1); mark_inode_dirty(inode); } } else --- linux-2.6.17/fs/ext2/ext2.h.zerofree2 2006-06-18 02:49:35.000000000 +0100 +++ linux-2.6.17/fs/ext2/ext2.h 2006-08-25 20:08:06.000000000 +0100 @@ -94,7 +94,7 @@ extern unsigned long ext2_bg_num_gdb(str extern int ext2_new_block (struct inode *, unsigned long, __u32 *, __u32 *, int *); extern void ext2_free_blocks (struct inode *, unsigned long, - unsigned long); + unsigned long, int); extern unsigned long ext2_count_free_blocks (struct super_block *); extern unsigned long ext2_count_dirs (struct super_block *); extern void ext2_check_blocks_bitmap (struct super_block *); --- linux-2.6.17/include/linux/ext2_fs.h.zerofree2 2006-06-18 02:49:35.000000000 +0100 +++ linux-2.6.17/include/linux/ext2_fs.h 2006-08-25 20:08:06.000000000 +0100 @@ -310,6 +310,7 @@ struct ext2_inode { #define EXT2_MOUNT_MINIX_DF 0x000080 /* Mimics the Minix statfs */ #define EXT2_MOUNT_NOBH 0x000100 /* No buffer_heads */ #define EXT2_MOUNT_NO_UID32 0x000200 /* Disable 32-bit UIDs */ +#define EXT2_MOUNT_ZEROFREE 0x000400 /* Zero freed blocks */ #define EXT2_MOUNT_XATTR_USER 0x004000 /* Extended user attributes */ #define EXT2_MOUNT_POSIX_ACL 0x008000 /* POSIX Access Control Lists */ #define EXT2_MOUNT_XIP 0x010000 /* Execute in place */