根据路径名获取目录节点
函数kern_path通过路径名name,在标志flags的指示下找到对应的目录节点,存储在path中。Flags相关标志在文件namei.h中定义,例如LOOKUP_DIRECTORY(表示要找到的目标必须是目录),LOOKUP_FOLLOW(表示如果找到的目标是“符号链接”到其他文件或目录的一个目录项,则要顺着连接一直找到终点)。struct path在文件path.h中定义,如下:
struct path {
struct vfsmount *mnt;//目标节点所在文件系统。
struct dentry *dentry;//存储目标节点的目录项。
};
函数在文件namespace.c中实现,如下:
int kern_path(const char *name, unsigned int flags, struct path *path)
{
struct nameidata nd;
int res = do_path_lookup(AT_FDCWD, name, flags, &nd);
if (!res)
*path = nd.path;
return res;
}
struct nameidata是一个临时数据结构用来返回搜索结果。该结构在文件namei.h中定义,如下:
struct nameidata {
struct path path;//搜索到的当前目录项
struct qstr last;//搜索的目标目录项
struct path root;//当前进程的根目录
struct inode *inode; /* path.dentry.d_inode */
unsigned int flags;
unsigned seq;
int last_type;
unsigned depth;
char *saved_names[MAX_NESTED_LINKS + 1];
union {
struct open_intent open;
} intent;
};
last_type:的可能值定义在文件namei.h中如下:
enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT, LAST_BIND};
在搜索过程中,这个字段会随路径的当前搜索结果而变。例如:如果找到了目标文件,这个值就变成了LAST_NORM;如果最后停留在一个“.”上,则变成LAST_DOT。
AT_FDCWD:表示从当前目录找起。
【kern_path--->do_path_lookup】
函数do_path_lookup主要就是调用函数path_lookupat和对函数返回值的一些处理。
static int do_path_lookup(int dfd, const char *name,
unsigned int flags, struct nameidata *nd)
{
int retval = path_lookupat(dfd, name, flags | LOOKUP_RCU, nd);
......
return retval;
}
【kern_path--->do_path_lookup--->path_lookupat】
通过函数path_init将临时结构struct nameidata初始化为路径的起始值。然后通过函数link_path_walk搜索中间路径,最后通过函数lookup_last找到目标节点,如果目标是链接文件则通过函数follow_link找到连接的实际文件。
static int path_lookupat(int dfd, const char *name,
unsigned int flags, struct nameidata *nd)
{
struct file *base = NULL;
struct path path;
int err;
err = path_init(dfd, name, flags | LOOKUP_PARENT, nd, &base);
if (unlikely(err))
return err;
current->total_link_count = 0;
err = link_path_walk(name, nd);
if (!err && !(flags & LOOKUP_PARENT)) {
err = lookup_last(nd, &path);
while (err > 0) {
void *cookie;
struct path link = path;
nd->flags |= LOOKUP_PARENT;
err = follow_link(&link, nd, &cookie);
if (!err)
err = lookup_last(nd, &path);
put_link(nd, &link, cookie);
}
}
......
return err;
}
【kern_path--->do_path_lookup--->path_lookupat--->path_init】
初始化路径的起始位置,该函数很长,下面分段分析。
static int path_init(int dfd, const char *name, unsigned int flags,
struct nameidata *nd, struct file **fp)
{
int retval = 0;
int fput_needed;
struct file *file;
nd->last_type = LAST_ROOT; /* if there are only slashes... */
nd->flags = flags | LOOKUP_JUMPED;
nd->depth = 0;
if (flags & LOOKUP_ROOT) {
struct inode *inode = nd->root.dentry->d_inode;
if (*name) {
if (!inode->i_op->lookup)
return -ENOTDIR;
retval = inode_permission(inode, MAY_EXEC);
if (retval)
return retval;
}
nd->path = nd->root;
nd->inode = inode;
......
return 0;
}
如果flags的LOOKUP_ROOT位置位,则当前搜索的路径节点为根目录。要穿过目录节点向前搜索,当前目录必须具有可执行权限。然后让nd->path指向 nd->root。
nd->root.mnt = NULL;
if (*name=='/') {
if (flags & LOOKUP_RCU) {
br_read_lock(vfsmount_lock);
rcu_read_lock();
set_root_rcu(nd);
} else {
set_root(nd);
path_get(&nd->root);
}
nd->path = nd->root;
} else if (dfd == AT_FDCWD) {
if (flags & LOOKUP_RCU) {
struct fs_struct *fs = current->fs;
unsigned seq;
br_read_lock(vfsmount_lock);
rcu_read_lock();
do {
seq = read_seqcount_begin(&fs->seq);
nd->path = fs->pwd;
nd->seq = __read_seqcount_begin(&nd->path.dentry->d_seq);
} while (read_seqcount_retry(&fs->seq, seq));
} else {
get_fs_pwd(current->fs, &nd->path);
}
如果文件名以'/'开头,表示路径的搜索起点为根目录。将临时结构struct nameidata的根目录nd->root 设为当前进程的根目录fs->root,将搜索路径的当前节点nd->path设为nd->root。如果dfd 的值为 AT_FDCWD表示从当前路径开始搜索,将nd->path设为进程的当前目录fs->pwd。
} else {
struct dentry *dentry;
file = fget_raw_light(dfd, &fput_needed);
retval = -EBADF;
if (!file)
goto out_fail;
dentry = file->f_path.dentry;
if (*name) {
retval = -ENOTDIR;
if (!S_ISDIR(dentry->d_inode->i_mode))
goto fput_fail;
retval = inode_permission(dentry->d_inode, MAY_EXEC);
if (retval)
goto fput_fail;
}
nd->path = file->f_path;
if (flags & LOOKUP_RCU) {
if (fput_needed)
*fp = file;
nd->seq = __read_seqcount_begin(&nd->path.dentry->d_seq);
br_read_lock(vfsmount_lock);
rcu_read_lock();
} else {
path_get(&file->f_path);
fput_light(file, fput_needed);
}
}
nd->inode = nd->path.dentry->d_inode;
return 0;
fput_fail:
fput_light(file, fput_needed);
out_fail:
return retval;
}
函数fget_raw_light根据文件描述符dfd在当前进程打开文件表里取出相应的file结构变量;如果name不为空,表示文件描述符dfd对应的文件不是最终目标,而是中间目录项,所以必须要有可执行权限。然后将当前路径节点nd->path 设为 file->f_path。最后让nd->inode 指向 nd->path.dentry->d_inode。
转载于:https://blog.51cto.com/csdyabc/856159