Linux内核源代码情景分析-文件系统安装后的访问

    在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(&current->fs->lock);
		if (nd->dentry == current->fs->root &&
		    nd->mnt == current->fs->rootmnt)  {//已到达的nd->dentry就是本进程的根节点,这时不能再往上跑了,所以保持nd->dentry不变
			read_unlock(&current->fs->lock);
			break;
		}
		read_unlock(&current->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结构
	}
}

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。