免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 2205 | 回复: 9
打印 上一主题 下一主题

求一模块实例.请大家请教 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2010-10-11 10:27 |只看该作者 |倒序浏览
看了readlink原码不是明白,不知道怎写,只想在内核态读取符号链接的文件,最后得到这个文件名,也就是readlink函数功能.

论坛徽章:
36
IT运维版块每日发帖之星
日期:2016-04-10 06:20:00IT运维版块每日发帖之星
日期:2016-04-16 06:20:0015-16赛季CBA联赛之广东
日期:2016-04-16 19:59:32IT运维版块每日发帖之星
日期:2016-04-18 06:20:00IT运维版块每日发帖之星
日期:2016-04-19 06:20:00每日论坛发贴之星
日期:2016-04-19 06:20:00IT运维版块每日发帖之星
日期:2016-04-25 06:20:00IT运维版块每日发帖之星
日期:2016-05-06 06:20:00IT运维版块每日发帖之星
日期:2016-05-08 06:20:00IT运维版块每日发帖之星
日期:2016-05-13 06:20:00IT运维版块每日发帖之星
日期:2016-05-28 06:20:00每日论坛发贴之星
日期:2016-05-28 06:20:00
2 [报告]
发表于 2010-10-11 13:34 |只看该作者
记得内核版有网友曾经发帖问过类似问题,LZ搜一下先。

论坛徽章:
9
辰龙
日期:2014-08-18 20:38:42未羊
日期:2014-09-04 08:50:45丑牛
日期:2014-09-06 00:12:55寅虎
日期:2014-12-22 20:50:56摩羯座
日期:2015-01-14 22:28:15巳蛇
日期:2015-01-23 20:39:272015年辞旧岁徽章
日期:2015-03-03 16:54:1515-16赛季CBA联赛之青岛
日期:2016-03-13 23:37:1915-16赛季CBA联赛之深圳
日期:2016-03-29 18:52:38
3 [报告]
发表于 2010-10-11 17:15 |只看该作者
回复 1# ouyangyufu


当进程必须识别一个文件时,就把它的文件路径名传递给某个VFS系统调用,如open()、mkdir()、rename()或stat()。我们这里要说明VFS如何实现路径名查找,也就是说如何从文件路径名导出相应的索引节点。

执行这一任务的标准过程就是分析路径名并把它拆分成一个文件名序列。除了最后一个文件名以外,所有的文件名都必定是目录。

如果路径名的第一个字符是“/”,例如:/usr/share/system-configure-ext2/test.conf,那么这个路径名是绝对路径,因此从current->fs->root(进程的根目录)所标识的目录开始搜索。否则,路径名是相对路径,因此从currrent->fs->pwd(进程的当前目录)所标识的目录开始搜索。

在对第一个目录的索引节点进行处理的过程中,代码要检查与第一个名字匹配的目录项,以获得相应的索引节点。然后,从缓存或磁盘读出包含那个索引节点的目录文件,并检查与第二个名字匹配的目录项,以获得相应的索引节点。对于包含在路径中的每个名字,这个过程反复执行。

目录项高速缓存极大地加速了这一过程,因为它把最近最常使用的目录项对象保留在内存中。正如我们以前看到的,每个这样的对象使特定目录中的一个文件名与它相应的索引节点相联系。因此在很多情况下,路径名的分析可以避免从磁盘读取中间目录。

但是,事情并不像看起来那么简单,因为必须考虑如下的Unix和VFS文件系统的特点:
- 对每个目录的访问权必须进行检查,以验证是否允许进程读取这一目录的内容。
- 文件名可能是与任意一个路径名对应的符号链接;在这种情况下,分析必须扩展到那个路径名的所有分量。
- 符号链接可能导致循环引用;内核必须考虑这个可能性,并能在出现这种情况时将循环终止。
- 文件名可能是一个已安装文件系统的安装点。这种情况必须检测到,这样,查找操作必须延伸到新的文件系统。
- 路径名查找应该在发出系统调用的进程的命名空间中完成。由具有不同命名空间的两个进程使用的相同路径名,可能指定了不同的文件。

路径名查找是由path_lookup()函数执行的,它接收三个参数:


name:指向要解析的文件路径名的指针。
flags:标志的值,表示将会怎样访问查找的文件。
nd:nameidata数据结构的地址,这个结构存放了查找操作的结果。

当path_lookup()返回时,结果参数nd指向的nameidata结构用与路径名查找操作有关的数据来填充:


struct nameidata {
     struct dentry     *dentry;                   /* 目录项对象的地址 */
     struct vfsmount *mnt;                       /* 已安装文件系统对象的地址 */
     struct qstr     last;                             /* 路径名的最后一个分量(当LOOKUP_PARENT标志被设置时使用)*/
     unsigned int     flags;                        /* 查找标志 */
     int          last_type;                           /* 路径名最后一个分量的类型(当LOOKUP_PARENT标志被设置时使用)*/
     unsigned     depth;                           /* 符号链接嵌套的当前级别(参见下面);必须小于6 */
     char *saved_names[MAX_NESTED_LINKS + 1];    /* 与嵌套的符号链接关联的路径名数组 */

     /* Intent data 单个成员联合体,指定如何访问文件 */
     union {
          struct open_intent open;
     } intent;                          
};

dentry和mnt字段分别指向所解析的最后一个路径分量的目录项对象(注意是test.conf而不是system-configure-ext2)和已安装文件系统对象。这两个字段“描述”由给定路径名表示的文件。

由于path_lookup()函数返回的nameidata结构中的目录项对象和已安装文件系统对象代表了查找操作的结果,因此在path_lookup()的调用者完成使用查找结果之前,这个两个对象都不能被释放。因此,path_lookup()增加这两个对象引用计数器的值。如果调用者想释放这些对象,则调用path_release()函数,传递给它的参数就是nameidata结构的地址。

flags字段存放查找操作中使用的某些标志的值,这些标志中的大部分可由调用者在path_lookup()的flags参数中进行设置:
LOOKUP_FOLLOW:如果最后一个分量是符号链接,则解释(追踪)它
LOOKUP_DIRECTORY:最后一个分量必须是目录
LOOKUP_CONTINUE:在路径名中还有文件名要检查
LOOKUP_PARENT:查找最后一个分量名所在的目录
LOOKUP_NOALT:不考虑模拟根目录(在80x86体系结构中没有用)
LOOKUP_OPEN:试图打开一个文件
LOOKUP_CREATE:试图创建一个文件(如果不存在)
LOOKUP_ACCESS:试图为一个文件检查用户的权限

论坛徽章:
9
辰龙
日期:2014-08-18 20:38:42未羊
日期:2014-09-04 08:50:45丑牛
日期:2014-09-06 00:12:55寅虎
日期:2014-12-22 20:50:56摩羯座
日期:2015-01-14 22:28:15巳蛇
日期:2015-01-23 20:39:272015年辞旧岁徽章
日期:2015-03-03 16:54:1515-16赛季CBA联赛之青岛
日期:2016-03-13 23:37:1915-16赛季CBA联赛之深圳
日期:2016-03-29 18:52:38
4 [报告]
发表于 2010-10-11 17:18 |只看该作者
测试在fedora 7下完成

测试代码:
  1. /*
  2. * Author:Tinnal <Tinnal.Feng@miartech.com>
  3. *
  4. *
  5. * The source code in this file can be freely used, adapted,
  6. * and redistributed in source or binary form, so long as an
  7. * acknowledgment appears in derived source files.No warranty
  8. * is attached;we cannot take responsibility for errors or
  9. * fitness for use.
  10. *
  11. * Modification:
  12. *      Tinnal  V1.00   2010-10-10      Create the file.
  13. */

  14. #include <linux/module.h>
  15. #include <linux/moduleparam.h>
  16. #include <linux/init.h>

  17. #include <linux/kernel.h>                       /* printk() */
  18. #include <linux/fs.h>                           /* everything... */
  19. #include <linux/namei.h>                                /* path_lookup*/

  20. static char *path = NULL;

  21. module_param(path, charp, 0);

  22. static int __init PathLookup_init(void)
  23. {

  24.         struct nameidata nd;
  25.         if(path == NULL)
  26.         {
  27.                 printk("PathLookup: Used: insmod PathLookup path=\"<Link file name>\"\n");
  28.                 return -1;
  29.         }
  30.         printk("PathLookup: The link file you enter is: %s\n", path);

  31.         if (path_lookup(path, LOOKUP_FOLLOW, &nd)) {
  32.                 printk("PathLookup:  path %s not found!\n", path);
  33.                 return -1;
  34.         }

  35.         printk("PathLookup: The real file is %s\n", nd.dentry->d_name.name);

  36.         path_release(&nd);

  37.         return 0;
  38. }

  39. static void __exit PathLookup_exit(void)
  40. {
  41. }

  42. module_init(PathLookup_init);
  43. module_exit(PathLookup_exit);
  44. MODULE_AUTHOR("Tinnal <tinnal.feng@miartech.com>");
  45. MODULE_LICENSE("Dual BSD/GPL");
  46. MODULE_DESCRIPTION("V1.00");
复制代码
测试过程:

[root@localhost driver]# touch normal.txt
[root@localhost driver]# ln -s normal.txt link.txt
[root@localhost driver]# insmod PathLookup.ko path="./link.txt"
[root@localhost driver]# dmesg | grep "PathLookup"
PathLookup: The link file you enter is: ./link.txt
PathLookup: The real file is normal.txt
[root@localhost driver]#

论坛徽章:
9
辰龙
日期:2014-08-18 20:38:42未羊
日期:2014-09-04 08:50:45丑牛
日期:2014-09-06 00:12:55寅虎
日期:2014-12-22 20:50:56摩羯座
日期:2015-01-14 22:28:15巳蛇
日期:2015-01-23 20:39:272015年辞旧岁徽章
日期:2015-03-03 16:54:1515-16赛季CBA联赛之青岛
日期:2016-03-13 23:37:1915-16赛季CBA联赛之深圳
日期:2016-03-29 18:52:38
5 [报告]
发表于 2010-10-11 18:29 |只看该作者
回复 1# ouyangyufu

刚才的path_lookup因为指定了LOOKUP_FOLLOW标志,path_lookup函数会一直找到最终的文件,我们可以做如下测试:

[root@localhost driver]# mkdir test
[root@localhost driver]# touch test/test.txt
[root@localhost driver]# ln -s ./test/test.txt link2.txt
[root@localhost driver]# insmod PathLookup.ko path="./link2.txt"
[root@localhost driver]# dmesg | grep "PathLookup"

你会得到如下的输出:
PathLookup: The link file you enter is: ./link2.txt
PathLookup: The real file is test.txt

如果你想得到的是连接的路径,而不是目标文件本身,那就得先得到link文件本身,然后再readlink了。代码如下:
  1. /*
  2. * Author:Tinnal <Tinnal.Feng@miartech.com>
  3. *
  4. *
  5. * The source code in this file can be freely used, adapted,
  6. * and redistributed in source or binary form, so long as an
  7. * acknowledgment appears in derived source files.No warranty
  8. * is attached;we cannot take responsibility for errors or
  9. * fitness for use.
  10. *
  11. * Modification:
  12. *      Tinnal  V1.00   2010-10-10      Create the file.
  13. */

  14. #include <linux/module.h>
  15. #include <linux/moduleparam.h>
  16. #include <linux/init.h>

  17. #include <linux/kernel.h>                       /* printk() */
  18. #include <linux/fs.h>                           /* everything... */
  19. #include <linux/namei.h>                                /* path_lookup*/

  20. static char *path = NULL;

  21. module_param(path, charp, 0);

  22. static int __init PathLookup_init(void)
  23. {

  24.         struct nameidata nd;
  25.         void *cookie;

  26.         if(path == NULL)
  27.         {
  28.                 printk("PathLookup: Used: insmod PathLookup path=\"<Link file name>\"\n");
  29.                 return -1;
  30.         }
  31.         printk("PathLookup: The link file you enter is: %s\n", path);

  32.         if (path_lookup(path, 0, &nd)) {
  33.                 printk("PathLookup:  path %s not found!\n", path);
  34.                 return -1;
  35.         }

  36.         printk("PathLookup: The real file is %s\n", nd.dentry->d_name.name);

  37.         cookie = nd.dentry->d_inode->i_op->follow_link(nd.dentry, &nd);
  38.         if (!IS_ERR(cookie)) {
  39.                 printk("PathLookup: [followlink]:%s\n", nd_get_link(&nd));
  40.                 if (nd.dentry->d_inode->i_op->put_link)
  41.                         nd.dentry->d_inode->i_op->put_link(nd.dentry, &nd, cookie);
  42.         }
  43.         path_release(&nd);

  44.         return 0;
  45. }

  46. static void __exit PathLookup_exit(void)
  47. {
  48. }

  49. module_init(PathLookup_init);
  50. module_exit(PathLookup_exit);
  51. MODULE_AUTHOR("Tinnal <tinnal.feng@miartech.com>");
  52. MODULE_LICENSE("Dual BSD/GPL");
  53. MODULE_DESCRIPTION("V1.00");
复制代码
这样的话,我们将得到如下的输出:
PathLookup: The real file is link2.txt
PathLookup: [followlink]:./test/test.txt

可以看出来,得到的是./test/test.txt这个完整的目标文件的路径,和我用ln命令连接他出来的时候的输入是一样的。

这里要注意的是path_lookup已经没有LOOKUP_FOLLOW了,也就是不跟进连接文件。

论坛徽章:
0
6 [报告]
发表于 2010-10-12 12:06 |只看该作者
学习了,感谢Tinnal

论坛徽章:
0
7 [报告]
发表于 2010-10-13 11:18 |只看该作者
本帖最后由 ouyangyufu 于 2010-10-13 11:19 编辑

麻烦请问一下Tinnal,在测试 得到链接的路径代码(第二个实例),读取/proc进程下的fd符号链接时,得不到路径,这是为什么.

论坛徽章:
9
辰龙
日期:2014-08-18 20:38:42未羊
日期:2014-09-04 08:50:45丑牛
日期:2014-09-06 00:12:55寅虎
日期:2014-12-22 20:50:56摩羯座
日期:2015-01-14 22:28:15巳蛇
日期:2015-01-23 20:39:272015年辞旧岁徽章
日期:2015-03-03 16:54:1515-16赛季CBA联赛之青岛
日期:2016-03-13 23:37:1915-16赛季CBA联赛之深圳
日期:2016-03-29 18:52:38
8 [报告]
发表于 2010-10-13 16:49 |只看该作者
回复 7# ouyangyufu


  试了一下,的确在读proc的连接文件时读不了。有空我看看吧。

论坛徽章:
9
辰龙
日期:2014-08-18 20:38:42未羊
日期:2014-09-04 08:50:45丑牛
日期:2014-09-06 00:12:55寅虎
日期:2014-12-22 20:50:56摩羯座
日期:2015-01-14 22:28:15巳蛇
日期:2015-01-23 20:39:272015年辞旧岁徽章
日期:2015-03-03 16:54:1515-16赛季CBA联赛之青岛
日期:2016-03-13 23:37:1915-16赛季CBA联赛之深圳
日期:2016-03-29 18:52:38
9 [报告]
发表于 2010-10-13 20:26 |只看该作者
本帖最后由 Tinnal 于 2010-10-13 20:29 编辑

回复 7# ouyangyufu


回复 7# ouyangyufu

之前的问题给你找出来了。主要是因为proc文件系统的inode操作proc_pid_follow_link函数没有通过nd_set_link函数把连接文件的路径存在dentry里头,而只直接把dentry改为目标文件的dentry。因我们通过nd_get_link函数而没法获得对应的路径。真气死我了,内核代码规范的不统一真令人担忧,查得我快吐血了。还是redhat 9 好的,我的fedora里头连原码都没有。害得我又得把ARM实验板抱出来。proc_pid_follow_link的原码如下:
  1. static void *proc_pid_follow_link(struct dentry *dentry, struct nameidata *nd)
  2. {
  3.         struct inode *inode = dentry->d_inode;
  4.         int error = -EACCES;

  5.         /* We don't need a base pointer in the /proc filesystem */
  6.         path_release(nd);

  7.         /* Are we allowed to snoop on the tasks file descriptors? */
  8.         if (!proc_fd_access_allowed(inode))
  9.                 goto out;

  10.         error = PROC_I(inode)->op.proc_get_link(inode, &nd->dentry, &nd->mnt);
  11.         nd->last_type = LAST_BIND;
  12. out:
  13.         return ERR_PTR(error);
  14. }
复制代码
气人的代码!

改写完以后的代码就可以读出fd的符号连接了,用的方法是得到目标文件后,用d_path命令重建目标文件的路径。
  1. /*
  2. * Author:Tinnal <Tinnal.Feng@miartech.com>
  3. *
  4. *
  5. * The source code in this file can be freely used, adapted,
  6. * and redistributed in source or binary form, so long as an
  7. * acknowledgment appears in derived source files.No warranty
  8. * is attached;we cannot take responsibility for errors or
  9. * fitness for use.
  10. *
  11. * Modification:
  12. *      Tinnal  V1.00   2010-10-10      Create the file.
  13. */

  14. #include <linux/module.h>
  15. #include <linux/moduleparam.h>
  16. #include <linux/init.h>

  17. #include <linux/kernel.h>                       /* printk() */
  18. #include <linux/fs.h>                           /* everything... */
  19. #include <linux/namei.h>                                /* path_lookup*/

  20. static char *path = NULL;

  21. module_param(path, charp, 0);

  22. #define BUFFER_SIZE (512)
  23. char path_buffer[BUFFER_SIZE];

  24. static int __init PathLookup_init(void)
  25. {

  26.         struct nameidata nd;
  27.         void *cookie;
  28.         char *link_path = NULL;

  29.         if(path == NULL)
  30.         {
  31.                 printk("PathLookup: Used: insmod PathLookup path=\"<Link file name>\"\n");
  32.                 return -1;
  33.         }
  34.         printk("PathLookup: The link file you enter is: %s\n", path);

  35.         if (path_lookup(path, 0, &nd)) {
  36.                 printk("PathLookup:  path %s not found!\n", path);
  37.                 return -1;
  38.         }

  39.         printk("PathLookup: The real file is %s\n", nd.dentry->d_name.name);
  40.         printk("PathLookup: The link file's dentry is: %p\n", nd.dentry);

  41.         nd.depth = 0;
  42.         cookie = nd.dentry->d_inode->i_op->follow_link(nd.dentry, &nd);
  43.         if (!IS_ERR(cookie)) {
  44. #if 0
  45.                 printk("PathLookup: [followlink]:%s\n", nd_get_link(&nd));
  46. #else
  47.                 printk("PathLookup: Target denrty:%p\n", nd.dentry);
  48.                 link_path = d_path(nd.dentry, nd.mnt, path_buffer, BUFFER_SIZE);
  49.                 printk("PathLookup: [followlink]:%s\n", link_path);
  50. #endif
  51.                 if (nd.dentry->d_inode->i_op->put_link)
  52.                         nd.dentry->d_inode->i_op->put_link(nd.dentry, &nd, cookie);
  53.         }
  54.         path_release(&nd);

  55.         return 0;
  56. }

  57. static void __exit PathLookup_exit(void)
  58. {
  59. }

  60. module_init(PathLookup_init);
  61. module_exit(PathLookup_exit);
  62. MODULE_AUTHOR("Tinnal <tinnal.feng@miartech.com>");
  63. MODULE_LICENSE("Dual BSD/GPL");
  64. MODULE_DESCRIPTION("V1.00");
复制代码
这种做法只针对proc这破文件系统。对普通LINK文件失败,因为对普通文件进行follow_link操作后,是不会改dentry本身的,也就是:它还是指向连接文件本身而不是目标。我们可以用新的程序对正常的link文件和/proc/XXX/fd下的连接文件进行测试。

正常的link文件输出:
insmod PathLookup.ko path="./link"
PathLookup: The link file you enter is: ./link
PathLookup: The real file is link
PathLookup: The link file's dentry is: c3fef098
PathLookup: Target denrty:c3fef098
PathLookup: [followlink]:/link
可以看到dentry 前后指针是一致的。

/proc/XXX/fd下文件输出:
insmod PathLookup.ko path="/proc/737/fd/0"
PathLookup: The link file you enter is: /proc/737/fd/0
PathLookup: The real file is 0
PathLookup: The link file's dentry is: c3e282c8
PathLookup: Target denrty:c3d544b8
PathLookup: [followlink]:/dev/ttyS0
可以看到dentry 前后指针是不一致的。
(以上结果是在开发板上运行的情况,PC一样)

可以看出,这个程序已经不适用于正常连接文件了。


为什么不直接用readlink函数?因为破函数直接在函数体内copy_to_uesr了,妈的!还是做应用好,一个readlink就搞定!

补充几句:自己多RTFSC。这东西很耗时间,帮不了你多少次。

论坛徽章:
0
10 [报告]
发表于 2010-10-14 11:42 |只看该作者
哦,原来是这样,源码读的的不够,是得多研究下了,Tinnal兄辛苦了。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP