Coder
分类: 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;
843
844 hash = init_name_hash();
845 do {
846 name++;
847 hash =
partial_name_hash(c, hash);
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.首先,获得要父目录的inode的i_mutex锁。
b.调用d_lookup(parent,
name)来在目录项缓存中查找,以防在上一步等待信号量的时候已经有进程创建了我们要查找的目录项。
c.如果d_lookup(parent,
name)返回非NULL值,则首先解对父目录的inode的i_mutex锁,然后检查dentry->d_op->d_revalidate方法是否有效,若是对查找结果dentry调用它,该方法成功返回时do_lookup(),设置path->mnt为nd->path.mnt,path->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方法从磁盘读取该目录,创建一个新的目录项对象并把它插入到目录项高速缓存中,然后创建一个新的索引节点对象并把它插入到索引节点高速缓存中。解除对父目录的inode的i_mutex锁,然后检查返回值的类型,若是错误码,则返回错误码。若是有效地目录项,则设置path->mnt为nd->path.mnt,path->dentry为查找到的目录项dentry。然后在path上调用__follow_mount(path)并返回0。
4、非常幸运的直接在目录项缓存中找到了要查找的目录项对象,则
a.调用do_revalidate(dentry,
nd)检查其有效性,若返回NULL,则执行同第3不完全相同的操作。
b. 若返回非NULL,检查返回值的类型,若是错误码,则返回错误码。若是有效地目录项,则设置path->mnt为nd->path.mnt,path->dentry为查找到的目录项dentry。然后在path上调用__follow_mount(path)并返回0。