在linux内核文件系统源代码里,有一个很重要的函数,就是do_follow_link函数,这个函数对应着用户空间的软链接文件的索引操作,有着很广泛的应用,我们今天来看一下他的来龙去脉。
首先我们从vfs_follow_link函数看起,vfs_follow_link函数定义在fs/namei.c,定义如下
继续看__vfs_follow_link函数,定义如下
然后在link_path_walk里主要调用了do_follow_link函数,do_follow_link函数定义在fs/namei.c,定义如下
/*软链接有限制,为了防止出现无限循环*/
我们继续看__do_follow_link函数,__do_follow_link函数定义在fs/namei.c,定义如下
以ext2文件系统为例,dentry->d_inode->i_op->follow_link函数就是ext2_follow_link,赋值在fs/ext2/symlink.c里
可以看到对应的就是ext2_follow_link函数,ext2_follow_link函数定义在fs/ext2/symlink.c里,定义如下
首先我们从vfs_follow_link函数看起,vfs_follow_link函数定义在fs/namei.c,定义如下
int vfs_follow_link(struct nameidata *nd, const char *link)
{
return __vfs_follow_link(nd, link);
}
继续看__vfs_follow_link函数,定义如下
static __always_inline int __vfs_follow_link(struct nameidata *nd, const char *link)
{
int res = 0;
char *name;
/*检查传入的参数*/
if (IS_ERR(link))
goto fail;
/*如果是从根目录开始的路径,释放path,然后调用walk_init_root*/
if (*link == '/') {
path_release(nd);
if (!walk_init_root(link, nd))
/* weird __emul_prefix() stuff did it */
goto out;
}
/*之前讲过的,获得文件对应的inode*/
res = link_path_walk(link, nd);
out:
/*有错误就返回错误*/
if (nd->depth || res || nd->last_type!=LAST_NORM)
return res;
/*申请内存返回文件名*/
name = __getname();
if (unlikely(!name)) {
path_release(nd);
return -ENOMEM;
}
strcpy(name, nd->last.name);
nd->last.name = name;
return 0;
fail:
path_release(nd);
return PTR_ERR(link);
}
然后在link_path_walk里主要调用了do_follow_link函数,do_follow_link函数定义在fs/namei.c,定义如下
/*软链接有限制,为了防止出现无限循环*/
static inline int do_follow_link(struct path *path, struct nameidata *nd)
{
int err = -ELOOP;
/*如果已经超出了最大循环限度,就退出*/
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;
/*进入之前,先把循环的深度++*/
current->link_count++;
current->total_link_count++;
nd->depth++;
/*主要工作函数*/
err = __do_follow_link(path, nd);
/*退出后减一*/
current->link_count--;
nd->depth--;
return err;
loop:
dput_path(path, nd);
path_release(nd);
return err;
}
我们继续看__do_follow_link函数,__do_follow_link函数定义在fs/namei.c,定义如下
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);
if (path->mnt != nd->mnt) {
path_to_nameidata(path, nd);
dget(dentry);
}
mntget(path->mnt);
/*调用底层文件系统的follow_link函数*/
cookie = dentry->d_inode->i_op->follow_link(dentry, nd);
error = PTR_ERR(cookie);
/*如果没有出错,就调用高层的follow_link*/
if (!IS_ERR(cookie)) {
/*首先从nd里获得软链接所指向的文件*/
char *s = nd_get_link(nd);
error = 0;
/*因为软链接可能指向其他的文件系统的文件,所以需要给高层调用,继续寻找*/
if (s)
error = __vfs_follow_link(nd, s);
/*使用后释放*/
if (dentry->d_inode->i_op->put_link)
dentry->d_inode->i_op->put_link(dentry, nd, cookie);
}
dput(dentry);
mntput(path->mnt);
return error;
}
以ext2文件系统为例,dentry->d_inode->i_op->follow_link函数就是ext2_follow_link,赋值在fs/ext2/symlink.c里
const struct inode_operations ext2_fast_symlink_inode_operations = {
.readlink = generic_readlink,
.follow_link = ext2_follow_link,
#ifdef CONFIG_EXT2_FS_XATTR
.setxattr = generic_setxattr,
.getxattr = generic_getxattr,
.listxattr = ext2_listxattr,
.removexattr = generic_removexattr,
#endif
};
可以看到对应的就是ext2_follow_link函数,ext2_follow_link函数定义在fs/ext2/symlink.c里,定义如下
static void *ext2_follow_link(struct dentry *dentry, struct nameidata *nd)
{
/*首先得到ext2_inode_info结构体,获得i_data字段,这个字段在ext2文件系统就是软链接的指向文件*/
struct ext2_inode_info *ei = EXT2_I(dentry->d_inode);
/*然后就在nd结构体内部记录下来*/
nd_set_link(nd, (char *)ei->i_data);
return NULL;
}