上面的链接是redirfs项目的主页...这个东西就是一个内核模块以及一些其他的用户空间工具组件.....
redirfs...名字上就能看出来..意思是重定向文件系统...
http://www.redirfs.org/tiki-index.php
主页上对于redirfs的介绍是:
The RedirFS or redirecting file system is a new layer between virtual file system switch (VFS) and file system drivers. It is implemented as an out-of-kernel module for Linux 2.6 and it provides framework allowing modification of file system calls in the VFS layer. The RedirFS by itself does not provide any additional functionality and if it is loaded into the Linux kernel, it just occupies some memory space and it does practically nothing. Now you maybe ask yourself a question: "So what is it good for then?". The RedirFS is intended to be used by so-called filters. Filter is a linux kernel module (LKM) that uses the RedirFS framework. Each filter can add some useful functionality to the existing file systems like transparent compression, transparent encryption, merging contents of several directories into one, allowing writing to a read-only media and others. Filter can set pre and post callback functions for selected file system calls (e.g. read or open) and include or exclude directories or single files for which its callback functions will be called. Every filter has its priority - a unique number that defines in which order will the filters be called. The RedirFS manages all filters and calls their callback functions in order specified by their priorities. While the RedirFS is located in the VFS layer, filters can be used generally in all file systems (e.g. ext2, nfs, proc). In addition, the RedirFS allows filters to be on-the-fly registered and unregistered.
简单的翻译:redirfs是介于VFS和文件系统驱动之间的一层...是一个内核模块...提供了一个方法修改VFS层的文件系统调用~~~。RedirFS本身不提供额外的功能..加载到内核空间时产生的仅仅是一些内存的消耗~.RedirFS用于filter上。filter是一个内核模块,这个模块建立在RedirFS框架之下.每个Filter可以向文件系统中添加有用的功能,透明压缩、透明加密、若干个目录的内容合并到一起,允许向一个只读介质等特性...Filter可以设置前后回调函数...,可以设置这些回调函数作用的包含或者排除目录或者文件..每个Filter有自己的优先级,调用时候会按照这个优先级来。Redirfs根据优先级来管理filter对象以及它们的回调函数。因为RedirFS是在VFS层的位置上..所以Redirfs可以应用于所有的文件系统.另外,RedirFS允许注册和注销filter对象..
上面这些话就是说,可以在VFS层的调用上做些变化,比如回调,传递数据,加密之类~~~.看起来似乎没有什么特殊..可能就是hook而已..
看了下RedirFS的核心代码..大部分都是在管理filter对象和组织filter对象的其他数据结构...以及路径查找等~
在RedirFS主页上的Documentation中的一些说RedirFS并不是修改系统调用表来实现的...还列出了一些杀毒软件或者类似模块都通过是修改系统调用表实现的...
根据Documentation中的thesis.pdf中framework这章说到的,RedirFS也是替换filter申请的目录或者文件的VFS层方法。
一个比较简单过程描述:
通过path_lookup函数可以看获得VFS的一些操作方法,(当然也包括路径信息、挂载信息、dentry、super_blocks等等) inode_operations, dentry_operations, file_operations等。
然后将具体的dentry或者inode对象中操作方法指针给记下来,本来file_operations指针是ops,现在用old_ops把ops之前指向的东西给记录下来,这个ops所指向的区域的内容,是无法修改的...(编译时候会错误:说指向区域不能修改,应该是内核设置的保护吧,毕竟是整个文件系统共用的方法,虚拟内存也会用到)。
然后加入希望在open操作之前加上简单的附加操作,那就做一个函数new_open,类型和file_operations.open类型一样的.这个函数中先执行自己额外的操作,然后把new_open中的参数再传递给old->ops.open,这样就做出了一个我们想要的open函数。
但是此时直接修改ops->open是不可行的,我们还要依赖这个,同时系统也保证你无法修改..不过,ops是可以修改指向的...此时先把old->ops的内容给复制过来,然后我们把ops指向这个复制的区域,此时方法还没有修改,不过,ops->open已经是可修改的了,因为现在ops指向的东西是我们自己的..所以此时把ops->open = new_open,就搞定了...
模块卸载时,ops = old_ops就恢复了..
这就是RedirFS替换VFS操作的简单描述..实际上需要花费很多精力去整理filter,包括和VFS中的dentry缓存打叫道等等...记录各种变化,才能保证filter监视的东西和RedirFS中的对象一致,以及注意一些操作的锁定。
PS:上面描述过程的简单实现代码在实验室的机器上,回头再贴..
上面过程的简单实现:条件...在/boot下面建立一个test/test文件..然后就把这个文件的open操作加了一个若干秒的延迟...内核printk一个hooked的消息.dmesg可以看到消息
用字符设备实现..(read函数就不用看了,是我拿来实验的)
下面的文件编译加载....然后执行echo test >/boot/test/test ,效果就是这个本来不应该阻塞的操作会阻塞3秒...dmesg可以看到hooked的消息... :)虽然看起来有点stupid,不过这个表明我们可以在特定的文件操作上hook自己的东西了..
//每个dentry对象都会包含一个指向super_block的对象...而super_block中又包含一个文件对象的link list...
//用这个信息就可以找到这个super_block(一个块设备一个)并遍历全部文件..即可操作其file_operations...
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/cdev.h>
#include <linux/wait.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/namei.h>
#include <asm/uaccess.h>
#include <linux/dcache.h>
#include <linux/mount.h>
#define PATH_NAME "/boot/test/test"
int test_open(struct inode*inode, struct file *filp);
int test_read(struct file *filp, char __user *buf, size_t count ,loff_t * f_pos);
int test_write(struct file *filp,const char __user *buf, size_t count, loff_t *f_pos);
int test_release(struct inode *inode ,struct file * filp);
int hooked_open(struct inode *inode ,struct file *filp);
int new_open(struct inode*inode, struct file *filp);
static int major_number=1000,minor_number=0;
static dev_t device_number;
struct cdev *my_cdev = NULL;
static struct nameidata *file_path =NULL;
static struct inode *pfile_list = NULL;
static struct dentry *pdentry = NULL;
static struct vfsmount *mount_point = NULL;
static struct file_operations *old_ops=NULL,*back_up=NULL;
//得到super_block->s_file的地址...
int test_open(struct inode*inode, struct file *filp)
{
printk("device opened...\n");
return 0;
}
int test_read(struct file *filp,char __user *buf,size_t count ,loff_t * f_pos)
{
//返回一些数据...路径名以及inode号
int i = copy_to_user((void*)buf, &(pfile_list->i_ino),sizeof(unsigned long));
//迭代获取路径.
struct dentry *tmp = pdentry;
char *full_path = (char *)kzalloc(256,GFP_KERNEL);
int len=0,j = 0;
char root[2] = "/";
while( len<252)
{
strncpy(full_path+len,tmp->d_name.name,tmp->d_name.len);
len += tmp->d_name.len;
full_path[len++] = '_';
if( ! strcmp(tmp->d_name.name,root))
{
//到最底了..找到挂载点.. //作成一个函数会比较好,适合递归
tmp = mount_point->mnt_mountpoint;
strncpy(full_path+len,tmp->d_name.name,tmp->d_name.len);
len+= tmp->d_name.len;
break;
}
tmp = tmp->d_parent;
}
full_path[len++] = '\0';
j = copy_to_user((void*)((char *)buf +4),full_path,len);
kfree(full_path);
return len + sizeof(unsigned long) -i-j;
}
int test_write(struct file *filp,const char __user *buf, size_t count, loff_t *f_pos)
{
//暂时不定义write...先读取super_block
return 0;
}
int test_release(struct inode *inode ,struct file * filp)
{
//释放open开辟的内存
return 0;
}
struct file_operations test_ops = {
.owner = THIS_MODULE,
.read = test_read,
.write = test_write,
.release = test_release,
.open = test_open,
};
int test_init(void)
{
int err = 0;
device_number = MKDEV(major_number,minor_number);
err = register_chrdev_region(device_number,1,"/dev/file_super");
if( 0 > err)
{
printk("cannot register device ..major = %d,minior =%d\n",major_number,minor_number);
return err;
}
my_cdev = cdev_alloc();
my_cdev->ops = &test_ops;
my_cdev->owner = THIS_MODULE;
err = cdev_add(my_cdev,device_number,1);
if( 0 > err)
{
printk("cannot add device ..\n");
unregister_chrdev_region(device_number,1);
cdev_del(my_cdev);
return err;
}
file_path = kzalloc(sizeof(struct nameidata),GFP_KERNEL);
err = path_lookup(PATH_NAME,0,file_path);
if(0 > err)
{
printk("path look up abnormal...module unloaded..\n");
kfree(file_path);
unregister_chrdev_region(device_number,1);
cdev_del(my_cdev);
return err;
}
//已经打开了/boot/test目录,现在可以获取该文件系统上的super_block了..
pdentry = file_path->path.dentry;
pfile_list = pdentry->d_inode; //这个是查找的文件的inode..
mount_point = file_path->path.mnt;
old_ops = pfile_list->i_fop; //这里已经记录下了之前的file_operations
//试图直接修改inode的i_fop会得到这个错误:
//error: assignment of member ‘open’ in read-only object,表明不能修改系统调用~~~
back_up = (struct file_operations *)kzalloc(sizeof(struct file_operations),GFP_KERNEL);
memcpy((void *)back_up,(void*) pfile_list->i_fop,sizeof(struct file_operations));//存起来
back_up->open = hooked_open;
pfile_list->i_fop = back_up;
return 0;
}
void __exit test_exit(void)
{
printk("remove module \n");
pfile_list->i_fop = old_ops;
cdev_del(my_cdev);
unregister_chrdev_region(device_number,1);
kfree(file_path);
kfree(back_up);
printk("module unloaded...\n");
}
int new_open(struct inode*inode, struct file *filp)
{
mdelay(3000);
printk("demo hooked ,you get it !!!!!\n");
return 0;
}
int hooked_open(struct inode *inode ,struct file *filp)
{
new_open(inode,filp);
return old_ops->open(inode,filp);
}
//一个简单的demo,让/特定目录下的open操作sleep 3秒钟...~
//pfile_list指向/boot/test/test的inode结构~~
//两个修改途径(1)修改inode->file结构...(无法修改inode中的file_operations),make提示该成员对象只读...
//可以修改指针...不能修改默认的file_operations。。。
//(2)super_blocks中s_files遍历找到file结构~~~这个是针对已经打开了的文件..需要修改file_op结构...
module_init(test_init);
module_exit(test_exit);
MODULE_LICENSE("GPL");