http://blog.csdn.net/air_snake/archive/2008/07/22/2690554.aspx
從檔案名找到檔案資訊(namei)
本文檔的Copyleft歸yfydz所有,使用GPL釋出,可以自由拷貝,轉載,轉載時請保持文檔的完整性,嚴禁用于任何商業用途。
msn: [email protected]
來源:http://yfydz.cublog.cn
1. 前言
inode是類Unix系統的檔案系統的基本索引方法,每個檔案都對應一個inode,再通過inode找到檔案中的實際資料,是以根據檔案路徑名找到具體的inode節點就是一個很重要的處理步驟。系統會緩存用過的每個檔案或目錄對應的dentry結構, 從該結構可以指向相應的inode, 每次打開檔案, 都會最終對應到檔案的inode,中間查找過程稱為namei。
本文介紹Linux下的路徑到檔案指針的轉換過程,核心版本為2.6.19.2。
虛拟檔案系統的轉換源代碼在fs/namei.c中,具體和檔案系統相關的部分在fs
asmlinkage long sys_open(const char __user *filename, int flags, int mode)
{
long ret;
if (force_o_largefile())
flags |= O_LARGEFILE;
ret = do_sys_open(AT_FDCWD, filename, flags, mode);
prevent_tail_call(ret);
return ret;
}
真正的打開函數是do_sys_open:
// dfd為AT_FDCWD
long do_sys_open(int dfd, const char __user *filename, int flags, int mode)
{
// 通過該函數将使用者空間的檔案名傳遞到核心
// tmp是一個cache類的動态記憶體空間,用于儲存檔案路徑名
//
char *tmp = getname(filename);
int fd = PTR_ERR(tmp);
if (!IS_ERR(tmp)) {
// 擷取一個未使用的檔案描述符, 和inode無關
fd = get_unused_fd();
if (fd >= 0) {
// 打開檔案,将檔案名轉換為檔案結構
struct file *f = do_filp_open(dfd, tmp, flags, mode);
if (IS_ERR(f)) {
put_unused_fd(fd);
fd = PTR_ERR(f);
} else {
fsnotify_open(f->f_dentry);
fd_install(fd, f);
}
}
putname(tmp);
}
return fd;
}
// 檔案打開
static struct file *do_filp_open(int dfd, const char *filename, int flags,
int mode)
{
int namei_flags, error;
// 注意這是結構而不是指針
struct nameidata nd;
namei_flags = flags;
if ((namei_flags+1) & O_ACCMODE)
namei_flags++;
// 根據檔案名得到nameidata, nd作為namei空間儲存結果
error = open_namei(dfd, filename, namei_flags, mode, &nd);
if (!error)
// 成功, nameidata再轉換為file指針
return nameidata_to_filp(&nd, flags);
return ERR_PTR(error);
}
是以重點函數是open_namei函數, 實作了從檔案名到inode的轉換, 也是namei的處理入口.
在分析open_namei前, 再分析一下getname, 這用到了kmem_cache來處理的:
// 檔案名轉換, 從使用者空間拷貝到核心空間
char * getname(const char __user * filename)
{
char *tmp, *result;
result = ERR_PTR(-ENOMEM);
// __getname和__putname的定義,實際就是核心cache的配置設定和釋放
// #define __getname() kmem_cache_alloc(names_cachep, SLAB_KERNEL)
// #define __putname(name) kmem_cache_free(names_cachep, (void *)(name))
// 這裡實際是配置設定names的cache, 該cache定義為
// names_cachep = kmem_cache_create("names_cache", PATH_MAX, 0,
// SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL, NULL);
tmp = __getname();
if (tmp) {
// cache配置設定成功
// 進入實際操作函數
int retval = do_getname(filename, tmp);
// 要傳回結果指向cache
result = tmp;
if (retval < 0) {
// 操作失敗,釋放cache,傳回錯誤
__putname(tmp);
result = ERR_PTR(retval);
}
}
// 編譯核心時如果沒有設定CONFIG_AUDITSYSCALL, 則audit_getname為空
// 審計系統調用結果
audit_getname(result);
return result;
}
static int do_getname(const char __user *filename, char *page)
{
int retval;
unsigned long len = PATH_MAX;
if (!segment_eq(get_fs(), KERNEL_DS)) {
if ((unsigned long) filename >= TASK_SIZE)
return -EFAULT;
if (TASK_SIZE - (unsigned long) filename < PATH_MAX)
len = TASK_SIZE - (unsigned long) filename;
}
// 将使用者空間提供的檔案名拷貝到cache中
retval = strncpy_from_user(page, filename, len);
if (retval > 0) {
if (retval < len)
return 0;
return -ENAMETOOLONG;
} else if (!retval)
retval = -ENOENT;
return retval;
}
3. namei相關資料結構
struct nameidata {
// 路徑點
struct dentry *dentry;
// 虛拟系統挂接點
struct vfsmount *mnt;
// 路徑名中的最後的檔案名或目錄名
struct qstr last;
unsigned int flags;
int last_type;
// 目錄深度
unsigned depth;
char *saved_names[MAX_NESTED_LINKS + 1]; // 9
// 相關資料
union {
// 包含打開的檔案的指針
struct open_intent open;
} intent;
};
struct open_intent {
// 标志
int flags;
// 建立模式
int create_mode;
// 檔案指針
struct file *file;
};
// 路徑結構, 屬于中間處理結構, 将檔案系統挂接點和dentry捆綁在一起而已
struct path {
struct vfsmount *mnt;
struct dentry *dentry;
};
// 檔案目錄項, 在系統cache中
struct dentry {
atomic_t d_count;
unsigned int d_flags;
spinlock_t d_lock;
struct inode *d_inode;
struct hlist_node d_hash;
struct dentry *d_parent;
struct qstr d_name;
struct list_head d_lru;
union {
struct list_head d_child;
struct rcu_head d_rcu;
} d_u;
struct list_head d_subdirs;
struct list_head d_alias;
unsigned long d_time;
struct dentry_operations *d_op;
struct super_block *d_sb;
void *d_fsdata;
#ifdef CONFIG_PROFILING
struct dcookie_struct *d_cookie;
#endif
int d_mounted;
unsigned char d_iname[DNAME_INLINE_LEN_MIN];
};
// 檔案結構
struct file {
union {
struct list_head fu_list;
struct rcu_head fu_rcuhead;
} f_u;
// 檔案的dentry
struct dentry *f_dentry;
// 虛拟檔案系統挂接點
struct vfsmount *f_vfsmnt;
// 檔案操作
const struct file_operations *f_op;
atomic_t f_count;
unsigned int f_flags;
mode_t f_mode;
loff_t f_pos;
struct fown_struct f_owner;
unsigned int f_uid, f_gid;
struct file_ra_state f_ra;
unsigned long f_version;
#ifdef CONFIG_SECURITY
void *f_security;
#endif
void *private_data;
#ifdef CONFIG_EPOLL
struct list_head f_ep_links;
spinlock_t f_ep_lock;
#endif
struct address_space *f_mapping;
};
4. namei操作
4.1 open_namei
int open_namei(int dfd, const char *pathname, int flag,
int mode, struct nameidata *nd)
{
int acc_mode, error;
struct path path;
struct dentry *dir;
int count = 0;
// #define ACC_MODE(x) ("\000\004\002\006"[(x)&O_ACCMODE])
// 審計模式
acc_mode = ACC_MODE(flag);
// 截斷标志, 基本上需要寫權限, 除非要截斷的長度實際大于檔案本身長度
if (flag & O_TRUNC)
acc_mode |= MAY_WRITE;
// 添加标志, 也是需要寫權限
if (flag & O_APPEND)
acc_mode |= MAY_APPEND;
// 不需要建立檔案
if (!(flag & O_CREAT)) {
// 直接找pathname的dentry和挂接點, 結果填在nd中
error = path_lookup_open(dfd, pathname, lookup_flags(flag),
nd, flag);
if (error)
return error;
goto ok;
}
// 建立檔案的dentry和挂接點, 資料填到nd中
error = path_lookup_create(dfd,pathname,LOOKUP_PARENT,nd,flag,mode);
if (error)
return error;
error = -EISDIR;
// 檢查nameidata結構中的last參數是否合法
if (nd->last_type != LAST_NORM || nd->last.name[nd->last.len])
goto exit;
// 檔案項dentry
dir = nd->dentry;
// 去掉查詢父目錄标志
nd->flags &= ~LOOKUP_PARENT;
mutex_lock(&dir->d_inode->i_mutex);
// 填充path參數, 又根據nd的資訊搜尋一次目前的緩存的dentry
// 不過dir與path.dentry難道不相同麼?
path.dentry = lookup_hash(nd);
path.mnt = nd->mnt;
do_last:
// 檢查path.entry是否合法
error = PTR_ERR(path.dentry);
if (IS_ERR(path.dentry)) {
mutex_unlock(&dir->d_inode->i_mutex);
goto exit;
}
// 檢查nd->intent.open.file是否合法, 這是最終要傳回的檔案指針
if (IS_ERR(nd->intent.open.file)) {
mutex_unlock(&dir->d_inode->i_mutex);
error = PTR_ERR(nd->intent.open.file);
goto exit_dput;
}
if (!path.dentry->d_inode) {
// 建立新檔案的inode, 然後傳回
error = open_namei_create(nd, &path, flag, mode);
if (error)
goto exit;
return 0;
}
// 現在是打開已經存在的檔案
mutex_unlock(&dir->d_inode->i_mutex);
audit_inode_update(path.dentry->d_inode);
error = -EEXIST;
// O_EXCL标志是隻必須打開的是不存在的檔案, 檔案已存在時錯誤
if (flag & O_EXCL)
goto exit_dput;
if (__follow_mount(&path)) {
error = -ELOOP;
if (flag & O_NOFOLLOW)
goto exit_dput;
}
error = -ENOENT;
if (!path.dentry->d_inode)
goto exit_dput;
// 如果dentry的具體FS的實作中定義了follow_link操作, 轉
// 不過大多數FS的實作中都沒有定義該函數
if (path.dentry->d_inode->i_op && path.dentry->d_inode->i_op->follow_link)
goto do_link;
// 從路徑中的dentry和mnt資訊指派到nameidata
path_to_nameidata(&path, nd);
error = -EISDIR;
// 如果是一個目錄, 傳回錯誤
if (path.dentry->d_inode && S_ISDIR(path.dentry->d_inode->i_mode))
goto exit;
ok:
// 對nd中的dentry及其inode進行打開前的錯誤檢查
error = may_open(nd, acc_mode, flag);
if (error)
goto exit;
return 0;
// 下面是錯誤處理, 釋放掉已配置設定的資源, 傳回錯誤
exit_dput:
dput_path(&path, nd);
exit:
if (!IS_ERR(nd->intent.open.file))
release_open_intent(nd);
path_release(nd);
return error;
// 處理符号連接配接, 找到實際檔案的inode,然後重新循環, 要注意回環情況的錯誤處理
do_link:
error = -ELOOP;
if (flag & O_NOFOLLOW)
goto exit_dput;
// 設定查找LOOKUP_PARENT标志
nd->flags |= LOOKUP_PARENT;
error = security_inode_follow_link(path.dentry, nd);
if (error)
goto exit_dput;
// 處理符号連結
error = __do_follow_link(&path, nd);
if (error) {
release_open_intent(nd);
return error;
}
nd->flags &= ~LOOKUP_PARENT;
// 檢查最後一段檔案或目錄名的屬性情況
if (nd->last_type == LAST_BIND)
goto ok;
error = -EISDIR;
if (nd->last_type != LAST_NORM)
goto exit;
if (nd->last.name[nd->last.len]) {
__putname(nd->last.name);
goto exit;
}
error = -ELOOP;
// 出現回環标志: 循環超過32次
if (count++==32) {
__putname(nd->last.name);
goto exit;
}
dir = nd->dentry;
mutex_lock(&dir->d_inode->i_mutex);
// 更新路徑的挂接點和dentry
path.dentry = lookup_hash(nd);
path.mnt = nd->mnt;
__putname(nd->last.name);
goto do_last;
}
4.2 path_lookup_open和path_lookup_create
這兩個函數找到路徑名對應的挂接點和dentry結構, 指派到nameidata結構中, create時如果檔案不存在, 建立新檔案:
int path_lookup_open(int dfd, const char *name, unsigned int lookup_flags,
struct nameidata *nd, int open_flags)
{
return __path_lookup_intent_open(dfd, name, lookup_flags, nd,
open_flags, 0);
}
static int path_lookup_create(int dfd, const char *name,
unsigned int lookup_flags, struct nameidata *nd,
int open_flags, int create_mode)
{
return __path_lookup_intent_open(dfd, name, lookup_flags|LOOKUP_CREATE,
nd, open_flags, create_mode);
}
這兩個函數都是調用__path_lookup_intent_open, 隻是參數不同,create中加入了LOOKUP_CREATE标志和create_mode:
static int __path_lookup_intent_open(int dfd, const char *name,
unsigned int lookup_flags, struct nameidata *nd,
int open_flags, int create_mode)
{
// 找一個空閑的檔案指針
struct file *filp = get_empty_filp();
int err;
// 找不到傳回錯誤, 檔案表溢出了
if (filp == NULL)
return -ENFILE;
// 在nameidate中填充打開的檔案參數, 這是最終會傳回的檔案指針
nd->intent.open.file = filp;
nd->intent.open.flags = open_flags;
nd->intent.open.create_mode = create_mode;
// 進行具體的路徑查找, name是路徑名
err = do_path_lookup(dfd, name, lookup_flags|LOOKUP_OPEN, nd);
// 先檢查nd->intent.open.file而不是err
if (IS_ERR(nd->intent.open.file)) {
// 打開的檔案指針錯誤
if (err == 0) {
// do_path_lookup已經成功了, 釋放path, err重新設定為錯誤值
err = PTR_ERR(nd->intent.open.file);
path_release(nd);
}
} else if (err != 0)
release_open_intent(nd);
return err;
}
// 路徑查找
static int fastcall do_path_lookup(int dfd, const char *name,
unsigned int flags, struct nameidata *nd)
{
int retval = 0;
int fput_needed;
struct file *file;
// 檔案系統指針從程序中擷取
struct fs_struct *fs = current->fs;
// 預設情況last_type為絕對路徑, 以"/"開頭的格式
nd->last_type = LAST_ROOT;
nd->flags = flags;
nd->depth = 0;
// 下面隻是用于增加某些變量的使用計數值, get是增加,put是減少
if (*name=='/') {
// 絕對路徑格式
read_lock(&fs->lock);
if (fs->altroot && !(nd->flags & LOOKUP_NOALT)) {
// 檢查是否更改了root, 即用chroot
// 增加altrootmnt的使用計數, 其為一vfsmount結構指針
nd->mnt = mntget(fs->altrootmnt);
nd->dentry = dget(fs->altroot);
read_unlock(&fs->lock);
if (__emul_lookup_dentry(name,nd))
goto out;
read_lock(&fs->lock);
}
// 增加rootmnt的使用計數然後指派到nd中
nd->mnt = mntget(fs->rootmnt);
// 增加根的dentry的使用計數然後指派到nd中
nd->dentry = dget(fs->root);
read_unlock(&fs->lock);
} else if (dfd == AT_FDCWD) {
// 從sys_open調用來的話會到這裡, 表示從目前工作目錄的路徑開始的相對路徑
read_lock(&fs->lock);
// 增加pwdmnt使用計數然後指派到nd中
nd->mnt = mntget(fs->pwdmnt);
// 增加pwd使用計數然後指派到nd中
nd->dentry = dget(fs->pwd);
read_unlock(&fs->lock);
} else {
struct dentry *dentry;
// 輕量級的路徑查找, fd不是共享的話不會增加引用計數
file = fget_light(dfd, &fput_needed);
retval = -EBADF;
if (!file)
goto out_fail;
dentry = file->f_dentry;
retval = -ENOTDIR;
if (!S_ISDIR(dentry->d_inode->i_mode))
goto fput_fail;
// 檢查檔案的執行權限
retval = file_permission(file, MAY_EXEC);
if (retval)
goto fput_fail;
// 增加f_vfsmnt的使用計數
nd->mnt = mntget(file->f_vfsmnt);
nd->dentry = dget(dentry);
// 輕量級釋放
fput_light(file, fput_needed);
}
// 清空總連結數
current->total_link_count = 0;
// 變量路徑表查詢, 核心函數
retval = link_path_walk(name, nd);
out:
if (likely(retval == 0)) {
// 在大部分情況下都會執行到這,能正确打開路徑
if (unlikely(!audit_dummy_context() && nd && nd->dentry &&
nd->dentry->d_inode))
audit_inode(name, nd->dentry->d_inode);
}
out_fail:
return retval;
fput_fail:
fput_light(file, fput_needed);
goto out_fail;
}
do_path_lookup調用的核心函數是link_path_walk:
int fastcall link_path_walk(const char *name, struct nameidata *nd)
{
// 先備份一下
struct nameidata save = *nd;
int result;
dget(save.dentry);
mntget(save.mnt);
result = __link_path_walk(name, nd);
if (result == -ESTALE) {
// ESTALE是失效的檔案句柄錯誤
// 用備份的nameidate重新恢複, 設定LOOKUP_REVAL标志後重新查詢
*nd = save;
dget(nd->dentry);
mntget(nd->mnt);
nd->flags |= LOOKUP_REVAL;
result = __link_path_walk(name, nd);
}
dput(save.dentry);
mntput(save.mnt);
return result;
}
真正的名稱解析函數__link_path_walk:
static fastcall int __link_path_walk(const char * name, struct nameidata *nd)
{
struct path next;
struct inode *inode;
int err;
unsigned int lookup_flags = nd->flags;
// 去掉起始多餘的"/", 同時也說明系統可以允許你輸入多個"/"而不報錯
while (*name=='/')
name++;
// 空路徑
if (!*name)
goto return_reval;
// 路徑對應的inode
inode = nd->dentry->d_inode;
if (nd->depth)
lookup_flags = LOOKUP_FOLLOW | (nd->flags & LOOKUP_CONTINUE);
for(;;) {
// 循環處理,每個循環提取檔案路徑的一個目錄名, '/'分隔
unsigned long hash;
struct qstr this;
unsigned int c;
nd->flags |= LOOKUP_CONTINUE;
// 檢查檔案權限, 包括讀寫執行權限, 使用者/組/其他權限, 傳回0為合法
err = exec_permission_lite(inode, nd);
if (err == -EAGAIN)
// EAGAIN表示該inode正在被操作, 檢查其執行權限
// 而對于普通檔案檢查結果将是錯誤
err = vfs_permission(nd, MAY_EXEC);
// 出錯中斷循環
if (err)
break;
// 填充quickstring結構
this.name = name;
// name的第一個字元的數值
c = *(const unsigned char *)name;
// 計算檔案名的hash, 不包括'/'
hash = init_name_hash();
do {
name++;
hash = partial_name_hash(c, hash);
c = *(const unsigned char *)name;
} while (c && (c != '/'));
// 目錄(如果有的話)的名稱長度
this.len = name - (const char *) this.name;
// hash
this.hash = end_name_hash(hash);
// c為0表示是最後的具體檔案名了
if (!c)
goto last_component;
// 跳過中間的'/'
while (*++name == '/');
// 到名稱尾, 說明檔案名最後一個字元是'/'
if (!*name)
goto last_with_slashes;
// 如果第一個字元是'.'
if (this.name[0] == '.') switch (this.len) {
default:
// 是一個一'.'開頭的檔案或目錄名稱
break;
case 2:
// 第2 個字元不是".", 是普通檔案或路徑名
if (this.name[1] != '.')
break;
// 以".."開頭, 是父目錄, 更新nd為父目錄nameidata資料, inode相應更新重新循環
follow_dotdot(nd);
inode = nd->dentry->d_inode;
case 1:
// 以'.'開頭的目前目錄, 忽略, 重新循環
continue;
}
// 底層FS實作中有自己的HASH算法
if (nd->dentry->d_op && nd->dentry->d_op->d_hash) {
err = nd->dentry->d_op->d_hash(nd->dentry, &this);
if (err < 0)
break;
}
// 根據檔案/目錄名進行具體的查找
err = do_lookup(nd, &this, &next);
if (err)
break;
err = -ENOENT;
// inode更新為本級檔案目錄的inode
inode = next.dentry->d_inode;
// 找不到inode, 轉錯誤處理
if (!inode)
goto out_dput;
err = -ENOTDIR;
if (!inode->i_op)
goto out_dput;
if (inode->i_op->follow_link) {
// 處理符号連結, 在其中考慮了遞歸互相連結的異常處理
err = do_follow_link(&next, nd);
if (err)
goto return_err;
err = -ENOENT;
// 更新inode為實際的inode
inode = nd->dentry->d_inode;
if (!inode)
break;
err = -ENOTDIR;
if (!inode->i_op)
break;
} else
// nd中得到下一級路徑資訊
path_to_nameidata(&next, nd);
err = -ENOTDIR;
if (!inode->i_op->lookup)
break;
// 繼續循環找下一目錄檔案名稱
continue;
// 最後的檔案名了, 處理和前面類似
last_with_slashes:
// 最後一個字元是'/', 是一個目錄
lookup_flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
last_component:
nd->flags &= lookup_flags | ~LOOKUP_CONTINUE;
if (lookup_flags & LOOKUP_PARENT)
goto lookup_parent;
if (this.name[0] == '.') switch (this.len) {
default:
break;
case 2:
// 檔案名不是"..", 繼續
if (this.name[1] != '.')
break;
// 檔案名是"..", 到父目錄
follow_dotdot(nd);
inode = nd->dentry->d_inode;
case 1:
// 檔案名就是".", 跳到傳回處理
goto return_reval;
}
// 一般檔案處理
// 底層FS實作中有自己的HASH算法
if (nd->dentry->d_op && nd->dentry->d_op->d_hash) {
err = nd->dentry->d_op->d_hash(nd->dentry, &this);
if (err < 0)
break;
}
// 查找最後的檔案名
err = do_lookup(nd, &this, &next);
if (err)
break;
inode = next.dentry->d_inode;
if ((lookup_flags & LOOKUP_FOLLOW)
&& inode && inode->i_op && inode->i_op->follow_link) {
err = do_follow_link(&next, nd);
if (err)
goto return_err;
inode = nd->dentry->d_inode;
} else
// 更新nameidata中的mnt, dentry值
path_to_nameidata(&next, nd);
err = -ENOENT;
if (!inode)
break;
if (lookup_flags & LOOKUP_DIRECTORY) {
err = -ENOTDIR;
if (!inode->i_op || !inode->i_op->lookup)
break;
}
goto return_base;
lookup_parent:
// 複制目前quickstring結構this資訊到nd的last中
// 類型為LAST_NORM
nd->last = this;
nd->last_type = LAST_NORM;
if (this.name[0] != '.')
goto return_base;
if (this.len == 1)
nd->last_type = LAST_DOT;
else if (this.len == 2 && this.name[1] == '.')
nd->last_type = LAST_DOTDOT;
else
goto return_base;
return_reval:
// 傳回
if (nd->dentry && nd->dentry->d_sb &&
(nd->dentry->d_sb->s_type->fs_flags & FS_REVAL_DOT)) {
err = -ESTALE;
if (!nd->dentry->d_op->d_revalidate(nd->dentry, nd))
break;
}
return_base:
return 0;
out_dput:
dput_path(&next, nd);
break;
}
// 到這裡屬于出錯了
path_release(nd);
return_err:
return err;
}
static int do_lookup(struct nameidata *nd, struct qstr *name,
struct path *path)
{
struct vfsmount *mnt = nd->mnt;
// 從系統緩存的dentry的hash表中查找父dentry是nd->dentry, 名稱為name的dentry
struct dentry *dentry = __d_lookup(nd->dentry, name);
// 沒找到dentry, 進行真正從存儲硬碟中查找
if (!dentry)
goto need_lookup;
// 需要進行revalidate操作時先進行validate操作
if (dentry->d_op && dentry->d_op->d_revalidate)
goto need_revalidate;
done:
// 找到, 填充path參數: 挂接點mnt和目錄項dentry
path->mnt = mnt;
path->dentry = dentry;
__follow_mount(path);
return 0;
need_lookup:
// 進行真正的查找, 不過read_lookup會重新調用__d_lookup, 找不到才調用底層的fs實作去查找
// 好象是重複操作了
// real_lookup中的操作才反映了各個fs底層和相關标志的差別處理
dentry = real_lookup(nd->dentry, name, nd);
if (IS_ERR(dentry))
goto fail;
goto done;
need_revalidate:
// 進行validate操作
dentry = do_revalidate(dentry, nd);
if (!dentry)
goto need_lookup;
if (IS_ERR(dentry))
goto fail;
goto done;
fail:
return PTR_ERR(dentry);
}
static struct dentry * real_lookup(struct dentry * parent, struct qstr * name, struct nameidata *nd)
{
struct dentry * result;
struct inode *dir = parent->d_inode;
mutex_lock(&dir->i_mutex);
// 查找緩存中的dentry項
result = d_lookup(parent, name);
if (!result) {
// 沒找到, 建立dentry項
struct dentry * dentry = d_alloc(parent, name);
result = ERR_PTR(-ENOMEM);
if (dentry) {
// 調用inode的查找操作, 這是和具體檔案系統相關
result = dir->i_op->lookup(dir, dentry, nd);
if (result)
// 失敗, 釋放dentry
dput(dentry);
else
// 成功, 找到的dentry作為結果傳回
result = dentry;
}
mutex_unlock(&dir->i_mutex);
return result;
}
// 在緩存中找到dentry項, 進行validate操作
mutex_unlock(&dir->i_mutex);
if (result->d_op && result->d_op->d_revalidate) {
result = do_revalidate(result, nd);
if (!result)
result = ERR_PTR(-ENOENT);
}
return result;
}
小結一下函數調用順序:
path_lookup_open path_lookup_create
| |
V V
__path_lookup_intent_open
|
V
do_path_lookup
|
V
link_path_walk
|
V
__link_path_walk
|
V
do_lookup
|
V
real_lookup
這些函數操作都屬于虛拟檔案系統操作, 對所有類型的檔案系統都适用, 而從各個FS的具體實作才能看出差異和相關标志的作用.
4.3 open_namei_create
static int open_namei_create(struct nameidata *nd, struct path *path,
int flag, int mode)
{
int error;
// nd目前的dentry
struct dentry *dir = nd->dentry;
if (!IS_POSIXACL(dir->d_inode))
mode &= ~current->fs->umask;
error = vfs_create(dir->d_inode, path->dentry, mode, nd);
mutex_unlock(&dir->d_inode->i_mutex);
dput(nd->dentry);
nd->dentry = path->dentry;
if (error)
return error;
return may_open(nd, 0, flag & ~O_TRUNC);
}
4.4 path_to_nameidata
// 将路徑參數指派到nameidata結構中
static inline void path_to_nameidata(struct path *path, struct nameidata *nd)
{
// 釋放原來的目錄項
dput(nd->dentry);
// 如果挂接點也不同,釋放掉原來的
if (nd->mnt != path->mnt)
mntput(nd->mnt);
// 将新路徑參數指派到nameidata結構中
nd->mnt = path->mnt;
nd->dentry = path->dentry;
}
5. 結論
打開檔案時, 目的是要生成一個struct file的結構的指針, 該結構中有相關檔案名的名稱, dentry指針, 挂接點檔案系統等資訊, 而struct nameidata作為一個中間過程結構儲存相關的處理結果, 最終傳回需要的檔案資訊。
本文來自CSDN部落格,轉載請标明出處:http://blog.csdn.net/air_snake/archive/2008/07/22/2690554.aspx