日期:2014-05-16  浏览次数:20797 次

Linux虚拟文件系统--文件路径名的解析(2)--回退父目录

     上文介绍了解析文件路径名的一个通体的过程,这里再把其中的一些细节拿出来进行分析。首先对于文件名的特点,可以分为三类:普通文件名,'.'和'..',对于'.'的处理很简单,因为它表示当前目录,因此直接通过continue进入下一轮查找即可,对于'..',也就是退回到父目录,本身也不是一件难事,但是这里要考虑到几个特殊情况,先看看内核处理'..'的方法:

static __always_inline void follow_dotdot(struct nameidata *nd)
{
	set_root(nd);

	while(1) {
		struct vfsmount *parent;
		struct dentry *old = nd->path.dentry;

		/*如果当前所处的目录即为根目录则break*/
		if (nd->path.dentry == nd->root.dentry &&
		    nd->path.mnt == nd->root.mnt) {
			break;
		}
		spin_lock(&dcache_lock);

		//如果当前所处的目录不为当前路径所属文件系统的根目录,也就是说可以直接向上退一级
		if (nd->path.dentry != nd->path.mnt->mnt_root) {
			nd->path.dentry = dget(nd->path.dentry->d_parent);//当前的目录退到上一级
			spin_unlock(&dcache_lock);
			dput(old);
			break;
		}

		/*下面的情况对应于当前所处的目录为文件系统的根目录*/
		spin_unlock(&dcache_lock);
		spin_lock(&vfsmount_lock);
		parent = nd->path.mnt->mnt_parent;//取父文件系统
		if (parent == nd->path.mnt) {//父文件系统即为本身,则表明没有父文件系统,直接break
			spin_unlock(&vfsmount_lock);
			break;
		}
		mntget(parent);//增加父文件系统的引用计数
		nd->path.dentry = dget(nd->path.mnt->mnt_mountpoint);//取当前文件系统的挂载点,这样就退回到了父文件系统
		spin_unlock(&vfsmount_lock);
		dput(old);
		mntput(nd->path.mnt);
		nd->path.mnt = parent;//设置当前路径的mnt为父文件系统
	}
	/*一般情况下,前面的操作可以保证返回到上级目录,但是有一种情况就是
	  当前目录的上级目录有可能还挂载了其他的文件系统,这样会隐藏之前的文件系统,
	  follow_mount()用来处理这种情况*/
	follow_mount(&nd->path);
}

 

while循环里面的路径可以分为三种:

1.当前目录为nd中已经预设好的根目录,也就是说无法再向上退一层了,这种情况直接break

2.当前目录不为所属文件系统的根目录,这种情况是最常见的,可以向上退一层

3.当前目录为所属文件系统的挂载点,这种情况下,后退一层的话则会进入到父文件系统中,所以先要做一个文件系统的交换,再通过while(1)循环回到前面两种情况

在一般情况下while(1)循环中的内容可以保证正确的退到父目录,但是考虑一种情况就是,当用户进入了一个目录后,其父目录上又重新挂载了一个文件系统,这时新的文件系统会覆盖旧的文件系统,使得之前的内容都被隐藏,因此这时要后退到上级目录则不是退回到原本所属的文件系统的目录了,而是退回到了最新的那个文件系统的挂载点。下面的一个例子可以用来说明这种情况:

1. 通过mkdir -p /mnt/test在/mnt目录下创建一个test目录

2. cd /mnt/test

3. ls ..   这时可以看到我们后退到的目录对应的是原文件系统,因此显示的是test

4. mount /dev/cdrom /mnt

5.ls ..   这时因为重新挂载了一个文件系统,因此test对应的文件系统被覆盖,所以显示的内容是cdrom根目录下的内容而看不到test了。

follow_mount()函数就是用来解决这么一个问题,即保证后退(或者前进)的目录是最新的那个文件系统对应的目录

 

static void follow_mount(struct path *path)
{
	while (d_mountpoint(path->dentry)) {//目录下有文件系统被安装
		struct vfsmount *mounted = lookup_mnt(path);//找到最先被挂载的子文件系统
		if (!mounted)//如果mounted为0,也就表示之前处理的子文件系统是最后一个子文件系统了,
			break;   //这个文件系统的mnt才是所要返回的上级目录的真正mnt
		dput(path->dentry);
		mntput(path->mnt);
		path->mnt = mounted;//保存vfsmount
		path->dentry = dget(mounted->mnt_root);//保存dentry,并更新引用计数
	}
}


 

1楼zi77642475昨天 21:00
终于有空写博客了!沙发依然在!