在上次的给定路径名到目标节点中都是以纯代码的形式讲解,本次将以代码集合图表的方式分析上次忽略的细节。
在这之前,先设想这样的几个实例:
(1)要查找的目标是一个链接。
(2)在路径名中存在子系统的情况。(比如在/mnt目录上挂载了一个文件系统,现在要查找/mnt/test文件夹)
在上一章我们选择性忽略了err = do_lookup(nd, &this, &next) 函数,因为这个函数比较复杂。
- static int do_lookup(struct nameidata *nd, struct qstr *name,
-
struct path *path)
-
{
-
struct vfsmount *mnt = nd->mnt;
-
//首先先根据其父目录以及文件名在杂凑表中查找。(即内存中)
-
struct dentry *dentry = __d_lookup(nd->dentry, name);
-
if (!dentry)
-
//如果没有找到,就表示在内存中不存在当前目录的dentry数据结构。就需要到设备中读出信息来重新在内存中建立该数据结构,这个就涉及到具体的文件系统了。
-
goto need_lookup;
-
if (dentry->d_op && dentry->d_op->d_revalidate)
-
goto need_revalidate;
-
done:
-
path->mnt = mnt;
-
path->dentry = dentry;
-
__follow_mount(path);
-
return 0;
-
need_lookup:
-
//real_lookup在磁盘中查找信息,然后在内存中建立数据结构,挂入杂凑表
-
dentry = real_lookup(nd->dentry, name, nd);
-
if (IS_ERR(dentry))
-
goto fail;
-
goto done;
-
need_revalidate:
-
if (dentry->d_op->d_revalidate(dentry, nd))
-
//通过d_revalidate来检验找到的dentry数据结构,如果验证失败就要从杂凑表中脱链
-
goto done;
-
if (d_invalidate(dentry))
-
goto done;
-
dput(dentry);
-
goto need_lookup;
-
fail:
-
return PTR_ERR(dentry);
-
}
关于上面的函数,首先在内存中根据父目录项以及文件名查找,如果没有找到就代表该目录项不在内存中,就需要调用具体文件系统的钩子函数从设备中读出相应的目录项,在内存中重建,然后挂入杂凑表中。在上面的函数中,有一个极其重要的函数__follow_mount(path)。
- static int __follow_mount(struct path *path)
-
{
-
int res = 0;
-
while (d_mountpoint(path->dentry)) {
-
struct vfsmount *mounted = lookup_mnt(path->mnt, path->dentry);
-
if (!mounted)
-
break;
-
dput(path->dentry);
-
if (res)
-
mntput(path->mnt);
-
path->mnt = mounted;
-
path->dentry = dget(mounted->mnt_root);
-
res = 1;
-
}
-
return res;
-
}
这个函数是首先通过d_mountpoint函数来检查在该dentry设备上有没有安装设备。
在dentry数据结构中有一个成员变量为d_mounted,用于指示在该目录项上有没有安装设备。d_mountpoint函数仅仅检查了该标志位。内核中一个全局变量mount_hashtable。这是一个hash表,用于链接所有的vfsmount结构,即在安装过程中产生的“连接部件”。我们可以认为vfsmount用于建立一个目录项dentry与一个另外一个文件系统的根目录之间的关系。
图一:vfsmount结构体在安装过程中的作用
事实在dentry结构中并没有相应的指针指向vfsmount结构,而是通过父目录项以及文件名构成的hash值存储在mount_hashtable中。
如果在某一个dentry中安装了设备会怎么样。很简单,用安装文件系统的根目录项替换当前目录项,然后继续查找。这样查找就从安装文件系统中继续下去,而原来的dentry的子目录项等等就变得不可见了。
图二:安装设备之后,原dentry的子目录项变得不可见
上述是在执行命令:mount -t yaffs /dev/mtdblock2 /mnt前后的对比(在操作系统的根文件系统下存在/mnt/test/code文件,而在yaffs文件系统下存在/test/code文件),在没有执行该命令之前,我们通过路径名/mnt/test/code查找到的目录项是属于操作系统的根文件系统的,而在/mnt上安装了yaffs文件系统之后,情况就变了。通过路径名查找到是yaffs文件系统上的文件/mnt/test/code。
另外在path_walk(2.6.16源码中为__link_path_walk函数)函数中存在这样的代码,在上一章中只是粗略的带过.
- if (inode->i_op->follow_link) {
-
err = do_follow_link(&next, nd);
-
if (err)
-
goto return_err;
-
err = -ENOENT;
-
inode = nd->dentry->d_inode;
-
if (!inode)
-
break;
-
err = -ENOTDIR;
-
if (!inode->i_op)
-
break;
-
}
如果inode操作结构体支持follow_link的话(比如FAT文件系统不支持软连接),那么就调用do_follow_link。
do_follow_link->__do_follow_link:
//首先调用follow_link来获得链接文件的路径名
- cookie = dentry->d_inode->i_op->follow_link(dentry, nd);
-
error = PTR_ERR(cookie);
-
if (!IS_ERR(cookie)) {
-
char *s = nd_get_link(nd);
-
error = 0;
-
if (s)
-
error = __vfs_follow_link(nd, s);
-
if (dentry->d_inode->i_op->put_link)
-
dentry->d_inode->i_op->put_link(dentry, nd, cookie);
-
}
首先需要调用各个文件系统具体的follow_link函数来获得链接导致指向何处。一般软连接没有具体的数据,而仅仅保持的是链接对象的路径名。要解析一个链接。首先需要获得链接指向对象的路径名。然后通过前面我们提到的path_walk来查找,即上面的__vfs_follow_link,那么为什么需要从VFS层来重新查找呢?因为链接的对象可能在另外一个设备上(即跨设备),需要从VFS层面来查找。另外为了防止链接陷入死循环(比如两个软连接相互指向)。在查找过程中需要对链接的深度做一定的限制。
阅读(2026) | 评论(0) | 转发(0) |