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/