Index: linux-2.6.19.1/Documentation/filesystems/ext2.txt =================================================================== --- linux-2.6.19.1.orig/Documentation/filesystems/ext2.txt 2007-01-11 23:34:01.000000000 +0800 +++ linux-2.6.19.1/Documentation/filesystems/ext2.txt 2007-01-11 23:34:36.000000000 +0800 @@ -58,6 +58,8 @@ 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. Index: linux-2.6.19.1/fs/ext2/balloc.c =================================================================== --- linux-2.6.19.1.orig/fs/ext2/balloc.c 2007-01-11 23:34:47.000000000 +0800 +++ linux-2.6.19.1/fs/ext2/balloc.c 2007-01-11 23:37:12.000000000 +0800 @@ -173,9 +173,28 @@ } } +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; @@ -200,6 +219,9 @@ 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)) / Index: linux-2.6.19.1/fs/ext2/ext2.h =================================================================== --- linux-2.6.19.1.orig/fs/ext2/ext2.h 2007-01-11 23:46:05.000000000 +0800 +++ linux-2.6.19.1/fs/ext2/ext2.h 2007-01-11 23:46:21.000000000 +0800 @@ -94,7 +94,7 @@ 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 *); Index: linux-2.6.19.1/fs/ext2/inode.c =================================================================== --- linux-2.6.19.1.orig/fs/ext2/inode.c 2007-01-11 23:43:02.000000000 +0800 +++ linux-2.6.19.1/fs/ext2/inode.c 2007-01-11 23:51:41.000000000 +0800 @@ -100,7 +100,7 @@ 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 @@ 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 @@ 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 @@ 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 @@ } 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 @@ (__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 Index: linux-2.6.19.1/fs/ext2/super.c =================================================================== --- linux-2.6.19.1.orig/fs/ext2/super.c 2007-01-11 23:37:25.000000000 +0800 +++ linux-2.6.19.1/fs/ext2/super.c 2007-01-11 23:38:40.000000000 +0800 @@ -324,7 +324,7 @@ 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 = { @@ -347,6 +347,7 @@ {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"}, @@ -435,6 +436,9 @@ 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); Index: linux-2.6.19.1/fs/ext2/xattr.c =================================================================== --- linux-2.6.19.1.orig/fs/ext2/xattr.c 2007-01-11 23:38:56.000000000 +0800 +++ linux-2.6.19.1/fs/ext2/xattr.c 2007-01-11 23:42:52.000000000 +0800 @@ -675,7 +675,7 @@ 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; } @@ -714,25 +714,25 @@ 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( @@ -743,8 +743,8 @@ 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: @@ -788,10 +788,10 @@ 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); Index: linux-2.6.19.1/include/linux/ext2_fs.h =================================================================== --- linux-2.6.19.1.orig/include/linux/ext2_fs.h 2007-01-11 23:46:32.000000000 +0800 +++ linux-2.6.19.1/include/linux/ext2_fs.h 2007-01-11 23:47:04.000000000 +0800 @@ -314,6 +314,7 @@ #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 */