在Linux内核源代码情景分析-文件系统的安装,一文中,已经调用sudo mount -t ext2 /dev/sdb1 /mnt/sdb,在/mnt/sdb节点上挂载了文件系统,那么我们接下来访问/mnt/sdb/hello.c节点。我们来看一下path_walk的执行有什么不同?
int path_walk(const char * name, struct nameidata *nd)
{
struct dentry *dentry;
struct inode *inode;
int err;
unsigned int lookup_flags = nd->flags;
while (*name=='/')
name++;
if (!*name)
goto return_base;
inode = nd->dentry->d_inode;
if (current->link_count)
lookup_flags = LOOKUP_FOLLOW;
/* At this point we know we have a real path component. */
for(;;) {
unsigned long hash;
struct qstr this;
unsigned int c;
err = permission(inode, MAY_EXEC);
dentry = ERR_PTR(err);
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);
/* remove trailing slashes? */
if (!c)
goto last_component;
while (*++name == '/');
if (!*name)
goto last_with_slashes;
/*
* "." and ".." are special - ".." especially so because it has
* to be able to know about the current root directory and
* parent relationships.
*/
if (this.name[0] == '.') switch (this.len) {
default:
break;
case 2:
if (this.name[1] != '.')
break;
follow_dotdot(nd);
inode = nd->dentry->d_inode;
/* fallthrough */
case 1:
continue;
}
/*
* See if the low-level filesystem might want
* to use its own hash..
*/
if (nd->dentry->d_op && nd->dentry->d_op->d_hash) {
err = nd->dentry->d_op->d_hash(nd->dentry, &this);
if (err < 0)
break;
}
/* This does the actual lookups.. */
dentry = cached_lookup(nd->dentry, &this, LOOKUP_CONTINUE);
if (!dentry) {
dentry = real_lookup(nd->dentry, &this, LOOKUP_CONTINUE);
err = PTR_ERR(dentry);
if (IS_ERR(dentry))
break;
}
/* Check mountpoints.. */
while (d_mountpoint(dentry) && __follow_down(&nd->mnt, &dentry))
;
err = -ENOENT;
inode = dentry->d_inode;
if (!inode)
goto out_dput;
err = -ENOTDIR;
if (!inode->i_op)
goto out_dput;
if (inode->i_op->follow_link) {
err = do_follow_link(dentry, nd);
dput(dentry);
if (err)
goto return_err;
err = -ENOENT;
inode = nd->dentry->d_inode;
if (!inode)
break;
err = -ENOTDIR;
if (!inode->i_op)
break;
} else {
dput(nd->dentry);
nd->dentry = dentry;
}
err = -ENOTDIR;
if (!inode->i_op->lookup)
break;
continue;
/* here ends the main loop */
last_with_slashes:
lookup_flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
last_component:
if (lookup_flags & LOOKUP_PARENT)
goto lookup_parent;
if (this.name[0] == '.') switch (this.len) {
default:
break;
case 2:
if (this.name[1] != '.')
break;
follow_dotdot(nd);
inode = nd->dentry->d_inode;
/* fallthrough */
case 1:
goto return_base;
}
if (nd->dentry->d_op && nd->dentry->d_op->d_hash) {
err = nd->dentry->d_op->d_hash(nd->dentry, &this);
if (err < 0)
break;
}
dentry = cached_lookup(nd->dentry, &this, 0);
if (!dentry) {
dentry = real_lookup(nd->dentry, &this, 0);
err = PTR_ERR(dentry);
if (IS_ERR(dentry))
break;
}
while (d_mountpoint(dentry) && __follow_down(&nd->mnt, &dentry))//在找到/mnt/sdb节点后,会执行到这里,也是不同的所在
;
inode = dentry->d_inode;
if ((lookup_flags & LOOKUP_FOLLOW)
&& inode && inode->i_op && inode->i_op->follow_link) {
err = do_follow_link(dentry, nd);
dput(dentry);
if (err)
goto return_err;
inode = nd->dentry->d_inode;
} else {
dput(nd->dentry);
nd->dentry = dentry;
}
err = -ENOENT;
if (!inode)
goto no_inode;
if (lookup_flags & LOOKUP_DIRECTORY) {
err = -ENOTDIR;
if (!inode->i_op || !inode->i_op->lookup)
break;
}
goto return_base;
no_inode:
err = -ENOENT;
if (lookup_flags & (LOOKUP_POSITIVE|LOOKUP_DIRECTORY))
break;
goto return_base;
lookup_parent:
nd->last = this;
nd->last_type = LAST_NORM;
if (this.name[0] != '.')
goto return_base;
if (this.len == 1)
nd->last_type = LAST_DOT;
else if (this.len == 2 && this.name[1] == '.')
nd->last_type = LAST_DOTDOT;
return_base:
return 0;
out_dput:
dput(dentry);
break;
}
path_release(nd);
return_err:
return err;
}
d_mountpoint,查看是否是挂载点。
static __inline__ int d_mountpoint(struct dentry *dentry)
{
return !list_empty(&dentry->d_vfsmnt);
}
还记得在
Linux内核源代码情景分析-文件系统的安装,一文中,最后add_vfsmnt。
list_add(&mnt->mnt_clash, &nd->dentry->d_vfsmnt);
所以这里非空,返回true,接着执行__follow_down,代码如下:
static inline int __follow_down(struct vfsmount **mnt, struct dentry **dentry)
{
struct list_head *p;
spin_lock(&dcache_lock);
p = (*dentry)->d_vfsmnt.next;
while (p != &(*dentry)->d_vfsmnt) {//参考文件系统的安装的add_vfsmnt
struct vfsmount *tmp;
tmp = list_entry(p, struct vfsmount, mnt_clash);
if (tmp->mnt_parent == *mnt) { //参考文件系统的安装的add_vfsmnt
*mnt = mntget(tmp);//nd->mnt为被安装设备的vfsmount结构
spin_unlock(&dcache_lock);
mntput(tmp->mnt_parent);
/* tmp holds the mountpoint, so... */
dput(*dentry);
*dentry = dget(tmp->mnt_root);//dentry为被安装设备的根节点的dentry结构
return 1;
}
p = p->next;
}
spin_unlock(&dcache_lock);
return 0;
}
还有一个知识点,就是对于中间节点是dot或者dotdot的处理。
if (this.name[0] == '.') switch (this.len) {
default:
break;
case 2:
if (this.name[1] != '.')
break;
follow_dotdot(nd);
inode = nd->dentry->d_inode;
/* fallthrough */
case 1:
continue;
如果是dot,那么就继续下一次循环。如果是dotdot,会调用follow_dotdot,代码如下:
static inline void follow_dotdot(struct nameidata *nd)
{
while(1) {
struct vfsmount *parent;
struct dentry *dentry;
read_lock(¤t->fs->lock);
if (nd->dentry == current->fs->root &&
nd->mnt == current->fs->rootmnt) {//已到达的nd->dentry就是本进程的根节点,这时不能再往上跑了,所以保持nd->dentry不变
read_unlock(¤t->fs->lock);
break;
}
read_unlock(¤t->fs->lock);
spin_lock(&dcache_lock);
if (nd->dentry != nd->mnt->mnt_root) {//已到达nd->dentry不等于根节点的dentry结构
dentry = dget(nd->dentry->d_parent);
spin_unlock(&dcache_lock);
dput(nd->dentry);
nd->dentry = dentry;//dentry结构往上走,mnt结构不变
break;
}
parent=nd->mnt->mnt_parent;//走到这里,说明nd->dentry已经是根节点的dentry结构,只能跑到另一个设备上了,读到父设备的vfsmount结构
if (parent == nd->mnt) {//如果父设备的vfsmount结构和本设备的vmfmount结构一样,那就不能再往父设备上跑了
spin_unlock(&dcache_lock);
break;
}
mntget(parent);//如果父设备的vfsmount结构和本设备的vmfmount结构不一样
dentry=dget(nd->mnt->mnt_mountpoint);//dentry为当前设备的挂载节点的dentry结构
spin_unlock(&dcache_lock);
dput(nd->dentry);
nd->dentry = dentry;
mntput(nd->mnt);
nd->mnt = parent;//nd->mnt也被赋值为父设备的vfsmount结构
}
}