path_lookup函数
path_walk函数
一:基本概念:
在进行以上几点的时候要作如下考虑:
1,对每个目录的访问权必须进行检查,以验证是否允许进程读取这一目录的内容。
2,文件名可能是与任意一个路径名对应的符号链接;在这种情况下,分析必须扩展到那个路径名的所有分量。
3,符号链接可能导致循环引用;内核必须考虑这个可能性,并能在出现这种情况时将循环终止。
4,文件名可能是一个已安装文件系统的安装点。这种情况必须检测到,这样,查找操作必须延伸到新的文件系统。
5,路径名查找应该在发出系统调用的进程的命名空间中完成。由具有不同命名空间的两个进程使用的相同路径名,可能指定了不同的文件。
LOOKUP_DIRECTORY:最后一个分量必须是目录
LOOKUP_CONTINUE:在路径名中还有文件名要检查
LOOKUP_PARENT:查找最后一个分量名所在的目录
LOOKUP_NOALT:不考虑模拟根目录(在80x86体系结构中没有用)
LOOKUP_OPEN:试图打开一个文件
LOOKUP_CREATE:试图创建一个文件(如果不存在)
1、当路径为相对路径,并dfd为特殊值AT_FDCWD,它被解释为相对于调用进程的当前工作目录。
2、当路径为相对路径,且没有设置,则从dfd指定的文件目录开始查找
3、当路径为绝对路径,dfd被忽略,从根目录开始查找
当path_lookup()返回时,结果参数nd指向的nameidata结构用与路径名查找操作有关的数据来填充:
LAST_NORM:最后一个分量是普通文件名
LAST_ROOT:最后一个分量是“/”(也就是整个路径名为“/”)
LAST_DOT:最后一个分量是“.”
LAST_DOTDOT:最后一个分量是“..”
LAST_BIND:最后一个分量是链接到特殊文件系统的符号链接
三:代码执行流程
主角是 path_openat,在这里 Kernel 向我们展示了“路径行走(path walk)”的两种策略:rcu-walk(3242)和 ref-walk(3244)。在 rcu-walk 期间将会禁止抢占,也决不能出现进程阻塞,所以其效率很高;ref-walk 会在 rcu-walk 失败、进程需要随眠或者需要取得某结构的引用计数(reference count)的情况下切换进来,很明显它的效率大大低于 rcu-walk。最后 REVAL(3246)其实也是 ref-walk,在以后我们会看到,该模式是在已经完成了路径查找,打开具体文件时,如果该文件已经过期(stale)才启动的,所以 REVAL 是给具体文件系统自己去解释的。其实 REVAL 几乎不会用到,在内核的文件系统中只有 nfs 用到了这个模式。 path_openat 主要作用是首先为 struct file 申请内存空间,设置遍历路径的初始状态,然后遍历路径并找到最
path_walk函数
一:基本概念:
path_lookup函数的适用场景:open() stat() 等函数传进去的字符串的路径在内核里面都要转换成相应的inode节点和dentry结构体,执行这一任务的标准过程就是分析路径名并把它拆分成一个文件名序列。除了最后一个文件名以外,所有的文件名都必定是目录,如果路径名的第一个字符是“/”,例如:/usr/share/system-configure-ext2/test.conf,那么这个路径名是绝对路径,因此从current->fs->root(进程的根目录)所标识的目录开始搜索。否则,路径名是相对路径,因此从currrent->fs->pwd(进程的当前目录)所标识的目录开始搜索。
在进行以上几点的时候要作如下考虑:
1,对每个目录的访问权必须进行检查,以验证是否允许进程读取这一目录的内容。
2,文件名可能是与任意一个路径名对应的符号链接;在这种情况下,分析必须扩展到那个路径名的所有分量。
3,符号链接可能导致循环引用;内核必须考虑这个可能性,并能在出现这种情况时将循环终止。
4,文件名可能是一个已安装文件系统的安装点。这种情况必须检测到,这样,查找操作必须延伸到新的文件系统。
5,路径名查找应该在发出系统调用的进程的命名空间中完成。由具有不同命名空间的两个进程使用的相同路径名,可能指定了不同的文件。
二:,path_lookup的入口函数是kern_path()函数,
int kern_path(const char*name,unsigned int flags,struct path*path),
1920 long do_mount(char *dev_name, char *dir_name, char *type_page,
1921 unsigned long flags, void *data_page)
1922 {
1923 struct path path;
1924 int retval = 0;
1940 retval = kern_path(dir_name, LOOKUP_FOLLOW, &path);
name:指向要解析的文件路径名的指针,flags:标志的值,表示将会怎样访问查找的文件,nd:nameidata数据结构的地址,这个结构存放了查找操作的结果。
LOOKUP_FOLLOW:如果最后一个分量是符号链接,则解释(追踪)它LOOKUP_DIRECTORY:最后一个分量必须是目录
LOOKUP_CONTINUE:在路径名中还有文件名要检查
LOOKUP_PARENT:查找最后一个分量名所在的目录
LOOKUP_NOALT:不考虑模拟根目录(在80x86体系结构中没有用)
LOOKUP_OPEN:试图打开一个文件
LOOKUP_CREATE:试图创建一个文件(如果不存在)
LOOKUP_ACCESS:试图为一个文件检查用户的权限
kern_path()函数最终会调用到do_path_lookup()函数,原型是:
static int do_path_lookup(int dfd,const char*name,unsigned int flasgs,struct nameidata*nd)
do_path_lookup()函数最终会调用到path_lookupat()函数,原型是:
static int path_lookupat(int dfd,const char*name,unsigned int flags,struct nameidata*nd)
dfd的含义:
1、当路径为相对路径,并dfd为特殊值AT_FDCWD,它被解释为相对于调用进程的当前工作目录。
2、当路径为相对路径,且没有设置,则从dfd指定的文件目录开始查找
3、当路径为绝对路径,dfd被忽略,从根目录开始查找
当path_lookup()返回时,结果参数nd指向的nameidata结构用与路径名查找操作有关的数据来填充:
18 struct nameidata {
19 struct path path; //保存当前路径分量的父路径,在该路径下查找路径分量
20 struct qstr last; //last包含了需要查找的名称,它是一个快速字符串,不仅包含字符串本身,还包含字符串长度和一个散列值。
21 struct path root; //当从根目录开始查找,需要设置root
22 unsigned int flags;
23 int last_type; //路径名最后一个分量类型(当LOOKUP_PAPRENT设置时使用)
24 unsigned depth; //符号链接嵌套的当前级别
25 char *saved_names[MAX_NESTED_LINKS + 1]; //与嵌套的符号链接关联的路径名数组
27 /* Intent data */
28 union {
29 struct open_intent open; //单个成员联合体,指定如何访问文件
30 } intent;
31 };
LAST_NORM:最后一个分量是普通文件名
LAST_ROOT:最后一个分量是“/”(也就是整个路径名为“/”)
LAST_DOT:最后一个分量是“.”
LAST_DOTDOT:最后一个分量是“..”
LAST_BIND:最后一个分量是链接到特殊文件系统的符号链接
三:代码执行流程
目前使用的是RCU walk,写时拷贝,读写锁。
3235. struct file *do_filp_open(int dfd, struct filename *pathname,
3236. const struct open_flags *op)
3237. {
3238. struct nameidata nd;
3239. int flags = op->lookup_flags;
3240. struct file *filp;
3241.
3242. filp = path_openat(dfd, pathname, &nd, op, flags | LOOKUP_RCU);
3243. if (unlikely(filp == ERR_PTR(-ECHILD)))
3244. filp = path_openat(dfd, pathname, &nd, op, flags);
3245. if (unlikely(filp == ERR_PTR(-ESTALE)))
3246. filp = path_openat(dfd, pathname, &nd, op, flags | LOOKUP_REVAL);
3247. return filp;
3248. }
主角是 path_openat,在这里 Kernel 向我们展示了“路径行走(path walk)”的两种策略:rcu-walk(3242)和 ref-walk(3244)。在 rcu-walk 期间将会禁止抢占,也决不能出现进程阻塞,所以其效率很高;ref-walk 会在 rcu-walk 失败、进程需要随眠或者需要取得某结构的引用计数(reference count)的情况下切换进来,很明显它的效率大大低于 rcu-walk。最后 REVAL(3246)其实也是 ref-walk,在以后我们会看到,该模式是在已经完成了路径查找,打开具体文件时,如果该文件已经过期(stale)才启动的,所以 REVAL 是给具体文件系统自己去解释的。其实 REVAL 几乎不会用到,在内核的文件系统中只有 nfs 用到了这个模式。 path_openat 主要作用是首先为 struct file 申请内存空间,设置遍历路径的初始状态,然后遍历路径并找到最