根据路径名获取目录节点

 函数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;

}

如果flagsLOOKUP_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