[CentOS] [PATCH] ext2: zero freed blocks

Fri Sep 8 09:04:05 UTC 2006
Ron Yorston <rmy at tigress.co.uk>

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 */