Chinaunix

标题: VFS层拦截read/write file_operations函数无效 [打印本页]

作者: viton_xuan    时间: 2006-02-07 18:10
标题: VFS层拦截read/write file_operations函数无效
改变read,write,readdir,open,release等函数指针,发现readdir可以正常获得目录名,open,release只能截获目录的操作, read,write完全截获不了任何操作。 请问各位大虾有遇到类似问题吗。现在感觉上是对目录的操作都能截获,但是对文件操作的就完全没有。
readdir(工作正常) 和 read 代码如下
int my_readdir(struct file *fp, void *buf, filldir_t filldir)
{
        char *path = (char*)kmalloc(MAX_DIR_LENTH);
        unsigned int offset = MAX_DIR_LENTH-1;
        struct dentry* cwd = fp->f_dentry;
        int rs;
        path[MAX_DIR_LENTH-1] = '\0';
        // path add one slash in the end
        path[--offset] = '/';
        while( cwd->d_parent != cwd ) {
                offset -= cwd->d_name.len;
                strncpy(path + offset, cwd->d_name.name, cwd->d_name.len);
                path[--offset] = '/';
                cwd = cwd->d_parent;
        }
        if (offset == MAX_DIR_LENTH-1) path[--offset] = '/';
        printk("<1> Read Dir %s\n", path+offset);
        kfree(path);
        if (orig_readdir == NULL) {
                printk("<1> orig read dir function is NULL\n");
                return -1;
        }
        rs = orig_readdir(fp, buf, filldir);
        return rs;
}

ssize_t my_read (struct file *fp, char *buf, size_t len, loff_t *off)
{
        int rs;
        printk("<1> enter my read \n");   //这也没有输出
        char *path = (char*)kmalloc(MAX_DIR_LENTH);
        unsigned int offset = MAX_DIR_LENTH-1;
        struct dentry* cwd = fp->f_dentry;
        path[MAX_DIR_LENTH-1] = '\0';
        while( cwd->d_parent != cwd ) {
                offset -= cwd->d_name.len;
                strncpy(path + offset, cwd->d_name.name, cwd->d_name.len);
                path[--offset] = '/';
                cwd = cwd->d_parent;
        }
        printk("<1> Read file %s\n", path+offset);
        kfree(path);
        if (orig_read == NULL) {
                printk("<1> orig read function is NULL\n");
                return -1;
        }
        rs = orig_read(fp, buf, len, off);
        return rs;
}

挂载方式一样都是保存原来函数指针,用新函数指针代替
请个位兄弟指教
作者: albcamus    时间: 2006-02-07 18:27
截获方式? 最好贴一下代码
作者: viton_xuan    时间: 2006-02-08 09:29
谢谢albcamus 斑竹的关注。
替换file_operations,打开一个文件(我打开的是/),得到该fs的file_operations指针,替换。
下面是代码。
int patch_vfs(const char* p)
{
        struct file* filep;
        filep = filp_open(p, O_RDONLY, 0);
        if (IS_ERR(filep)){
                printk("<1> can not open file\n");
                return -1;
        }
        orig_read = filep->f_op->read;
        orig_write = filep->f_op->write;
        orig_readdir = filep->f_op->readdir;
        orig_ioctl = filep->f_op->ioctl;
        orig_open = filep->f_op->open;
        orig_lock = filep->f_op->lock;
        orig_mmap = filep->f_op->mmap;
        orig_release = filep->f_op->release;

        filep->f_op->read = my_read;
        filep->f_op->write = my_write;
        filep->f_op->readdir = my_readdir;
        filep->f_op->ioctl = my_ioctl;
        filep->f_op->open = my_open;
        filep->f_op->lock = my_lock;
        filep->f_op->mmap = my_mmap;
        filep->f_op->release = my_release;
       
        filp_close(filep, 0);
        return 0;
}

static int patch_init(void)
{
        if (patch_vfs(root_fs) != 0) return -1;
        printk("<1> VFS patched\n");
        return 0;
}

module_init(patch_init);
作者: 思一克    时间: 2006-02-08 12:04
你没有做任何READDIR操作,所以无read输出。
作者: viton_xuan    时间: 2006-02-08 14:30
我发现我表述不清楚,其实现在用ls命令,可以看到readdir有捕捉到。看到了有输出
Feb  7 17:50:24 xuan kernel:  open file /root/kernel
Feb  7 17:50:24 xuan kernel:  Read Dir /root/kernel/
Feb  7 17:50:24 xuan kernel:  Read Dir /root/kernel/
Feb  7 17:50:24 xuan kernel:  release file /root/kernel
但是文件的打开,读写就都没有能够捕捉到。 无论是read,write等都没有捕捉到,对文件的open,release也没有。
strace ls或者很多命令都看到用了 open read write的系统调用,就是没有捕捉到。我自己写的一个函数就是open两个文件,一个读一个写都没有捕捉到。
我看fs/read_write.c  里面sys_read函数是用了file->f_op->read的函数指针,应该会调用我替换了的my_read函数。不知何故,所以想请教各位。

[ 本帖最后由 viton_xuan 于 2006-2-8 14:33 编辑 ]
作者: albcamus    时间: 2006-02-08 15:22
我猜想啊, 可能是因为你从“/”这个文件获得的指针, 而它碰巧是个目录。 不过这个自己也感觉解释不通, 我用file->f_dentry->d_inode->i_fop来截获的read、write、open等函数, 没问题。(我是用regular file做的)
作者: treul    时间: 2006-02-08 17:18
标题: 回复 1楼 viton_xuan 的帖子
我想,你的这种做法不具有通用性。也就是说,只有在操作你替换了调用以后的“那个文件”时,这个patch模块才起作用。
作者: albcamus    时间: 2006-02-08 18:20
原帖由 treul 于 2006-2-8 17:18 发表
我想,你的这种做法不具有通用性。也就是说,只有在操作你替换了调用以后的“那个文件”时,这个patch模块才起作用。


有通用性的。 你可以把函数指针指向的地址大印出来, 跟System.map或者/proc/kallsyms比对一下, 就看到那个地址代表一个通用的函数了。
作者: richardhesidu    时间: 2006-02-09 02:40
原帖由 viton_xuan 于 2006-2-8 09:29 发表
谢谢albcamus 斑竹的关注。
替换file_operations,打开一个文件(我打开的是/),得到该fs的file_operations指针,替换。
下面是代码。
int patch_vfs(const char* p)
{
        struct file* filep;
        filep = fil ...


你这样截获不了文件的读函数指针的. 上面的my_read函数替换的是目录操作函数ext3_dir_operations->generic_read_dir(), 如果你用的是ext3文件系统的话.

albcamus大哥说的"file->f_dentry->d_inode->i_fop来截获的read、write、open等函数"说的没错.
作者: mq110    时间: 2006-02-09 09:47
借个地儿 问个问题.

这样在VFS层拦截. 和自己导出syscall_table 然后修改系统调用然后替换有什么区别??
后者应该包含前者吧?
作者: albcamus    时间: 2006-02-09 10:22
原帖由 mq110 于 2006-2-9 09:47 发表
借个地儿 问个问题.

这样在VFS层拦截. 和自己导出syscall_table 然后修改系统调用然后替换有什么区别??
后者应该包含前者吧?


从sys_xxx拦截的话, 是比较高级别的, 以sys_open为例, 那时候还没有真正open, 我们只能通过操作文件名来做一些东西; VFS的话, 就是最底层的实现了, 这时候操作的是inode、dentry, 而不是文件名。

sys_read调用…调用…最终会走到VFS的open的
作者: mq110    时间: 2006-02-09 10:33
原帖由 albcamus 于 2006-2-9 10:22 发表


从sys_xxx拦截的话, 是比较高级别的, 以sys_open为例, 那时候还没有真正open, 我们只能通过操作文件名来做一些东西; VFS的话, 就是最底层的实现了, 这时候操作的是inode、dentry, 而不是文件名。

...


明白了.多谢.
作者: viton_xuan    时间: 2006-02-09 10:49
谢谢各位,再看了一下源码,发现正如albcamus大哥和richardhesidu大哥所说,文件和目录是不同的操作函数集。
在fs/ext3/inode.c:
ext3_read_inode() 有:
        if (S_ISREG(inode->i_mode)) {
                inode->i_op = &ext3_file_inode_operations;
                inode->i_fop = &ext3_file_operations;
                inode->i_mapping->a_ops = &ext3_aops;
        } else if (S_ISDIR(inode->i_mode)) {
                inode->i_op = &ext3_dir_inode_operations;
                inode->i_fop = &ext3_dir_operations;
        }
.....

另外觉得截获文件操作函数,用file->f_dentry->d_inode->i_fop和用file->f_op应该是一样的吧,因为看了一下
fs/open.c:
在filp_open()->dentry_open() 有:
inode = dentry->d_inode;
...
f->f_dentry = dentry;
...
f->f_op = fops_get(inode->i_fop);
在打开文件的时候file->f_op就应该和file->f_dentry->d_inode->i_fop指向同样的操作函数集了。

非常谢谢各位的帮助, 我先再试试, 如果有问题, 再麻烦各位。^_^
作者: treul    时间: 2006-02-09 11:08
标题: 解释一下
原帖由 albcamus 于 2006-2-8 18:20 发表


有通用性的。 你可以把函数指针指向的地址大印出来, 跟System.map或者/proc/kallsyms比对一下, 就看到那个地址代表一个通用的函数了。



我的意思是说,filp->f_op仅属于“该文件”本身,而实际上,filp->f_dentry->d_inode->i_fop则会通过filp_open() > dentry_open() > fops_get()向下传递给目录里的文件(正如你所做的那样)。所以我推想,只要用你这种方法,对于non-regular file(比如“/”)也是可行的。right?
作者: albcamus    时间: 2006-02-09 11:20
原帖由 treul 于 2006-2-9 11:08 发表



我的意思是说,filp->f_op仅属于“该文件”本身,而实际上,filp->f_dentry->d_inode->i_fop则会通过filp_open() > dentry_open() > fops_get()向下传递给目录里的文件(正如你所做的那样 ...


啊, 明白了^_^
BTW: 这个讨论真好, 一讨论大家都清楚了
作者: 思一克    时间: 2006-02-09 15:28
to treul,

filp->f_op不属于该文件本身。在一个文件系统中它为不同文件公用。
在sys_open()中在


  1. f = filp_open(tmp, flags, mode);
  2. 后加入

  3. if(!IS_ERR(f)) printk("open %s  f_op = %p\n", tmp, f->f_op);

复制代码


就可以看出
作者: treul    时间: 2006-02-10 10:28
标题: 回复 16楼 思一克 的帖子
re: 思一克

filp->f_op就是由其所属文件系统继承下来的(通过inode),不会传给下级。它是可定制的。所以偶称其“属于文件本身”。“在一个文件系统中它为不同文件公用”是一个现象,其本质就是第一句话。
作者: wuhui_zsu    时间: 2006-02-10 11:28
我知道问题出在哪里了。struct file中的file_options函数指针是在file_open时,通过super_block中的file_operation获取的。如果不更改super_block里的file_operation,你是不可能替换成功的。
作者: viton_xuan    时间: 2006-02-10 13:07
to wuhui_zsu :
如 思一克 和 treul 所说的 在一个文件系统中它为不同文件公用”
比如在ext3里面,
ext3_read_inode()里面有

inode->i_fop = &ext3_file_operations;

所以inode->i_fop已经是指向了ext3的文件操作函数集。
fops_get()增加其模块引用后返回的已经是指向ext3_file_operations的指针了。所以应该可以替换。

[ 本帖最后由 viton_xuan 于 2006-2-10 13:09 编辑 ]




欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2