Re: PATCH - ext2fs privacy (i.e. secure deletion) patch

From: the grugq
Date: Thu Feb 05 2004 - 19:06:03 EST


Ok, I've update the secure delete patch to now check

is_secure_delete(inode)

to see if either the chattr +s bit is set, or if the superblock has EXT2FS_MOUNT_SECRM (which I've added) set.

I believe I've correctly modified the super.c to support a "secrm" mount option, but I haven't tested it so I can't say for sure. That is to say it compiles, but I haven't tried to run it. This rev of the patch is to see if the direction (and logic) of the secure delete functionality is in line with what people are thinking.

There is some complexity around directory files. If a directory file has chattr +s set, then any directory entry within that file will be erased when the entry is removed. If a file has chattr +s set, or the fs was mounted with SECRM then the directory entry is removed. I would like people to pay particular attention to this part of the patch, its the area I'm least confident with.

A directory entry's rec_len is preserved, in case we are removing the first entry for a directory block.

The rest of the patch is basicly unchanged from the previous patchs. I haven't done a patch for ext3, when this one is agreed on then I'll it port acroos to ext3.


peace,

--gq --- linux-2.6.2/include/linux/ext2_fs.h 2004-02-04 03:43:12.000000000 +0000
+++ linux-2.6.2/include/linux/ext2_fs.h-secrm 2004-02-05 22:08:59.000000000 +0000
@@ -310,6 +310,7 @@
#define EXT2_MOUNT_MINIX_DF 0x0080 /* Mimics the Minix statfs */
#define EXT2_MOUNT_NOBH 0x0100 /* No buffer_heads */
#define EXT2_MOUNT_NO_UID32 0x0200 /* Disable 32-bit UIDs */
+#define EXT2_MOUNT_SECRM 0x0400 /* Securely delete files */
#define EXT2_MOUNT_XATTR_USER 0x4000 /* Extended user attributes */
#define EXT2_MOUNT_POSIX_ACL 0x8000 /* POSIX Access Control Lists */

--- linux-2.6.2/fs/ext2/balloc.c 2004-02-04 03:43:42.000000000 +0000
+++ linux-2.6.2/fs/ext2-secrm/balloc.c 2004-02-05 21:02:10.000000000 +0000
@@ -173,6 +173,21 @@
}
}

+static inline void delete_block(struct super_block *sb, unsigned long block)
+{
+ struct buffer_head * bh;
+
+ bh = sb_getblk(sb, block);
+
+ memset(bh->b_data, 0, bh->b_size);
+
+ mark_buffer_dirty(bh);
+ wait_on_buffer(bh); /* XXX is this necessary? */
+ brelse(bh);
+
+ return;
+}
+
/* Free given blocks, update quota and i_blocks field */
void ext2_free_blocks (struct inode * inode, unsigned long block,
unsigned long count)
@@ -240,8 +255,11 @@
ext2_error (sb, "ext2_free_blocks",
"bit already cleared for block %lu",
block + i);
- else
+ else {
+ if (is_secure_delete(inode))
+ delete_block(inode->i_sb, block + i);
group_freed++;
+ }
}

mark_buffer_dirty(bitmap_bh);
--- linux-2.6.2/fs/ext2/dir.c 2004-02-04 03:43:56.000000000 +0000
+++ linux-2.6.2/fs/ext2-secrm/dir.c 2004-02-05 22:01:00.000000000 +0000
@@ -530,6 +530,7 @@
{
struct address_space *mapping = page->mapping;
struct inode *inode = mapping->host;
+ struct inode *dino;
char *kaddr = page_address(page);
unsigned from = ((char*)dir - kaddr) & ~(ext2_chunk_size(inode)-1);
unsigned to = ((char*)dir - kaddr) + le16_to_cpu(dir->rec_len);
@@ -555,9 +556,24 @@
BUG();
if (pde)
pde->rec_len = cpu_to_le16(to-from);
- dir->inode = 0;
+
+ dino = iget(inode->i_sb, dir->inode);
+ if (!dino || (!is_secure_delete(dino) && !is_secure_delete(inode))) {
+ dir->inode = 0;
+ inode->i_ctime = inode->i_mtime = CURRENT_TIME;
+ } else {
+ unsigned short rec_len = dir->rec_len;
+
+ memset(dir, 0, dir->rec_len);
+ dir->rec_len = rec_len;
+ }
+
+ if (dino)
+ iput(dino);
+
err = ext2_commit_chunk(page, from, to);
- inode->i_ctime = inode->i_mtime = CURRENT_TIME;
+
+ /* XXX should this be in the if (!is_secure_delete()) above? */
EXT2_I(inode)->i_flags &= ~EXT2_BTREE_FL;
mark_inode_dirty(inode);
out:
--- linux-2.6.2/fs/ext2/ext2.h 2004-02-05 20:46:40.000000000 +0000
+++ linux-2.6.2/fs/ext2-secrm/ext2.h 2004-02-05 20:45:25.000000000 +0000
@@ -79,6 +79,16 @@
return container_of(inode, struct ext2_inode_info, vfs_inode);
}

+static inline int is_secure_delete(struct inode *inode)
+{
+ /* expanded for readibility */
+ if (EXT2_I(inode)->i_flags & EXT2_SECRM_FL)
+ return 1;
+ if (test_opt(inode->i_sb, SECRM))
+ return 1;
+ return 0;
+}
+
/* balloc.c */
extern int ext2_bg_has_super(struct super_block *sb, int group);
extern unsigned long ext2_bg_num_gdb(struct super_block *sb, int group);
--- linux-2.6.2/fs/ext2/inode.c 2004-02-04 03:43:09.000000000 +0000
+++ linux-2.6.2/fs/ext2-secrm/inode.c 2004-02-05 22:18:56.000000000 +0000
@@ -64,6 +64,36 @@
ext2_discard_prealloc(inode);
}

+static inline void delete_inode(struct inode *inode)
+{
+ if (!is_secure_delete(inode))
+ EXT2_I(inode)->i_dtime = get_seconds();
+ else {
+ inode->i_mode = 0;
+ inode->i_uid = 0;
+ inode->i_gid = 0;
+ inode->i_nlink = 0;
+ inode->i_atime.tv_sec = 0;
+ inode->i_atime.tv_nsec = 0;
+ inode->i_ctime.tv_sec = 0;
+ inode->i_ctime.tv_nsec = 0;
+ inode->i_mtime.tv_sec = 0;
+ inode->i_mtime.tv_nsec = 0;
+ EXT2_I(inode)->i_dtime = 0;
+ EXT2_I(inode)->i_faddr = 0;
+ EXT2_I(inode)->i_frag_no = 0;
+ EXT2_I(inode)->i_frag_size = 0;
+ EXT2_I(inode)->i_file_acl = 0;
+ inode->i_generation = 0;
+ }
+}
+
+static inline void delete_blocks(struct inode *inode)
+{
+ inode->i_blocks = 0;
+ memset(EXT2_I(inode)->i_data, 0, sizeof(EXT2_I(inode)->i_data));
+}
+
/*
* Called at the last iput() if i_nlink is zero.
*/
@@ -71,13 +101,21 @@
{
if (is_bad_inode(inode))
goto no_delete;
- EXT2_I(inode)->i_dtime = get_seconds();
+ delete_inode(inode);
mark_inode_dirty(inode);
ext2_update_inode(inode, inode_needs_sync(inode));

inode->i_size = 0;
if (inode->i_blocks)
ext2_truncate (inode);
+
+ if (is_secure_delete(inode)) {
+ EXT2_I(inode)->i_flags = 0;
+
+ delete_blocks(inode);
+ mark_inode_dirty(inode);
+ ext2_update_inode(inode, inode_needs_sync(inode));
+ }
ext2_free_inode (inode);

return;
--- linux-2.6.2/fs/ext2/super.c 2004-02-04 03:44:04.000000000 +0000
+++ linux-2.6.2/fs/ext2-secrm/super.c 2004-02-05 22:04:17.000000000 +0000
@@ -270,7 +270,7 @@
Opt_bsd_df, Opt_minix_df, Opt_grpid, Opt_nogrpid,
Opt_resgid, Opt_resuid, Opt_sb, Opt_err_cont, Opt_err_panic, Opt_err_ro,
Opt_nouid32, Opt_check, Opt_nocheck, Opt_debug, Opt_oldalloc, Opt_orlov, Opt_nobh,
- Opt_user_xattr, Opt_nouser_xattr, Opt_acl, Opt_noacl,
+ Opt_user_xattr, Opt_nouser_xattr, Opt_acl, Opt_noacl, Opt_secrm,
Opt_ignore, Opt_err,
};

@@ -299,6 +299,7 @@
{Opt_nouser_xattr, "nouser_xattr"},
{Opt_acl, "acl"},
{Opt_noacl, "noacl"},
+ {Opt_secrm, "secrm"},
{Opt_ignore, "grpquota"},
{Opt_ignore, "noquota"},
{Opt_ignore, "quota"},
@@ -410,6 +411,9 @@
printk("EXT2 (no)acl options not supported\n");
break;
#endif
+ case Opt_secrm:
+ set_opt(sbi->s_mount_opt, SECRM);
+ break;
case Opt_ignore:
break;
default: