根據路徑名獲取目錄節點
函數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。