`
xitong
  • 浏览: 6196432 次
文章分类
社区版块
存档分类
最新评论

Linux虚拟文件系统--文件路径名的解析(1)--整体过程

 
阅读更多

文件路径名的解析是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)
			break;
		
		this.name = name;//当前进行解析的文件名分量
		c = *(const unsigned char *)name;

		hash = init_name_hash();//hash值初始化为0
		/*计算当前文件名分量的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);//保存当前文件名分量的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://文件名长度为2并且下一个字符也为'.'则表明要退回到上级目录	
				if (this.name[1] != '.')
					break;
				follow_dotdot(nd);
				inode = nd->path.dentry->d_inode;
				//注意这里没有break,因此将通过后面的continue直接回到循环开头
			case 1://长度为1表示当前目录,则直接忽略即可
				continue;
		}

		/*下面的部分用来处理普通的文件路径分量(不为.和..)*/
		 
		 /*如果底层文件系统的dentry定义了d_op和d_hash则调用文件系统中的d_hash进行hash值的计算*/
		if (nd->path.dentry->d_op && nd->path.dentry->d_op->d_hash) {
			err = nd->path.dentry->d_op->d_hash(nd->path.dentry,
							    &this);
			if (err < 0)
				break;
		}
		
		//do_lookup执行实际的查找,注意nd中的path对应的是父目录,而this是对应的当前解析的路径分量
		err = do_lookup(nd, &this, &next);
		if (err)
			break;

		err = -ENOENT;
		inode = next.dentry->d_inode;//获取刚刚解析的路径分量的inode
		if (!inode)
			goto out_dput;

		if (inode->i_op->follow_link) {//如果刚刚解析的路径为符号链接,则通过do_follow_link进行处理
			err = do_follow_link(&next, nd);
			if (err)
				goto return_err;
			err = -ENOENT;
			inode = nd->path.dentry->d_inode;
			if (!inode)
				break;
		} else//不为符号链接,则路径分量解析完毕,根据next更新nd中的path准备解析下一个分量
			path_to_nameidata(&next, nd);
		err = -ENOTDIR; 
		if (!inode->i_op->lookup)//如果刚刚解析的路径分量对应的inode没有定义lookup函数,
			break;				  //则无法以此为父目录进行解析了

		continue;//这里标识一次解析完毕,跳转到开头继续解析下一个分量				
		
		/* here ends the main loop */

last_with_slashes:
		lookup_flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
last_component://到了最后一个分量
		/* Clear LOOKUP_CONTINUE iff it was previously unset */
		nd->flags &= lookup_flags | ~LOOKUP_CONTINUE;
		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->path.dentry->d_inode;
				/* fallthrough */
			case 1:
				goto return_reval;
		}
		if (nd->path.dentry->d_op && nd->path.dentry->d_op->d_hash) {
			err = nd->path.dentry->d_op->d_hash(nd->path.dentry,
							    &this);
			if (err < 0)
				break;
		}
		err = do_lookup(nd, &this, &next);
		if (err)
			break;
		inode = next.dentry->d_inode;
		if (follow_on_final(inode, lookup_flags)) {
			err = do_follow_link(&next, nd);
			if (err)
				goto return_err;
			inode = nd->path.dentry->d_inode;
		} else
			path_to_nameidata(&next, nd);
		err = -ENOENT;
		if (!inode)
			break;
		if (lookup_flags & LOOKUP_DIRECTORY) {
			err = -ENOTDIR; 
			if (!inode->i_op->lookup)
				break;
		}
		goto return_base;
lookup_parent://如果目标是最后一个分量的父目录,则不用进行查找了,
				//因为nd的path总是指向当前要分析的路径的父目录的
				//下面只需要对nd的last_tpye字段进行处理,表示最后一个分量的类型
		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;
		else
			goto return_base;
return_reval:
		/*
		 * We bypassed the ordinary revalidation routines.
		 * We may need to check the cached dentry for staleness.
		 */
		if (nd->path.dentry && nd->path.dentry->d_sb &&
		    (nd->path.dentry->d_sb->s_type->fs_flags & FS_REVAL_DOT)) {
			err = -ESTALE;
			/* Note: we do not d_invalidate() */
			if (!nd->path.dentry->d_op->d_revalidate(
					nd->path.dentry, nd))
				break;
		}
return_base:
		return 0;
out_dput:
		path_put_conditional(&next, nd);
		break;
	}
	path_put(&nd->path);
return_err:
	return err;
}


其中几个比较重要的函数,follow_dotdot()--退回到上级目录, do_lookup()--普通文件名的实际查找工作, do_follow_link()--追踪符号链接,在下篇博文再拿出来进行具体分析。



分享到:
评论

相关推荐

    LINUX系统管理白皮书

    2.7.1 主机名解析 19 2.7.2 输入DNS 20 2.7.3 利用DNS进行名字查找 21 2.7.4 域名服务器 22 2.7.5 DNS数据库 22 2.7.6 逆向查找 23 第3章 网络硬件的配置 26 3.1 内核配置 27 3.1.1 内核选项1.0及以上版本 ...

    新版Android开发教程.rar

    这一联盟将会支持 Google 发布的 Android 手机操作系统或者应用软件,共同开发名为 Android 的 开 放源代码的移动系统。开放手机联盟包括手机制造商、手机芯片厂商和移动运营商几类。目前,联盟成员 数 量已经达到了...

    linux网络编程-宋敬彬-part1

    3.1.7 虚拟文件系统VFS 68 3.2 文件的通用操作方法 72 3.2.1 文件描述符 72 3.2.2 打开创建文件open()、create()函数 72 3.2.3 关闭文件close()函数 76 3.2.4 读取文件read()函数 77 3.2.5 写文件write...

    入门学习Linux常用必会60个命令实例详解doc/txt

    (1)Linux可以识别的文件系统 ◆ Windows 95/98常用的FAT 32文件系统:vfat ; ◆ Win NT/2000 的文件系统:ntfs ; ◆ OS/2用的文件系统:hpfs; ◆ Linux用的文件系统:ext2、ext3; ◆ CD-ROM光盘用的文件...

    Ubuntu权威指南(1/2)

    4.1.2 路径名 88 4.2 文件系统的组织结构 88 4.3 文件的类型 92 4.3.1 普通文件 92 4.3.2 目录文件 94 4.3.3 特殊文件 95 4.3.4 链接文件 98 4.3.5 符号链接文件 99 4.3.6 管道文件 101 4.4 文件的安全保护机制 101 ...

    Ubuntu权威指南(2/2)

    4.1.2 路径名 88 4.2 文件系统的组织结构 88 4.3 文件的类型 92 4.3.1 普通文件 92 4.3.2 目录文件 94 4.3.3 特殊文件 95 4.3.4 链接文件 98 4.3.5 符号链接文件 99 4.3.6 管道文件 101 4.4 文件的安全保护机制 101 ...

    易语言程序免安装版下载

    “库文件名”以.lib或.obj为后缀的将被视为静态库,可使用绝对路径或相对路径(相对当前源代码所在目录),如依赖多个静态库请分别列出并以逗号分隔;“在库中的对应命令名”请务必准确填写静态库中公开导出的符号...

    自己动手写操作系统(含源代码).part1

    Linus Torvalds当时是一名赫尔辛基大学计算机科学系的二年级学生,经常要用自己的电脑去访问大学主机上的新闻组和邮件,为了方便读写和下载文件,他自己编写了磁盘驱动程序和文件系统,这成为了 Linux第一个内核的...

    vc++ 开发实例源码包

    内含各种例子(vc下各种控件的使用方法、标题栏与菜单栏、工具栏与状态栏、图标与光标、程序窗口、程序控制、进程与线程、字符串、文件读写操作、文件与文件夹属性操作、文件与文件夹系统操作、系统控制操作、程序...

    自己动手写操作系统(含源代码).part2

    Linus Torvalds当时是一名赫尔辛基大学计算机科学系的二年级学生,经常要用自己的电脑去访问大学主机上的新闻组和邮件,为了方便读写和下载文件,他自己编写了磁盘驱动程序和文件系统,这成为了 Linux第一个内核的...

    JAVA上百实例源码以及开源项目

    2个目标文件,FTP的目标是:(1)提高文件的共享性(计算机程序和/或数据),(2)鼓励间接地(通过程序)使用远程计算机,(3)保护用户因主机之间的文件存储系统导致的变化,(4)为了可靠和高效地传输,虽然用户...

    VBox Raw Disk GUI:VirtualBox 命令 createrawvmdk 的 GUI-开源

    适用于 Windows 和 Linux,但应该适用于 Mac、BSD 等,前提是 VirtualBox 和 Java 工作正常,并且命令针对您正在运行的操作系统进行了更新。 -- 变更日志 -- 2017-12-05 - 2.7 版本发布! (修复/重写Windows磁盘...

    构建最高可用Oracle数据库系统 Oracle 11gR2 RAC管理、维护与性能优化

    4.6 ACFS集群文件系统 4.6.1 ACFS概述 4.6.2 ADVM动态卷管理 4.6.3 ACFS快照 4.6.4 ACFS的备份和恢复 4.6.5 ACFS同ASM整合 4.7本章小结 第5章 RAC工作原理 5.1单实例并发与一致性 5.1.1数据读一致性与写...

Global site tag (gtag.js) - Google Analytics