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_FOLLOWLOOKUP_DIRECTORY都设成1

标志LOOKUP_PARENT表示要寻找的并不是路径的终点,而是它的上一层。