lkml.org 
[lkml]   [1996]   [Jun]   [23]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
SubjectRe: Drawbacks of implementing undelete entirely in user space
Date
From


There has been a discussion about wether undelete should be
implemented in userspace or in the kernel.

To "show" that we've been wasting more time discussing what to
do, than that it would cost to implement it, I implemented the
kernel variant of the undelete feature.

Features:

- Only works when a .wastebasket is in the root of the partition.
- Only "root" can now delete files.
- If a user has the right to delete a file, it will be moved
to the wastebasket regardless of the permissions of the wastebasket.
If you don't want security holes to others, you should
make the wastebasket mode 700.

I moved a few functions around. Therefore they are replicated
in the patch. The actual patch is only a few lines.


I don't really know what functions "silently" iput inodes I pass
them. I think I've got things right. Anybody care to comment?
(is it ok to do "inode->i_count++" if you want to keep an inode
that a called function happens to free?)


Roger.

Here is the patch:
--------------------------------------------------------------
--- namei.c Mon Dec 11 05:56:35 1995
+++ /usr/src/linux/fs/ext2/namei.c Sun Jun 23 17:37:45 1996
@@ -24,6 +24,7 @@
#include <linux/string.h>
#include <linux/locks.h>

+#define CONFIG_EXT2_UNDELETE
/*
* comment out this line if you want names > EXT2_NAME_LEN chars to be
* truncated. Else they will be disallowed.
@@ -687,201 +688,6 @@
return retval;
}

-int ext2_unlink (struct inode * dir, const char * name, int len)
-{
- int retval;
- struct inode * inode;
- struct buffer_head * bh;
- struct ext2_dir_entry * de;
-
-repeat:
- if (!dir)
- return -ENOENT;
- retval = -ENOENT;
- inode = NULL;
- bh = ext2_find_entry (dir, name, len, &de);
- if (!bh)
- goto end_unlink;
- if (!(inode = iget (dir->i_sb, de->inode)))
- goto end_unlink;
- if (inode->i_sb->dq_op)
- inode->i_sb->dq_op->initialize (inode, -1);
- retval = -EPERM;
- if (S_ISDIR(inode->i_mode))
- goto end_unlink;
- if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
- goto end_unlink;
- if (de->inode != inode->i_ino) {
- iput(inode);
- brelse(bh);
- current->counter = 0;
- schedule();
- goto repeat;
- }
- if ((dir->i_mode & S_ISVTX) && !fsuser() &&
- current->fsuid != inode->i_uid &&
- current->fsuid != dir->i_uid)
- goto end_unlink;
- if (!inode->i_nlink) {
- ext2_warning (inode->i_sb, "ext2_unlink",
- "Deleting nonexistent file (%lu), %d",
- inode->i_ino, inode->i_nlink);
- inode->i_nlink = 1;
- }
- retval = ext2_delete_entry (de, bh);
- if (retval)
- goto end_unlink;
- dir->i_version = ++event;
- mark_buffer_dirty(bh, 1);
- if (IS_SYNC(dir)) {
- ll_rw_block (WRITE, 1, &bh);
- wait_on_buffer (bh);
- }
- dir->i_ctime = dir->i_mtime = CURRENT_TIME;
- dir->i_dirt = 1;
- inode->i_nlink--;
- inode->i_dirt = 1;
- inode->i_ctime = dir->i_ctime;
- retval = 0;
-end_unlink:
- brelse (bh);
- iput (inode);
- iput (dir);
- return retval;
-}
-
-int ext2_symlink (struct inode * dir, const char * name, int len,
- const char * symname)
-{
- struct ext2_dir_entry * de;
- struct inode * inode = NULL;
- struct buffer_head * bh = NULL, * name_block = NULL;
- char * link;
- int i, err;
- int l;
- char c;
-
- if (!(inode = ext2_new_inode (dir, S_IFLNK, &err))) {
- iput (dir);
- return err;
- }
- inode->i_mode = S_IFLNK | S_IRWXUGO;
- inode->i_op = &ext2_symlink_inode_operations;
- for (l = 0; l < inode->i_sb->s_blocksize - 1 &&
- symname [l]; l++)
- ;
- if (l >= sizeof (inode->u.ext2_i.i_data)) {
-
- ext2_debug ("l=%d, normal symlink\n", l);
-
- name_block = ext2_bread (inode, 0, 1, &err);
- if (!name_block) {
- iput (dir);
- inode->i_nlink--;
- inode->i_dirt = 1;
- iput (inode);
- return err;
- }
- link = name_block->b_data;
- } else {
- link = (char *) inode->u.ext2_i.i_data;
-
- ext2_debug ("l=%d, fast symlink\n", l);
-
- }
- i = 0;
- while (i < inode->i_sb->s_blocksize - 1 && (c = *(symname++)))
- link[i++] = c;
- link[i] = 0;
- if (name_block) {
- mark_buffer_dirty(name_block, 1);
- brelse (name_block);
- }
- inode->i_size = i;
- inode->i_dirt = 1;
-
- bh = ext2_find_entry (dir, name, len, &de);
- if (bh) {
- inode->i_nlink--;
- inode->i_dirt = 1;
- iput (inode);
- brelse (bh);
- iput (dir);
- return -EEXIST;
- }
- bh = ext2_add_entry (dir, name, len, &de, &err);
- if (!bh) {
- inode->i_nlink--;
- inode->i_dirt = 1;
- iput (inode);
- iput (dir);
- return err;
- }
- de->inode = inode->i_ino;
- dir->i_version = ++event;
- dcache_add(dir, de->name, de->name_len, de->inode);
- mark_buffer_dirty(bh, 1);
- if (IS_SYNC(dir)) {
- ll_rw_block (WRITE, 1, &bh);
- wait_on_buffer (bh);
- }
- brelse (bh);
- iput (dir);
- iput (inode);
- return 0;
-}
-
-int ext2_link (struct inode * oldinode, struct inode * dir,
- const char * name, int len)
-{
- struct ext2_dir_entry * de;
- struct buffer_head * bh;
- int err;
-
- if (S_ISDIR(oldinode->i_mode)) {
- iput (oldinode);
- iput (dir);
- return -EPERM;
- }
- if (IS_APPEND(oldinode) || IS_IMMUTABLE(oldinode)) {
- iput (oldinode);
- iput (dir);
- return -EPERM;
- }
- if (oldinode->i_nlink >= EXT2_LINK_MAX) {
- iput (oldinode);
- iput (dir);
- return -EMLINK;
- }
- bh = ext2_find_entry (dir, name, len, &de);
- if (bh) {
- brelse (bh);
- iput (dir);
- iput (oldinode);
- return -EEXIST;
- }
- bh = ext2_add_entry (dir, name, len, &de, &err);
- if (!bh) {
- iput (dir);
- iput (oldinode);
- return err;
- }
- de->inode = oldinode->i_ino;
- dir->i_version = ++event;
- dcache_add(dir, de->name, de->name_len, de->inode);
- mark_buffer_dirty(bh, 1);
- if (IS_SYNC(dir)) {
- ll_rw_block (WRITE, 1, &bh);
- wait_on_buffer (bh);
- }
- brelse (bh);
- iput (dir);
- oldinode->i_nlink++;
- oldinode->i_ctime = CURRENT_TIME;
- oldinode->i_dirt = 1;
- iput (oldinode);
- return 0;
-}

static int subdir (struct inode * new_inode, struct inode * old_inode)
{
@@ -1085,6 +891,250 @@
return retval;
}

+
+int ext2_unlink (struct inode * dir, const char * name, int len)
+{
+ int retval;
+ struct inode * inode;
+ struct buffer_head * bh;
+ struct ext2_dir_entry * de;
+
+repeat:
+ if (!dir)
+ return -ENOENT;
+ retval = -ENOENT;
+ inode = NULL;
+ bh = ext2_find_entry (dir, name, len, &de);
+ if (!bh)
+ goto end_unlink;
+ if (!(inode = iget (dir->i_sb, de->inode)))
+ goto end_unlink;
+ if (inode->i_sb->dq_op)
+ inode->i_sb->dq_op->initialize (inode, -1);
+ retval = -EPERM;
+ if (S_ISDIR(inode->i_mode))
+ goto end_unlink;
+ if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
+ goto end_unlink;
+ if (de->inode != inode->i_ino) {
+ iput(inode);
+ brelse(bh);
+ current->counter = 0;
+ schedule();
+ goto repeat;
+ }
+ if ((dir->i_mode & S_ISVTX) && !fsuser() &&
+ current->fsuid != inode->i_uid &&
+ current->fsuid != dir->i_uid)
+ goto end_unlink;
+ if (!inode->i_nlink) {
+ ext2_warning (inode->i_sb, "ext2_unlink",
+ "Deleting nonexistent file (%lu), %d",
+ inode->i_ino, inode->i_nlink);
+ inode->i_nlink = 1;
+ }
+#ifdef CONFIG_EXT2_UNDELETE
+ if (!fsuser ()) { /* "root" uses the old unlink */
+ if (inode->i_sb) {
+ struct inode *wb,*tmp;
+ const char *new_name;
+ char buf[EXT2_NAME_LEN];
+ int new_len;
+ int version;
+
+ inode->i_sb->s_mounted->i_count++;
+ if (ext2_lookup (inode->i_sb->s_mounted,".wastebasket",12,&wb) == 0) {
+ printk ("inode_c=%d, dir_c=%d,wb_c=%d\n",inode->i_count,dir->i_count,wb->i_count);
+ new_name=name;
+ new_len = len;
+ version = 0;
+ wb->i_count++;
+ while (ext2_lookup (wb,new_name,new_len,&tmp) == 0) {
+ iput (tmp);
+ sprintf (buf,"%s_%d",name,version++);
+ new_name = buf;
+ new_len = strlen (buf);
+ wb->i_count++;
+ }
+
+ printk ("moveing %s to wastebasket as %s.\n",name,new_name);
+ while (dir->i_sb->u.ext2_sb.s_rename_lock)
+ sleep_on (&dir->i_sb->u.ext2_sb.s_rename_wait);
+ dir->i_sb->u.ext2_sb.s_rename_lock = 1;
+
+ retval = do_ext2_rename (dir, name, len, wb, new_name, new_len);
+
+ dir->i_sb->u.ext2_sb.s_rename_lock = 0;
+ wake_up (&dir->i_sb->u.ext2_sb.s_rename_wait);
+ printk ("inode_c=%d, dir_c=%d,wb_c=%d\n",inode->i_count,dir->i_count,wb->i_count);
+ brelse (bh);
+ iput (inode);
+ return retval;
+#ifdef DEBUG_UNDELETE
+ } else {
+ printk ("No wastebasket. Old unlink for %s....\n",name);
+#endif
+ }
+ } else {
+ printk ("Warning: Undelete can't find a superblock for inode %ld\n"
+ "Deleting file normally.\n",inode->i_ino);
+ }
+ }
+#endif
+ retval = ext2_delete_entry (de, bh);
+ if (retval)
+ goto end_unlink;
+ dir->i_version = ++event;
+ mark_buffer_dirty(bh, 1);
+ if (IS_SYNC(dir)) {
+ ll_rw_block (WRITE, 1, &bh);
+ wait_on_buffer (bh);
+ }
+ dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+ dir->i_dirt = 1;
+ inode->i_nlink--;
+ inode->i_dirt = 1;
+ inode->i_ctime = dir->i_ctime;
+ retval = 0;
+end_unlink:
+ brelse (bh);
+ iput (inode);
+ iput (dir);
+ return retval;
+}
+
+int ext2_symlink (struct inode * dir, const char * name, int len,
+ const char * symname)
+{
+ struct ext2_dir_entry * de;
+ struct inode * inode = NULL;
+ struct buffer_head * bh = NULL, * name_block = NULL;
+ char * link;
+ int i, err;
+ int l;
+ char c;
+
+ if (!(inode = ext2_new_inode (dir, S_IFLNK, &err))) {
+ iput (dir);
+ return err;
+ }
+ inode->i_mode = S_IFLNK | S_IRWXUGO;
+ inode->i_op = &ext2_symlink_inode_operations;
+ for (l = 0; l < inode->i_sb->s_blocksize - 1 &&
+ symname [l]; l++)
+ ;
+ if (l >= sizeof (inode->u.ext2_i.i_data)) {
+
+ ext2_debug ("l=%d, normal symlink\n", l);
+
+ name_block = ext2_bread (inode, 0, 1, &err);
+ if (!name_block) {
+ iput (dir);
+ inode->i_nlink--;
+ inode->i_dirt = 1;
+ iput (inode);
+ return err;
+ }
+ link = name_block->b_data;
+ } else {
+ link = (char *) inode->u.ext2_i.i_data;
+
+ ext2_debug ("l=%d, fast symlink\n", l);
+
+ }
+ i = 0;
+ while (i < inode->i_sb->s_blocksize - 1 && (c = *(symname++)))
+ link[i++] = c;
+ link[i] = 0;
+ if (name_block) {
+ mark_buffer_dirty(name_block, 1);
+ brelse (name_block);
+ }
+ inode->i_size = i;
+ inode->i_dirt = 1;
+
+ bh = ext2_find_entry (dir, name, len, &de);
+ if (bh) {
+ inode->i_nlink--;
+ inode->i_dirt = 1;
+ iput (inode);
+ brelse (bh);
+ iput (dir);
+ return -EEXIST;
+ }
+ bh = ext2_add_entry (dir, name, len, &de, &err);
+ if (!bh) {
+ inode->i_nlink--;
+ inode->i_dirt = 1;
+ iput (inode);
+ iput (dir);
+ return err;
+ }
+ de->inode = inode->i_ino;
+ dir->i_version = ++event;
+ dcache_add(dir, de->name, de->name_len, de->inode);
+ mark_buffer_dirty(bh, 1);
+ if (IS_SYNC(dir)) {
+ ll_rw_block (WRITE, 1, &bh);
+ wait_on_buffer (bh);
+ }
+ brelse (bh);
+ iput (dir);
+ iput (inode);
+ return 0;
+}
+
+int ext2_link (struct inode * oldinode, struct inode * dir,
+ const char * name, int len)
+{
+ struct ext2_dir_entry * de;
+ struct buffer_head * bh;
+ int err;
+
+ if (S_ISDIR(oldinode->i_mode)) {
+ iput (oldinode);
+ iput (dir);
+ return -EPERM;
+ }
+ if (IS_APPEND(oldinode) || IS_IMMUTABLE(oldinode)) {
+ iput (oldinode);
+ iput (dir);
+ return -EPERM;
+ }
+ if (oldinode->i_nlink >= EXT2_LINK_MAX) {
+ iput (oldinode);
+ iput (dir);
+ return -EMLINK;
+ }
+ bh = ext2_find_entry (dir, name, len, &de);
+ if (bh) {
+ brelse (bh);
+ iput (dir);
+ iput (oldinode);
+ return -EEXIST;
+ }
+ bh = ext2_add_entry (dir, name, len, &de, &err);
+ if (!bh) {
+ iput (dir);
+ iput (oldinode);
+ return err;
+ }
+ de->inode = oldinode->i_ino;
+ dir->i_version = ++event;
+ dcache_add(dir, de->name, de->name_len, de->inode);
+ mark_buffer_dirty(bh, 1);
+ if (IS_SYNC(dir)) {
+ ll_rw_block (WRITE, 1, &bh);
+ wait_on_buffer (bh);
+ }
+ brelse (bh);
+ iput (dir);
+ oldinode->i_nlink++;
+ oldinode->i_ctime = CURRENT_TIME;
+ oldinode->i_dirt = 1;
+ iput (oldinode);
+ return 0;
+}
/*
* Ok, rename also locks out other renames, as they can change the parent of
* a directory, and we don't want any races. Other races are checked for by



--
** Q: What's the difference between MicroSoft Windows and a virus? **
** A: Apart from the fact that virusses install easier, none. **
** EMail: R.E.Wolff@et.tudelft.nl * Tel +31-15-2783643 or +31-15-2137459 **
*** <a href="http://einstein.et.tudelft.nl/~wolff/">my own homepage</a> ***


\
 
 \ /
  Last update: 2005-03-22 13:37    [W:0.098 / U:0.588 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site