天天看點

sys_open分析,從檔案名找到檔案資訊

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==&apos;/&apos;) {

// 絕對路徑格式

  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==&apos;/&apos;)

  name++;

// 空路徑

 if (!*name)

  goto return_reval;

// 路徑對應的inode

 inode = nd->dentry->d_inode;

 if (nd->depth)

  lookup_flags = LOOKUP_FOLLOW | (nd->flags & LOOKUP_CONTINUE);

 for(;;) {

// 循環處理,每個循環提取檔案路徑的一個目錄名, &apos;/&apos;分隔

  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, 不包括&apos;/&apos;

  hash = init_name_hash();

  do {

   name++;

   hash = partial_name_hash(c, hash);

   c = *(const unsigned char *)name;

  } while (c && (c != &apos;/&apos;));

// 目錄(如果有的話)的名稱長度

  this.len = name - (const char *) this.name;

// hash

  this.hash = end_name_hash(hash);

// c為0表示是最後的具體檔案名了

  if (!c)

   goto last_component;

// 跳過中間的&apos;/&apos;

  while (*++name == &apos;/&apos;);

// 到名稱尾, 說明檔案名最後一個字元是&apos;/&apos;

  if (!*name)

   goto last_with_slashes;

// 如果第一個字元是&apos;.&apos;

  if (this.name[0] == &apos;.&apos;) switch (this.len) {

   default:

// 是一個一&apos;.&apos;開頭的檔案或目錄名稱

    break;

   case 2:

// 第2 個字元不是".", 是普通檔案或路徑名

    if (this.name[1] != &apos;.&apos;)

     break;

// 以".."開頭, 是父目錄, 更新nd為父目錄nameidata資料, inode相應更新重新循環

    follow_dotdot(nd);

    inode = nd->dentry->d_inode;

   case 1:

// 以&apos;.&apos;開頭的目前目錄, 忽略, 重新循環

    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:

// 最後一個字元是&apos;/&apos;, 是一個目錄

  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] == &apos;.&apos;) switch (this.len) {

   default:

    break;

   case 2:

// 檔案名不是"..", 繼續

    if (this.name[1] != &apos;.&apos;)

     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] != &apos;.&apos;)

   goto return_base;

  if (this.len == 1)

   nd->last_type = LAST_DOT;

  else if (this.len == 2 && this.name[1] == &apos;.&apos;)

   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

繼續閱讀