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

Linux虚拟文件系统--文件路径名的解析(4)--符号链接

    符号链接也是一种文件,只不过其内容是另一个文件的路径名。对于符号链接的处理,要注意避免死循环的产生,如一个符号链接指向其本身。符号链接可能包含了符号链接,因此内核采用递归的方式来处理这个问题,为了避免符号链接的死循环而导致无穷递归,内核采用link_count和total_link_count来跟踪符号链接的处理,其中前者表示连续的符号链接数,后者表示总共的符号链接数,两者都不能超过各自的限制,否则内核会放弃解析。

static inline int do_follow_link(struct path *path, struct nameidata *nd)
{
	int err = -ELOOP;
	/*如果处理的链接数超过下面的限制则会放弃搜索(因为有可能陷入死循环)*/
	/*link_count表示连续的符号链接数,其值不能超过8,total_link_count表示总共的
	 链接数,气值不能超过40*/
	if (current->link_count >= MAX_NESTED_LINKS)
		goto loop;
	if (current->total_link_count >= 40)
		goto loop;
	BUG_ON(nd->depth >= MAX_NESTED_LINKS);
	cond_resched();
	err = security_inode_follow_link(path->dentry, nd);
	if (err)
		goto loop;
	/*每处理一次符号链接,link_count,total_link_count和depth都要加1*/
	current->link_count++;
	current->total_link_count++;//
	nd->depth++;
	/*主要是调用特定于文件系统的follow_link函数进行符号链接的跟踪*/
	err = __do_follow_link(path, nd);
	/*每执行完一次符号链接,link_count和depth都要减1*/
	current->link_count--;
	nd->depth--;
	return err;
loop:
	path_put_conditional(path, nd);
	path_put(&nd->path);
	return err;
}


 

static __always_inline int __do_follow_link(struct path *path, struct nameidata *nd)
{
	int error;
	void *cookie;
	struct dentry *dentry = path->dentry;

	touch_atime(path->mnt, dentry);
	nd_set_link(nd, NULL);//在saved_names中清除当前解析的符号链接路径

	if (path->mnt != nd->path.mnt) {
		path_to_nameidata(path, nd);
		dget(dentry);
	}
	mntget(path->mnt);
	/*调用特定于文件系统的follow_link方法来跟踪符号链接,并将符号链接中保存的路径名提取出来
	  存放在nd的saved_names中*/
	cookie = dentry->d_inode->i_op->follow_link(dentry, nd);
	error = PTR_ERR(cookie);
	if (!IS_ERR(cookie)) {
		char *s = nd_get_link(nd);//提取saved_names中的路径
		error = 0;
		if (s)//路径存在,则继续解析,__vfs_follow_link将会调用link_path_walk,因此对于
			  //路径中存在多个符号链接的情况,这是一个递归的过程
			error = __vfs_follow_link(nd, s);
		if (dentry->d_inode->i_op->put_link)
			dentry->d_inode->i_op->put_link(dentry, nd, cookie);
	}
	path_put(path);

	return error;
}


 

static __always_inline int __vfs_follow_link(struct nameidata *nd, const char *link)
{
	int res = 0;
	char *name;
	if (IS_ERR(link))
		goto fail;

	if (*link == '/') {
		set_root(nd);
		path_put(&nd->path);
		nd->path = nd->root;
		path_get(&nd->root);
	}

	res = link_path_walk(link, nd);
	if (nd->depth || res || nd->last_type!=LAST_NORM)
		return res;
	/*
	 * If it is an iterative symlinks resolution in open_namei() we
	 * have to copy the last component. And all that crap because of
	 * bloody create() on broken symlinks. Furrfu...
	 */
	name = __getname();
	if (unlikely(!name)) {
		path_put(&nd->path);
		return -ENOMEM;
	}
	strcpy(name, nd->last.name);
	nd->last.name = name;
	return 0;
fail:
	path_put(&nd->path);
	return PTR_ERR(link);
}


这里可以看到解析的过程又回到了link_path_walk()(见前文),因此实现了递归的过程。

 

1楼zi77642475昨天 18:44
更新速度不错啊!沙发依然在!