diff -ruN linux-2.6.16.2.orig/fs/Kconfig linux-2.6.16.2/fs/Kconfig
--- linux-2.6.16.2.orig/fs/Kconfig 2006-04-07 18:56:47.000000000 +0200
+++ linux-2.6.16.2/fs/Kconfig 2006-04-10 13:27:10.000000000 +0200
@@ -438,6 +438,32 @@
depends on XFS_QUOTA || QUOTA
default y
+config DARE
+ bool "Data Recovery System - DARE"
+ help
+ DARE is a small Linux kernel patch to VFS (Virtual Filesystem) which
+ transparently moves files into a special directory on each filesystem
+ instead of removing them. It works similar like Novell Netware
+ Data Recovery or Windows Recycle Bin.
+
+ Deleted files are moved into /deleted directory on each mounted
+ filesystem. Deleted files are grouped into subdirectories by N hours.
+
+ If you want to use this feature for getting ability to undelete files
+ you need answer Y here and create directory 'deleted' accessible only
+ for user 'root' at root directory of every mounted filesystem which
+ you want to have undelete ability.
+
+ You also need user space utility for undeleting files and user space
+ daemon which periodicaly checks free space on mounted filesystems and
+ removes older files from 'deleted' directory if needed.
+ Those utilities can be found at .
+
+config DARE_HOURS
+ int "Group deleted files by N hours"
+ depends on DARE
+ default 1
+
config DNOTIFY
bool "Dnotify support" if EMBEDDED
default y
diff -ruN linux-2.6.16.2.orig/fs/Makefile linux-2.6.16.2/fs/Makefile
--- linux-2.6.16.2.orig/fs/Makefile 2006-04-07 18:56:47.000000000 +0200
+++ linux-2.6.16.2/fs/Makefile 2006-04-10 13:14:32.000000000 +0200
@@ -48,6 +48,8 @@
obj-y += devpts/
obj-$(CONFIG_PROFILING) += dcookies.o
+
+obj-$(CONFIG_DARE) += dare.o
# Do not add any filesystems before this line
obj-$(CONFIG_REISERFS_FS) += reiserfs/
diff -ruN linux-2.6.16.2.orig/fs/dare.c linux-2.6.16.2/fs/dare.c
--- linux-2.6.16.2.orig/fs/dare.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.16.2/fs/dare.c 2006-04-10 13:30:20.000000000 +0200
@@ -0,0 +1,381 @@
+/*
+ * linux/fs/dare.c
+ *
+ * Copyright (C) 2004-2006 by Marek Zelem
+ */
+
+#define DELETED_DIR "deleted"
+// #define CONFIG_DARE_HOURS 1
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+// #include
+#include
+
+char * d_path_relative(struct dentry *dentry, struct vfsmount *vfsmnt,
+ char *buf, int buflen);
+
+static DECLARE_MUTEX(deleted_mutex);
+
+struct vfsmount *find_inode_mnt(struct inode *inode);
+
+static int is_offspring( struct dentry * dir, struct dentry * offspring )
+{
+ /* FIXME: do locking */
+ /* maybe add some depth limitation eg. /deleted/xxx/yyy */
+ while( offspring->d_parent!=NULL && offspring->d_parent!=offspring )
+ {
+ if( offspring->d_parent==dir )
+ return(1);
+ offspring=offspring->d_parent;
+ }
+ return(0);
+}
+
+struct proc_priv {
+ uid_t fsuid;
+ gid_t fsgid;
+ kernel_cap_t cap_effective;
+};
+
+static void save_proc_priv( struct proc_priv *p )
+{
+ p->fsuid=current->fsuid;
+ p->fsgid=current->fsgid;
+ cap_t(p->cap_effective) = cap_t(current->cap_effective);
+}
+
+static void restore_proc_priv( struct proc_priv *p )
+{
+ current->fsuid=p->fsuid;
+ current->fsgid=p->fsgid;
+ cap_t(current->cap_effective) = cap_t(p->cap_effective);
+}
+
+static void set_proc_priv( void )
+{
+ current->fsuid=0;
+ current->fsgid=0;
+ cap_t(current->cap_effective) = to_cap_t(CAP_FS_MASK);
+}
+
+static int write_to_file( struct file *file, char *buf, int len )
+{
+ mm_segment_t old_fs;
+ int result;
+ loff_t pos;
+
+ if( buf==NULL )
+ return(0);
+ old_fs = get_fs();
+ set_fs(get_ds());
+ /* The cast to a user pointer is valid due to the set_fs() */
+ pos=file->f_pos;
+ result = vfs_write(file,buf,len,&pos);
+ file->f_pos=pos;
+ set_fs(old_fs);
+ return(result);
+}
+
+static int write_path_to_file( struct file *file, struct dentry *dentry, struct vfsmount *mnt )
+{ char *fn;
+ char *page = (char *) __get_free_page(GFP_USER);
+
+ if( page!=NULL )
+ {
+ fn=d_path_relative(dentry,mnt,page,PAGE_SIZE);
+ if( fn!=NULL )
+ write_to_file(file,fn,strlen(fn));
+ free_page((unsigned long) page);
+ return(0);
+ }
+ return(0);
+}
+
+/*
+ * dare_move( struct inode *dir, struct dentry * dentry )
+ * Must be called with mutex_lock(&dir->i_mutex);
+ */
+int dare_move( struct inode *dir, struct dentry * dentry )
+{
+ struct dentry *deldir;
+ struct proc_priv proc_priv;
+ struct timeval now;
+ int num;
+ char buf[64];
+ struct dentry *d,*f;
+ struct iattr newattrs;
+ struct file *file;
+ struct vfsmount *mnt;
+ unsigned long ino;
+ int ret = -ENOENT;
+
+ /* We move only a valid dentry */
+
+ if( !dentry->d_inode )
+ return(ret);
+
+
+// printk("ZZZ: start\n");
+ /* Save current process privileges and set root privileges */
+
+ save_proc_priv(&proc_priv);
+ set_proc_priv();
+
+
+ /* search for /deleted directory */
+
+ deldir=lookup_one_len(DELETED_DIR,dentry->d_sb->s_root,strlen(DELETED_DIR));
+ if( IS_ERR(deldir) )
+ {
+ printk("dare_move: Error 1 (%d) [patent inode %ld]\n",(int)(deldir),dentry->d_sb->s_root->d_inode->i_ino);
+ goto Out0;
+ }
+ if( !deldir->d_inode )
+ {
+ /* /deleted does not exist */
+ goto Out;
+ }
+ if( !S_ISDIR(deldir->d_inode->i_mode) )
+ {
+ /* /deleted is not a directory! */
+/* printk("dare_move: /deleted is not a directory!\n"); */
+ goto Out;
+ }
+ if( deldir==dentry || is_offspring(deldir,dentry) )
+ {
+ /* Deleting inside /deleted */
+ /* printk("dare_move: Deleting inside /deleted\n"); */
+ goto Out;
+ }
+
+// printk("ZZZ: 1\n");
+
+ /* search for /deleted/