【kern_path--->do_path_lookup--->path_lookupat--->link_path_walk】
比如要搜索文件test.c,它的路径为/home/mywork/testdir/test.c,下面函数要做的工作就是循环搜索路径/home/mywork/testdir/的各个节点,然后将test.c对应的文件名,文件名长度,哈希值存放到nd->last 中。
static int link_path_walk(const char *name, struct nameidata *nd)
{
struct path next;
int err;
while (*name=='/')
name++;
if (!*name)
return 0;
去掉路径前面的'/',之所以要用用while循环,是因为有些路径前有多个'/',比如//usr/lib。
for(;;) {
unsigned long hash;
struct qstr this;
unsigned int c;
int type;
err = may_lookup(nd);
if (err)
break;
this.name = name;
c = *(const unsigned char *)name;
hash = init_name_hash();
do {
name++;
hash = partial_name_hash(c, hash);
c = *(const unsigned char *)name;
} while (c && (c != '/'));
this.len = name - (const char *) this.name;
this.hash = end_name_hash(hash);
以'/'为界取出路径中的一个节点,用以填充结构struct qstr。该结构在文件dcache.h中实现,如下:
struct qstr {
unsigned int hash;//根据文件名计算的哈希值
unsigned int len;//文件名长度
const unsigned char *name;//指向文件名
};
type = LAST_NORM;
if (this.name[0] == '.') switch (this.len) {
case 2:
if (this.name[1] == '.') {
type = LAST_DOTDOT;
nd->flags |= LOOKUP_JUMPED;
}
break;
case 1:
type = LAST_DOT;
}
if (likely(type == LAST_NORM)) {
struct dentry *parent = nd->path.dentry;
nd->flags &= ~LOOKUP_JUMPED;
if (unlikely(parent->d_flags & DCACHE_OP_HASH)) {
err = parent->d_op->d_hash(parent, nd->inode,
&this);
if (err < 0)
break;
}
}
首先将type设为LAST_NORM,表示假设找到的是一个正常的路径节点。如果文件名中有'.'或者是'..'则将type设为LAST_DOT或者LAST_DOTDOT。如果是一个正常的路径节点则通过相应文件系统函数parent->d_op->d_hash计算路径节点名的哈希值。
if (!c)
goto last_component;
while (*++name == '/');
if (!*name)
goto last_component;
如果已经找到路径的最后节点则做最后的处理然后退出函数。
err = walk_component(nd, &next, &this, type, LOOKUP_FOLLOW);
if (err < 0)
return err;
if (err) {
err = nested_symlink(&next, nd);
if (err)
return err;
}
if (can_lookup(nd->inode))
continue;
err = -ENOTDIR;
break;
函数walk_component用于搜索路径的中间节点。如果是一个链接文件,通过函数nested_symlink来找到链接文件所连接的实际文件,并找到该实际文件对应的目录项。
last_component:
nd->last = this;
nd->last_type = type;
return 0;
}
保存路径最终节点到nd->last,记录搜索结果状态到nd->last_type。
terminate_walk(nd);
return err;
}
【kern_path--->do_path_lookup--->path_lookupat--->link_path_walk--->walk_component】
搜索路径中name的所对应的目录对象,将搜索结果存在path中,并用path初始化nd->path。
static inline int walk_component(struct nameidata *nd, struct path *path,
struct qstr *name, int type, int follow)
{
struct inode *inode;
int err;
if (unlikely(type != LAST_NORM))
return handle_dots(nd, type);
err = do_lookup(nd, name, path, &inode);
if (unlikely(err)) {
terminate_walk(nd);
return err;
}
if (!inode) {
path_to_nameidata(path, nd);
terminate_walk(nd);
return -ENOENT;
}
如果type不为LAST_NORM即不是一个正常的路径节点,比如为LAST_DOTDOT(查找上一层目录)。在这种情况下调用函数handle_dots来搜索对应目录对象。如果是一个正常的路径节点则通过函数do_lookup来找到对应的目录对象。并将找到的文件的索引节点地址存放在inode中。
if (should_follow_link(inode, follow)) {
if (nd->flags & LOOKUP_RCU) {
if (unlikely(unlazy_walk(nd, path->dentry))) {
terminate_walk(nd);
return -ECHILD;
}
}
BUG_ON(inode != path->dentry->d_inode);
return 1;
}
path_to_nameidata(path, nd);
nd->inode = inode;
return 0;
}
如果找到的目录项是连接文件则返回1,否则用文件对应的path 初始化nd->path。
【kern_path--->do_path_lookup--->path_lookupat--->link_path_walk--->walk_component--->do_lookup】
查找路径节点。
static int do_lookup(struct nameidata *nd, struct qstr *name,
struct path *path, struct inode **inode)
{
struct vfsmount *mnt = nd->path.mnt;
struct dentry *dentry, *parent = nd->path.dentry;
int need_reval = 1;
int status = 1;
int err;
if (nd->flags & LOOKUP_RCU) {
unsigned seq;
*inode = nd->inode;
dentry = __d_lookup_rcu(parent, name, &seq, inode);
......
path->mnt = mnt;
path->dentry = dentry;
if (unlikely(!__follow_mount_rcu(nd, path, inode)))
goto unlazy;
if (unlikely(path->dentry->d_flags & DCACHE_NEED_AUTOMOUNT))
goto unlazy;
return 0;
通过函数__d_lookup_rcu到缓存中寻找目标目录节点,如果找到则用相关信息来初始化path结构。如果该目录是一个挂载点则通过函数__follow_mount_rcu重新初始化path结构。
unlazy:
if (unlazy_walk(nd, dentry))
return -ECHILD;
} else {
dentry = __d_lookup(parent, name);
}
if (dentry && unlikely(d_need_lookup(dentry))) {
dput(dentry);
dentry = NULL;
}
retry:
if (unlikely(!dentry)) {
struct inode *dir = parent->d_inode;
BUG_ON(nd->inode != dir);
mutex_lock(&dir->i_mutex);
dentry = d_lookup(parent, name);
if (likely(!dentry)) {
dentry = d_alloc_and_lookup(parent, name, nd);
if (IS_ERR(dentry)) {
mutex_unlock(&dir->i_mutex);
return PTR_ERR(dentry);
}
need_reval = 0;
status = 1;
} else if (unlikely(d_need_lookup(dentry))) {
dentry = d_inode_lookup(parent, dentry, nd);
if (IS_ERR(dentry)) {
mutex_unlock(&dir->i_mutex);
return PTR_ERR(dentry);
}
need_reval = 0;
status = 1;
}
mutex_unlock(&dir->i_mutex);
}
建立dentry结构的过程不能受到其他进程的打扰,所以通过互斥量放在临界区中进行。但是,进入临界区可能会经历一段睡眠等待时间,而其他进程可能已经在这段时间中把所需的dentry结构建立好了,再建立一个就重复了。所以在进入临界区以后还要通过函数d_lookup查找一遍以确认所需的dentry结构确实不在杂凑表队列中。如果没找到需要的dentry结构,则通过函数d_alloc_and_lookup分配一个dentry结构,并在父目录中找到当前节点的目录项,初始化该结构中的其他项。如果找到了需要的dentry结构,则通过函数d_inode_lookup在父目录中找到当前节点的目录项,并初始化该结构中的其他项。
if (unlikely(dentry->d_flags & DCACHE_OP_REVALIDATE) && need_reval)
status = d_revalidate(dentry, nd);
if (unlikely(status <= 0)) {
if (status < 0) {
dput(dentry);
return status;
}
if (!d_invalidate(dentry)) {
dput(dentry);
dentry = NULL;
need_reval = 1;
goto retry;
}
}
path->mnt = mnt;
path->dentry = dentry;
err = follow_managed(path, nd->flags);
if (unlikely(err < 0)) {
path_put_conditional(path, nd);
return err;
}
if (err)
nd->flags |= LOOKUP_JUMPED;
*inode = path->dentry->d_inode;
return 0;
}
建立dentry结构后初始化path结构,然后通过函数follow_managed重新初始化path结构。最后将*inode指向path->dentry->d_inode。
【kern_path--->do_path_lookup--->path_lookupat--->link_path_walk--->nested_symlink】
如果文件是一个链接文件,则跟随链接直到找到实际文件对应的目录项为止。
static inline int nested_symlink(struct path *path, struct nameidata *nd)
{
int res;
if (unlikely(current->link_count >= MAX_NESTED_LINKS)) {
path_put_conditional(path, nd);
path_put(&nd->path);
return -ELOOP;
}
BUG_ON(nd->depth >= MAX_NESTED_LINKS);
nd->depth++;
current->link_count++;
do {
struct path link = *path;
void *cookie;
res = follow_link(&link, nd, &cookie);
if (!res)
res = walk_component(nd, path, &nd->last,
nd->last_type, LOOKUP_FOLLOW);
put_link(nd, &link, cookie);
} while (res > 0);
current->link_count--;
nd->depth--;
return res;
}
在函数follow_link中通过调用对应文件系统函数dentry->d_inode->i_op->follow_link查找符号链接实际指向的索引节点。然后调用函数walk_component搜索到实际节点的目录项。在上面循环中重复前述操作直到最终实际文件为止。
【kern_path--->do_path_lookup--->lookup_last】
搜索最终的目标设备节点。
static inline int lookup_last(struct nameidata *nd, struct path *path)
{
if (nd->last_type == LAST_NORM && nd->last.name[nd->last.len])
nd->flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
nd->flags &= ~LOOKUP_PARENT;
return walk_component(nd, path, &nd->last, nd->last_type,
nd->flags & LOOKUP_FOLLOW);
}
如果搜索的终点是个目录,并且,如果这个节点代表着一个连接就一定要前进到所连接的对象(也是个目录)。所以在这种情况下把标志位LOOKUP_FOLLOW和LOOKUP_DIRECTORY都设成1。
标志LOOKUP_PARENT表示要寻找的并不是路径的终点,而是它的上一层。