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

Linux虚拟文件系统--文件路径名的解析(上)

        文件路径名的解析是VFS中最基本也是最频繁用到的一个部分,它的代码实现还是十分繁杂的,主要是因为除了普通文件名的解析,内核还要考虑各种可能出现的情况,如一个目录下挂载了多个文件系统,路径中的符号链接等等……后面我会分几次将整个过程进行一个尽量仔细的分析,其中所涉及到的各种数据结构在ULK等相关内核书籍上都有比较详细的介绍,就不列出来了

       文件路径名的解析路口函数为path_lookup(),如下:

int path_lookup(const char *name, unsigned int flags,
			struct nameidata *nd)
{
	return do_path_lookup(AT_FDCWD, name, flags, nd);
}

name:路径名

flags:查找操作的标识

struct nameidata:用于保存当前的相关查找结果


这里可以看到path_lookup()只是对do_path_lookup()的一层封装

 

static int do_path_lookup(int dfd, const char *name,
				unsigned int flags, struct nameidata *nd)
{
	/*path_init进行一些搜索前的初始化工作,主要是确定起始搜索的起点并保存在nd中*/
	int retval = path_init(dfd, name, flags, nd);
	
	if (!retval)//初始化没问题的话就开始解析
		retval = path_walk(name, nd);
	if (unlikely(!retval && !audit_dummy_context() && nd->path.dentry &&
				nd->path.dentry->d_inode))
		audit_inode(name, nd->path.dentry);
	if (nd->root.mnt) {
		path_put(&nd->root);
		nd->root.mnt = NULL;
	}
	return retval;
}

 

 

static int path_init(int dfd, const char *name, unsigned int flags, struct nameidata *nd)
{
	int retval = 0;
	int fput_needed;
	struct file *file;

	nd->last_type = LAST_ROOT; /* if there are only slashes... */
	nd->flags = flags;
	nd->depth = 0;
	nd->root.mnt = NULL;

	if (*name=='/') {//如果文件路径是以绝对路径的形式给出
		set_root(nd);//设置nd的根目录
		nd->path = nd->root;//当前的path从根目录开始
		path_get(&nd->root);
	} else if (dfd == AT_FDCWD) {
		struct fs_struct *fs = current->fs;
		read_lock(&fs->lock);
		nd->path = fs->pwd;//获取当前目录的path
		path_get(&fs->pwd);
		read_unlock(&fs->lock);
	} else {
		struct dentry *dentry;

		/*根据dfd,从当前进程的fs_struct结构的fdtable中取出文件描述符指针*/
		file = fget_light(dfd, &fput_needed);
		retval = -EBADF;
		if (!file)
			goto out_fail;
		//从文件描述符中获取dentry
		dentry = file->f_path.dentry;

		retval = -ENOTDIR;
		if (!S_ISDIR(dentry->d_inode->i_mode))//如果不是目录
			goto fput_fail;
		//相应的权限检查
		retval = file_permission(file, MAY_EXEC);
		if (retval)
			goto fput_fail;

		nd->path = file->f_path;//获取path
		path_get(&file->f_path);

		fput_light(file, fput_needed);
	}
	return 0;

fput_fail:
	fput_light(file, fput_needed);
out_fail:
	return retval;
}

注意之前传递进来的dfd为AT_FDCWD,因此path_init中只有可能出现前两种情况:1.路径以绝对路径的方式给出 2.路径以相对路径的方式给出。此时nd中的path保存了查找的起始目录,对于第一种情况,即为'/',第二种情况起始目录要从当前进程的fs结构中提取。

下面通过path_walk()开始进行解析,我们直接进入path_walk()-->link_path_walk()-->__link_path_walk()进行分析,因为前面几个函数都没做什么实质性的工作。

static int __link_path_walk(const char *name, struct nameidata *nd)
{
	struct path next;
	struct inode *inode;
	int err;
	unsigned int lookup_flags = nd->flags;
	
	while (*name=='/')//忽略文件名前面的'/'
		name++;
	if (!*name)
		goto return_reval;

	inode = nd->path.dentry->d_inode;//获取当前目录的inode
	if (nd->depth)
		lookup_flags = LOOKUP_FOLLOW | (nd->flags & LOOKUP_CONTINUE);

	/* At this point we know we have a real path component. */
	for(;;) {
		unsigned long hash;
		struct qstr this;
		unsigned int c;

		nd->flags |= LOOKUP_CONTINUE;
		err = exec_permission_lite(inode);//进行访问权限的检查
 		if (err)