根據路徑名獲取目錄節點

根據路徑名獲取目錄節點

 函數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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章