Chinaunix首页 | 论坛 | 博客
  • 博客访问: 819752
  • 博文数量: 117
  • 博客积分: 2583
  • 博客等级: 少校
  • 技术积分: 1953
  • 用 户 组: 普通用户
  • 注册时间: 2008-12-06 22:58
个人简介

Coder

文章分类
文章存档

2013年(1)

2012年(10)

2011年(12)

2010年(77)

2009年(13)

2008年(4)

分类: LINUX

2010-07-31 11:40:30

do_lookup()根据父目录的路径,及文件名来找到文件的路径,也就是目录项和vfsmount,回忆一下,do_lookup()的调用环境,在link_path_walk()中有:

---------------------------------------------------------------------

fs/namei.c

836                 nd->flags |= LOOKUP_CONTINUE;

837                 err = exec_permission(inode);

838                 if (err)

839                         break;

840

841                 this.name = name;

842                 c = *(const unsigned char *)name;

843

844                 hash = init_name_hash();

845                 do {

846                         name++;

847                         hash = partial_name_hash(c, hash);

848                         c = *(const unsigned char *)name;

849                 } while (c && (c != '/'));

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

851                 this.hash = end_name_hash(hash);

852

853                 /* remove trailing slashes? */

854                 if (!c)

855                         goto last_component;

856                 while (*++name == '/');

857                 if (!*name)

858                         goto last_with_slashes;

859

 

878                 err = do_lookup(nd, &this, &next);

879                 if (err)

880                         break;

---------------------------------------------------------------------

qstr结构局部变量this中存有路径分量的信息,包括文件名字符串地址及其长度,根据文件名算得的哈希值,nd变量中存有父路径的信息,包括vfsmount对象地址和目录项对象地址。Path结构体类型的next变量用来存放查找的结果。

 

do_lookup()接受3个参数,nd保存有要查找的分量所在的目录的信息,name要查找的分量的名字信息,path则用于返回查找的结果。do_lookup()定义如下:

---------------------------------------------------------------------

fs/namei.c

698 static int do_lookup(struct nameidata *nd, struct qstr *name,

699                      struct path *path)

700 {

701         struct vfsmount *mnt = nd->path.mnt;

702         struct dentry *dentry, *parent;

703         struct inode *dir;

704         /*

705          * See if the low-level filesystem might want

706          * to use its own hash..

707          */

708         if (nd->path.dentry->d_op && nd->path.dentry->d_op->d_hash) {

709                 int err = nd->path.dentry->d_op->d_hash(nd->path.dentry, name);

710                 if (err < 0)

711                         return err;

712         }

713

714         dentry = __d_lookup(nd->path.dentry, name);

715         if (!dentry)

716                 goto need_lookup;

717         if (dentry->d_op && dentry->d_op->d_revalidate)

718                 goto need_revalidate;

719 done:

720         path->mnt = mnt;

721         path->dentry = dentry;

722         __follow_mount(path);

723         return 0;

724

725 need_lookup:

726         parent = nd->path.dentry;

727         dir = parent->d_inode;

728

729         mutex_lock(&dir->i_mutex);

730         /*

731          * First re-do the cached lookup just in case it was created

732          * while we waited for the directory semaphore..

733          *

734          * FIXME! This could use version numbering or similar to

735          * avoid unnecessary cache lookups.

736          *

737          * The "dcache_lock" is purely to protect the RCU list walker

738          * from concurrent renames at this point (we mustn't get false

739          * negatives from the RCU list walk here, unlike the optimistic

740          * fast walk).

741          *

742          * so doing d_lookup() (with seqlock), instead of lockfree __d_lookup

743          */

744         dentry = d_lookup(parent, name);

745         if (!dentry) {

746                 struct dentry *new;

747

748                 /* Don't create child dentry for a dead directory. */

749                 dentry = ERR_PTR(-ENOENT);

750                 if (IS_DEADDIR(dir))

751                         goto out_unlock;

752

753                 new = d_alloc(parent, name);

754                 dentry = ERR_PTR(-ENOMEM);

755                 if (new) {

756                         dentry = dir->i_op->lookup(dir, new, nd);

757                         if (dentry)

758                                 dput(new);

759                         else

760                                 dentry = new;

761                 }

762 out_unlock:

763                 mutex_unlock(&dir->i_mutex);

764                 if (IS_ERR(dentry))

765                         goto fail;

766                 goto done;

767         }

768

769         /*

770          * Uhhuh! Nasty case: the cache was re-populated while

771          * we waited on the semaphore. Need to revalidate.

772          */

773         mutex_unlock(&dir->i_mutex);

774         if (dentry->d_op && dentry->d_op->d_revalidate) {

775                 dentry = do_revalidate(dentry, nd);

776                 if (!dentry)

777                         dentry = ERR_PTR(-ENOENT);

778         }

779         if (IS_ERR(dentry))

780                 goto fail;

781         goto done;

782

783 need_revalidate:

784         dentry = do_revalidate(dentry, nd);

785         if (!dentry)

786                 goto need_lookup;

787         if (IS_ERR(dentry))

788                 goto fail;

789         goto done;

790

791 fail:

792         return PTR_ERR(dentry);

793 }

---------------------------------------------------------------------

这个函数也有点长,但结构还算清晰。

1、检查底层文件系统是否要使用它自己的哈希方法(nd->path.dentry->d_op->d_hash),若是,则调用该方法来更新已经计算出的分量名的哈希值。

 

2、调用__d_lookup(nd->path.dentry, name)来在目录项高速缓存中搜索分量的目录项对象。该函数定义如下:

---------------------------------------------------------------------

fs/dcache.c

1374 struct dentry * __d_lookup(struct dentry * parent, struct qstr * name)

1375 {

1376         unsigned int len = name->len;

1377         unsigned int hash = name->hash;

1378         const unsigned char *str = name->name;

1379         struct hlist_head *head = d_hash(parent,hash);

1380         struct dentry *found = NULL;

1381         struct hlist_node *node;

1382         struct dentry *dentry;

1383

1384         rcu_read_lock();

1385        

1386         hlist_for_each_entry_rcu(dentry, node, head, d_hash) {

1387                 struct qstr *qstr;

1388

1389                 if (dentry->d_name.hash != hash)

1390                         continue;

1391                 if (dentry->d_parent != parent)

1392                         continue;

1393

1394                 spin_lock(&dentry->d_lock);

1395

1396                 /*

1397                  * Recheck the dentry after taking the lock - d_move may have

1398                  * changed things.  Don't bother checking the hash because we're

1399                  * about to compare the whole name anyway.

1400                  */

1401                 if (dentry->d_parent != parent)

1402                         goto next;

1403

1404                 /* non-existing due to RCU? */

1405                 if (d_unhashed(dentry))

1406                         goto next;

1407

1408                 /*

1409                  * It is safe to compare names since d_move() cannot

1410                  * change the qstr (protected by d_lock).

1411                  */

1412                 qstr = &dentry->d_name;

1413                 if (parent->d_op && parent->d_op->d_compare) {

1414                         if (parent->d_op->d_compare(parent, qstr, name))

1415                                 goto next;

1416                 } else {

1417                         if (qstr->len != len)

1418                                 goto next;

1419                         if (memcmp(qstr->name, str, len))

1420                                 goto next;

1421                 }

1422

1423                 atomic_inc(&dentry->d_count);

1424                 found = dentry;

1425                 spin_unlock(&dentry->d_lock);

1426                 break;

1427 next:

1428                 spin_unlock(&dentry->d_lock);

1429         }

1430         rcu_read_unlock();

1431

1432         return found;

1433 }

---------------------------------------------------------------------

a.调用d_hash(parent,hash)来找到目录项可能存在于其中的哈希表项,也就是hlist_head指针,存放在局部变量head中。

---------------------------------------------------------------------

fs/dcache.c

1123 static inline struct hlist_head *d_hash(struct dentry *parent,

1124                                         unsigned long hash)

1125 {

1126         hash += ((unsigned long) parent ^ GOLDEN_RATIO_PRIME) / L1_CACHE_BYTES;

1127         hash = hash ^ ((hash ^ GOLDEN_RATIO_PRIME) >> D_HASHBITS);

1128         return dentry_hashtable + (hash & D_HASHMASK);

1129 }

---------------------------------------------------------------------

 

b.执行循环hlist_for_each_entry_rcu(dentry, node, head, d_hash),在链表中查找

 

c.返回查找结果。若没找到,则返回NULL,若找到则返回目录项。

 

3、如果没有找到这样的目录项对象,则执行如下操作:

a.首先,获得要父目录的inodei_mutex锁。

 

b.调用d_lookup(parent, name)来在目录项缓存中查找,以防在上一步等待信号量的时候已经有进程创建了我们要查找的目录项。

 

c.如果d_lookup(parent, name)返回非NULL值,则首先解对父目录的inodei_mutex锁,然后检查dentry->d_op->d_revalidate方法是否有效,若是对查找结果dentry调用它,该方法成功返回时do_lookup(),设置path->mntnd->path.mntpath->dentry为查找到的目录项dentry。然后在path上调用__follow_mount(path)并返回0。该方法失败时,则返回错误码。

 

d.如果d_lookup(parent, name)依然返回NULL值,即说明目录项缓存中依然没有我们要查找的目录项。则

(1)、首先检查提供的父目录路径是不是真的是一个目录文件,若不是对对父目录的inode解锁并返回-ENOENT

 

(2)、父目录路径是一个目录文件。则调用d_alloc(parent, name)来分配并填充一个目录项。其定义为:

---------------------------------------------------------------------

fs/dcache.c

925 struct dentry *d_alloc(struct dentry * parent, const struct qstr *name)

926 {

927         struct dentry *dentry;

928         char *dname;

929

930         dentry = kmem_cache_alloc(dentry_cache, GFP_KERNEL);

931         if (!dentry)

932                 return NULL;

933

934         if (name->len > DNAME_INLINE_LEN-1) {

935                 dname = kmalloc(name->len + 1, GFP_KERNEL);

936                 if (!dname) {

937                         kmem_cache_free(dentry_cache, dentry);

938                         return NULL;

939                 }

940         } else  {

941                 dname = dentry->d_iname;

942         }      

943         dentry->d_name.name = dname;

944

945         dentry->d_name.len = name->len;

946         dentry->d_name.hash = name->hash;

947         memcpy(dname, name->name, name->len);

948         dname[name->len] = 0;

949

950         atomic_set(&dentry->d_count, 1);

951         dentry->d_flags = DCACHE_UNHASHED;

952         spin_lock_init(&dentry->d_lock);

953         dentry->d_inode = NULL;

954         dentry->d_parent = NULL;

955         dentry->d_sb = NULL;

956         dentry->d_op = NULL;

957         dentry->d_fsdata = NULL;

958         dentry->d_mounted = 0;

959         INIT_HLIST_NODE(&dentry->d_hash);

960         INIT_LIST_HEAD(&dentry->d_lru);

961         INIT_LIST_HEAD(&dentry->d_subdirs);

962         INIT_LIST_HEAD(&dentry->d_alias);

963

964         if (parent) {

965                 dentry->d_parent = dget(parent);

966                 dentry->d_sb = parent->d_sb;

967         } else {

968                 INIT_LIST_HEAD(&dentry->d_u.d_child);

969         }

970

971         spin_lock(&dcache_lock);

972         if (parent)

973                 list_add(&dentry->d_u.d_child, &parent->d_subdirs);

974         dentry_stat.nr_dentry++;

975         spin_unlock(&dcache_lock);

976

977         return dentry;

978 }

---------------------------------------------------------------------

 

(3)、若分配失败则返回-ENOMEM

 

(4)、成功分配目录项,则执行父目录索引节点的lookup方法从磁盘读取该目录,创建一个新的目录项对象并把它插入到目录项高速缓存中,然后创建一个新的索引节点对象并把它插入到索引节点高速缓存中。解除对父目录的inodei_mutex锁,然后检查返回值的类型,若是错误码,则返回错误码。若是有效地目录项,则设置path->mntnd->path.mntpath->dentry为查找到的目录项dentry。然后在path上调用__follow_mount(path)并返回0

 

4、非常幸运的直接在目录项缓存中找到了要查找的目录项对象,则

a.调用do_revalidate(dentry, nd)检查其有效性,若返回NULL,则执行同第3不完全相同的操作。

b. 若返回非NULL,检查返回值的类型,若是错误码,则返回错误码。若是有效地目录项,则设置path->mntnd->path.mntpath->dentry为查找到的目录项dentry。然后在path上调用__follow_mount(path)并返回0

阅读(2850) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~