string查找子串失败返回值_LINUX VFS分析之do_sys_open接口的路径查找接口分析

以上几个小节说明了do_sys_open接口实现的简要说明、路径查找相关结构体的说明、路径查找初始化函数path_init的分析,本小节介绍link_path_walk接口,该接口实现了用户输入路径的查找操作,其对每一级路径均确认路径是否有效,并获取路径对应的dentry、inode等信息,下面详细介绍link_path_walk接口主要的调用流程,主要涉及普通路径的查找、链接路径的查找等内容。主要涉及的接口包括link_path_walk、walk_component、handle_dots、lookup_slow、lookup_dcache、lookup_real、nested_symlink、follow_link等接口函数的分析。

link_path_walk为路径查找接口,该接口对传入的文件路径,进行一级一级的查找,并对查找过程中遇到的链接目录进行进一步的查找,

在路径查找的过程中,可能会跨越多个文件系统,因此在查找过程中,需要更新struct nameidate类型的变量中的root参数。

路径查找接口的主要功能如下流程图,主要分为如下几个主要步骤:

1.首先去除路径名称起始字符中所有的'/'字符(这也是我们在实际linux系统中进入执行"cd //home/"命令,可正常进入家目录的原因);

2.针对传入的路径名称,循环进行查找:

a.若需要查找的目录,没有执行权限或者不能执行lookup操作,则返回查找失败;

b.若当前目录为最后一级路径(最后一级路径为文件或者为目录),则更新last_type、 last_str,该接口返回成功

c.若不是以上情况,则更新name指针的位置,移植下一级目录的首地址处

d.若不是上述b中的情况,则说明此处为一个目录项,则调用walk_component接口,查找本次目录对应的dentry以及inode:

>若查找失败,则返回路径查找失败;

>若查找成功且不是链接目录,则跳转至a,继续下一级路径的查找;

>若查找成功且是链接目录,则调用nested_symlink对链接路径对应的目标路径继续进行解

析:

i.若解析失败,则返回查找失败;

Ii.若解析成功,则跳转至a,继续下一级路径的查找。

73f724347e9b5ccff7a7cdcd160104b3.png

walk_component接口分析

该接口对当前的搜索路径进行搜索操作

1.若last_type不是LAST_NORM,则调用handle_dots进行处理,针对'.' 、'..'的情形更新nd->path变量的值,程序退出

2.若last_type是LAST_NORM,说明当前路径是正常的目录,则调用lookup_fast、lookup_slow进行查找。

a.首先调用lookup_fast,从nd->patn.dentry的hash链表中,根据nd->last.name查找子dentry:

若查找成功则返回0;

若没有查找到,则返回1,调用lookup_slow进行查找;

否则,则返回失败。

本次接口调用了三个主要的接口,handle_dots、lookup_fast、lookup_slow,后续详细分析下这三个接口,这三个接口主要

对应于处理".."目录、从dcache中查找目录项、从子目录的dentry中查找目录项。

static inline int walk_component(struct nameidata *nd, struct path *path,

int follow)

{

struct inode *inode;

int err;

/*

* "." and ".." are special - ".." especially so because it has

* to be able to know about the current root directory and

* parent relationships.

*/

if (unlikely(nd->last_type != LAST_NORM))

return handle_dots(nd, nd->last_type);

err = lookup_fast(nd, path, &inode);

/*若通过hash缓存中查找失败,则根据返回值确定是否需要调用lookup_slow进行继续查找:

若err小于0,则返回失败;

若err大于0,则调用lookup_slow进行继续查找。*/

if (unlikely(err)) {

if (err < 0)

goto out_err;

/*调用lookup_slow查找符合要求的子dentry变量。*/

err = lookup_slow(nd, path);

if (err < 0)

goto out_err;

/*若查找成功,则获取该新dentry对应的inode节点变量*/

inode = path->dentry->d_inode;

}

err = -ENOENT;

if (!inode)

goto out_path_put;

/*确认该dentr是否为链接目录,若为链接目录则返回1,由link_path_walk做进一步处理*/

if (should_follow_link(inode, follow)) {

if (nd->flags & LOOKUP_RCU) {

if (unlikely(unlazy_walk(nd, path->dentry))) {

err = -ECHILD;

goto out_err;

}

}

BUG_ON(inode != path->dentry->d_inode);

return 1;

}

/*若不是链接目录,则更新nd->path、nd->inode的值*/

path_to_nameidata(path, nd);

nd->inode = inode;

return 0;

out_path_put:

path_to_nameidata(path, nd);

out_err:

terminate_walk(nd);

return err;

}

handle_dots接口分析

该接口主要处理LAST_DOT、LAST_DOTDOT这两种类型:

1.对于LAST_DOTDOT,则需要跳转至父目录,具体调用follow_dotdot

2.对于LAST_DOT,则表示为当前目录,直接返回即可。

static inline int handle_dots(struct nameidata *nd, int type)

{

if (type == LAST_DOTDOT) {

if (nd->flags & LOOKUP_RCU) {

if (follow_dotdot_rcu(nd))

return -ECHILD;

} else

follow_dotdot(nd);

}

return 0;

}

针对follow_dotdot接口,其定义如下:

该接口主要处理LAST_DOTDOT类型的路径变量,需要跳转至父目录,有如下几种情况

1.若当前搜索路径的dentry以及该dentry对应文件系统的mnt,即为当前进程的root路径及其mnt,则不能再往上查找,仍然以该目录作为查找路径。

2.若当前路径的dentry不是当前路径对应的文件系统的根dentry,则说明当前路径的父路径仍在相同的文件系统下,获取当前dentry的父节点即可。

3.若当前搜索路径的dentry即为其所在文件系统的根dentry,则调用follow_up进入其所挂载目录的上一级dentry,并修改nd->path中的mnt、dentry

成员变量以及nd->inode的值。

static void follow_dotdot(struct nameidata *nd)

{

set_root(nd);

while(1) {

struct dentry *old = nd->path.dentry;

if (nd->path.dentry == nd->root.dentry &&

nd->path.mnt == nd->root.mnt) {

break;

}

if (nd->path.dentry != nd->path.mnt->mnt_root) {

/* rare case of legitimate dget_parent()... */

/*更新nd->path的dentry为其父dentry*/

nd->path.dentry = dget_parent(nd->path.dentry);

dput(old);

break;

}

/*若当前搜索路径的dentry即为其所在文件系统的根dentry,则调用follow_up进入其所挂载目录的上一级dentry,并修改nd->path中的mnt、dentry成员变量以及nd->inode的值。*/

if (!follow_up(&nd->path))

break;

}

/*更新nd->path.mnt变量*/

follow_mount(&nd->path);

nd->inode = nd->path.dentry->d_inode;

}

针对其调用的follow_up接口,该接口为获取当前挂载点目录的dentry

1.通过path->mnt,获取该文件系统对应struct mount 类型的指针变量;

2.根据上述获取的struct mount类型的指针变量,获取挂载点对应目录的dentry以及其父mount的值,并赋值该struct path类型的指针变量中

int follow_up(struct path *path)

{

struct mount *mnt = real_mount(path->mnt);

struct mount *parent;

struct dentry *mountpoint;

br_read_lock(&vfsmount_lock);

parent = mnt->mnt_parent;

if (parent == mnt) {

br_read_unlock(&vfsmount_lock);

return 0;

}

mntget(&parent->mnt);

mountpoint = dget(mnt->mnt_mountpoint);

br_read_unlock(&vfsmount_lock);

dput(path->dentry);

path->dentry = mountpoint;

mntput(path->mnt);

path->mnt = &parent->mnt;

return 1;

}

lookup_fast接口分析

该接口的作用是在dentry的hash缓存链表中,查找名称为nd->last的dentry,且其parent dentry为nd->path.dentry

static int lookup_fast(struct nameidata *nd,

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;

/*

* Rename seqlock is not required here because in the off chance

* of a false negative due to a concurrent rename, we're going to

* do the non-racy lookup, below.

*/

/*此处为nd->flags标记为LOOKUP_RCU时的处理流程*/

if (nd->flags & LOOKUP_RCU) {

unsigned seq;

dentry = __d_lookup_rcu(parent, &nd->last, &seq, nd->inode);

if (!dentry)

goto unlazy;

/*

* This sequence count validates that the inode matches

* the dentry name information from lookup.

*/

*inode = dentry->d_inode;

if (read_seqcount_retry(&dentry->d_seq, seq))

return -ECHILD;

/*

* This sequence count validates that the parent had no

* changes while we did the lookup of the dentry above.

*

* The memory barrier in read_seqcount_begin of child is

* enough, we can use __read_seqcount_retry here.

*/

if (__read_seqcount_retry(&parent->d_seq, nd->seq))

return -ECHILD;

nd->seq = seq;

if (unlikely(dentry->d_flags & DCACHE_OP_REVALIDATE)) {

status = d_revalidate(dentry, nd->flags);

if (unlikely(status <= 0)) {

if (status != -ECHILD)

need_reval = 0;

goto unlazy;

}

}

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;

unlazy:

if (unlazy_walk(nd, dentry))

return -ECHILD;

} else {

/*调用__d_lookup,从parent的hash链表中查找符合条件的子dentry(目录名称为nd->last.name)*/

dentry = __d_lookup(parent, &nd->last);

}

/*若没有查找到,则将结果设置为1,需要调用lookup_slow继续进行查找*/

if (unlikely(!dentry))

goto need_lookup;

/*若需要重新使能该dentry,则调用d_revalidate使能该dentry*/

if (unlikely(dentry->d_flags & DCACHE_OP_REVALIDATE) && need_reval)

status = d_revalidate(dentry, nd->flags);

/*若status小于等于0,则说明调用d_revalidate使能dentry失败,则返回失败*/

if (unlikely(status <= 0)) {

if (status < 0) {

dput(dentry);

return status;

}

if (!d_invalidate(dentry)) {

dput(dentry);

goto need_lookup;

}

}

/*设置path变量的mnt、dentry变量,并调用*/

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;

need_lookup:

return 1;

}

lookup_slow接口分析

该接口实现的功能如下:

1.调用__lookup_hash查找符合要求的子dentry(该接口包括两种查找方式:1.从dcache中查找,成功则返回;2.若dcache中查找失败,

则调用dentry的lookup接口进行查找);

2.若查找成功,则更新path的mnt、dentry变量,并调用follow_managed对查找到的dentry变量,确认是否进行autofs、mountpoint、

automountpoint的处理

static int lookup_slow(struct nameidata *nd, struct path *path)

{

struct dentry *dentry, *parent;

int err;

parent = nd->path.dentry;

BUG_ON(nd->inode != parent->d_inode);

mutex_lock(&parent->d_inode->i_mutex);

dentry = __lookup_hash(&nd->last, parent, nd->flags);

mutex_unlock(&parent->d_inode->i_mutex);

if (IS_ERR(dentry))

return PTR_ERR(dentry);

path->mnt = nd->path.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;

return 0;

}

针对__lookup_hash接口,其功能如下:

该接口的主要功能是从base的子dentry中查找符合要求的dentry

1.首先调用lookup_dcache接口,从base的hash链表中查找符合要求的子dentry:

若查找到则返回该dentry;

若没有查找到,则申请一个dentry变量,并标记need_lookup变量,需要lookup_real继续查找

2.调用lookup_real接口,调用该dentry对应的inode节点的lookup接口,查找符合要求的子dentry。

static struct dentry *__lookup_hash(struct qstr *name,

struct dentry *base, unsigned int flags)

{

bool need_lookup;

struct dentry *dentry;

dentry = lookup_dcache(name, base, flags, &need_lookup);

if (!need_lookup)

return dentry;

return lookup_real(base->d_inode, dentry, flags);

}

lookup_dcache接口分析

该接口主要实现如下功能:

1.调用d_lookup,查找dentry->d_hash链表中是否存在符合要求的子dentry:

若查找到,则使能该dentry;

若没有查找到,则根据查找的子dentry名称,申请一个新的dentry类型的内存空间,并设置need_lookup为true

static struct dentry *lookup_dcache(struct qstr *name, struct dentry *dir,

unsigned int flags, bool *need_lookup)

{

struct dentry *dentry;

int error;

*need_lookup = false;

dentry = d_lookup(dir, name);

if (dentry) {

if (dentry->d_flags & DCACHE_OP_REVALIDATE) {

error = d_revalidate(dentry, flags);

if (unlikely(error <= 0)) {

if (error < 0) {

dput(dentry);

return ERR_PTR(error);

} else if (!d_invalidate(dentry)) {

dput(dentry);

dentry = NULL;

}

}

}

}

if (!dentry) {

dentry = d_alloc(dir, name);

if (unlikely(!dentry))

return ERR_PTR(-ENOMEM);

*need_lookup = true;

}

return dentry;

}

lookup_real接口分析

该接口主要调用dentry的lookup接口进行子目录项的查找,这样就进入了每个文件系统的目录项查找接口。

该接口实现功能如下:

1.若该dentry为deaddir,则返回失败;

2.调用该dentry对应inode节点的lookup接口,执行查找操作,并返回查找结果

static struct dentry *lookup_real(struct inode *dir, struct dentry *dentry,

unsigned int flags)

{

struct dentry *old;

/* Don't create child dentry for a dead directory. */

if (unlikely(IS_DEADDIR(dir))) {

dput(dentry);

return ERR_PTR(-ENOENT);

}

old = dir->i_op->lookup(dir, dentry, flags);

if (unlikely(old)) {

dput(dentry);

dentry = old;

}

return dentry;

}

nested_symlink接口分析

该接口主要用于解析链接路径对应的目标路径,该接口的定义如下,其实现的功能如下(在进行路径查找时,可能会跨越不同的文件系统):

该接口主要是对进行链接路径的查找,具体实现如下功能:

1.判断当前查找的链接计数是否超过限制范围,若超过,返回失败;

2.调用follow_link进行链接路径的查找,进入链接目录对应的target目录(该接口最终调用link_path_walk进行查找操作);

3.接着调用walk_component,进行子路径的查找与更新更新nd->path、nd->inode等信息

static inline int nested_symlink(struct path *path, struct nameidata *nd)

{

int res;

/*如果当前进程查找路径出现的链接次数超过MAX_NESTED_LINKS,则返回失败*/

if (unlikely(current->link_count >= MAX_NESTED_LINKS)) {

path_put_conditional(path, nd);

path_put(&nd->path);

return -ELOOP;

}

/*如果当前nd->depth的值大于MAX_NESTED_LINKS,则返回失败*/

BUG_ON(nd->depth >= MAX_NESTED_LINKS);

/*增加nd与当前进程的depth与link_count值*/

nd->depth++;

current->link_count++;

do {

struct path link = *path;

void *cookie;

res = follow_link(&link, nd, &cookie);

if (res)

break;

/*本次路径搜索,更新相应的nd->path、nd->inode等信息*/

res = walk_component(nd, path, LOOKUP_FOLLOW);

put_link(nd, &link, cookie);

} while (res > 0);

/*当完成链接路径的查找后,则减小current->link_count、nd->depth的计数*/

current->link_count--;

nd->depth--;

return res;

}

follow_link接口分析

针对follow_link,该接口的作用是进入链接目录对应的target目录,即进入一个链接目录,这可能涉及到文件系统的跨越,

需要更新path.mnt、path.dentry

1.判断当前进程的total_link_count是否超过限制,若超过限制,返回失败;

2.调用nd_set_link,设置nd->saved_names[nd->depth]值为空,等下用于存放当前链接目录所链接的目标目录的路径值;

3.调用各文件系统follow_link的接口,设置nd->saved_names[nd->depth],主要是用于__vfs_follow_link对软链接的目录进行路径查找。

4.__vfs_follow_link接口主要是调用link_path_walk进行链接路径的查找操作。

*/

static __always_inline int

follow_link(struct path *link, struct nameidata *nd, void **p)

{

struct dentry *dentry = link->dentry;

int error;

char *s;

BUG_ON(nd->flags & LOOKUP_RCU);

if (link->mnt == nd->path.mnt)

mntget(link->mnt);

error = -ELOOP;

if (unlikely(current->total_link_count >= 40))

goto out_put_nd_path;

cond_resched();

/*该变量主要记录一次路径查找中,所解析的链接路径的次数,当链接路径的次数超过40后,亦返回失败*/

current->total_link_count++;

touch_atime(link);

nd_set_link(nd, NULL);

error = security_inode_follow_link(link->dentry, nd);

if (error)

goto out_put_nd_path;

/*针对链接路径,需将其last_type设置为LAST_BIND*/

nd->last_type = LAST_BIND;

*p = dentry->d_inode->i_op->follow_link(dentry, nd);

error = PTR_ERR(*p);

if (IS_ERR(*p))

goto out_put_nd_path;

error = 0;

s = nd_get_link(nd);

if (s) {

error = __vfs_follow_link(nd, s);

if (unlikely(error))

put_link(nd, link, *p);

}

return error;

out_put_nd_path:

*p = NULL;

path_put(&nd->path);

path_put(link);

return error;

}

__vfs_follow_link接口主要是对传入的路径“link”,进行路径查找

主要是调用link_path_walk,执行路径查找操作

*/

static __always_inline int __vfs_follow_link(struct nameidata *nd, const char *link)

{

int ret;

if (IS_ERR(link))

goto fail;

if (*link == '/') {

set_root(nd);

path_put(&nd->path);

nd->path = nd->root;

path_get(&nd->root);

nd->flags |= LOOKUP_JUMPED;

}

nd->inode = nd->path.dentry->d_inode;

ret = link_path_walk(link, nd);

return ret;

fail:

path_put(&nd->path);

return PTR_ERR(link);

}

至此完成路径查找的分析,主要包括普通路径的查找、链接路径的查找等信息。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值