do_lookup()根据父目录的路径,及文件名来找到文件的路径,也就是目录项和vfsmount,回忆一下,do_lookup()的调用环境,在link_path_walk()中有:
---------------------------------------------------------------------
fs/namei.c
836nd->flags |=
LOOKUP_CONTINUE;
837err = exec_permission(inode);
838if (err)
839break;
840
841this.name = name;
842c= *(const unsigned char
*)name;
843
844hash = init_name_hash();
845do {
846name++;
847hash =
partial_name_hash(c, hash);
848c= *(const unsigned char
*)name;
849} while (c && (c !=
'/'));
850this.len = name - (const char
*) this.name;
851this.hash =
end_name_hash(hash);
852
853/* remove trailing slashes? */
854if (!c)
855goto last_component;
856while (*++name == '/');
857if (!*name)
858goto
last_with_slashes;
859
878err = do_lookup(nd, &this,
&next);
879if (err)
880break;
---------------------------------------------------------------------
在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,
699struct path *path)
700
{
701struct vfsmount *mnt =
nd->path.mnt;
702struct dentry *dentry, *parent;
703struct inode *dir;
704/*
705* See if the low-level filesystem
might want
706* to use its own hash..
707*/
708if (nd->path.dentry->d_op
&& nd->path.dentry->d_op->d_hash) {
709int err =
nd->path.dentry->d_op->d_hash(nd->path.dentry, name);
710if (err < 0)
711return err;
712}
713
714dentry =
__d_lookup(nd->path.dentry, name);
715if (!dentry)
716goto need_lookup;
717if (dentry->d_op &&
dentry->d_op->d_revalidate)
718goto need_revalidate;
719
done:
720path->mnt = mnt;
721path->dentry = dentry;
722__follow_mount(path);
723return 0;
724
725
need_lookup:
726parent = nd->path.dentry;
727dir = parent->d_inode;
728
729mutex_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*/
744dentry = d_lookup(parent, name);
745if (!dentry) {
746struct dentry *new;
747
748/* Don't create child dentry
for a dead directory. */
749dentry = ERR_PTR(-ENOENT);
750if (IS_DEADDIR(dir))
751goto out_unlock;
752
753new = d_alloc(parent, name);
754dentry = ERR_PTR(-ENOMEM);
755if (new) {
756dentry =
dir->i_op->lookup(dir, new, nd);
757if (dentry)
758
dput(new);
759else
760dentry = new;
761}
762
out_unlock:
763mutex_unlock(&dir->i_mutex);
764if (IS_ERR(dentry))
765goto fail;
766goto done;
767}
768
769/*
770* Uhhuh! Nasty case: the cache was
re-populated while
771* we waited on the semaphore. Need to
revalidate.
772*/
773mutex_unlock(&dir->i_mutex);
774if (dentry->d_op &&
dentry->d_op->d_revalidate) {
775dentry = do_revalidate(dentry,
nd);
776if (!dentry)
777dentry =
ERR_PTR(-ENOENT);
778}
779
if (IS_ERR(dentry))
780goto fail;
781goto done;
782
783
need_revalidate:
784dentry = do_revalidate(dentry, nd);
785if (!dentry)
786goto need_lookup;
787if (IS_ERR(dentry))
788goto fail;
789goto done;
790
791
fail:
792return 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
{
1376unsigned int len = name->len;
1377unsigned int hash = name->hash;
1378const unsigned char *str =
name->name;
1379struct hlist_head *head =
d_hash(parent,hash);
1380struct dentry *found = NULL;
1381struct hlist_node *node;
1382struct dentry *dentry;
1383
1384rcu_read_lock();
1385
1386hlist_for_each_entry_rcu(dentry, node,
head, d_hash) {
1387struct qstr *qstr;
1388
1389if (dentry->d_name.hash !=
hash)
1390continue;
1391if (dentry->d_parent !=
parent)
1392continue;
1393
1394spin_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*/
1401if (dentry->d_parent !=
parent)
1402goto next;
1403
1404/* non-existing due to RCU? */
1405if (d_unhashed(dentry))
1406goto next;
1407
1408/*
1409* It is safe to compare names
since d_move() cannot
1410* change the qstr (protected
by d_lock).
1411*/
1412qstr = &dentry->d_name;
1413if (parent->d_op &&
parent->d_op->d_compare) {
1414if
(parent->d_op->d_compare(parent, qstr, name))
1415goto next;
1416} else {
1417if (qstr->len !=
len)
1418goto next;
1419if
(memcmp(qstr->name, str, len))
1420goto next;
1421}
1422
1423atomic_inc(&dentry->d_count);
1424found = dentry;
1425spin_unlock(&dentry->d_lock);
1426break;
1427
next:
1428spin_unlock(&dentry->d_lock);
1429}
1430rcu_read_unlock();
1431
1432return found;
1433
}
---------------------------------------------------------------------
a.调用d_hash(parent,hash)来找到目录项可能存在于其中的哈希表项,也就是hlist_head指针,存放在局部变量head中。
---------------------------------------------------------------------
fs/dcache.c
1123
static inline struct hlist_head *d_hash(struct dentry *parent,
1124unsigned long hash)
1125
{
1126hash += ((unsigned long) parent ^
GOLDEN_RATIO_PRIME) / L1_CACHE_BYTES;
1127hash = hash ^ ((hash ^
GOLDEN_RATIO_PRIME) >> D_HASHBITS);
1128return 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
{
927struct dentry *dentry;
928char *dname;
929
930dentry =
kmem_cache_alloc(dentry_cache, GFP_KERNEL);
931if (!dentry)
932return NULL;
933
934if (name->len >
DNAME_INLINE_LEN-1) {
935dname = kmalloc(name->len +
1, GFP_KERNEL);
936if (!dname) {
937kmem_cache_free(dentry_cache, dentry);
938return NULL;
939}
940} else{
941dname = dentry->d_iname;
942}
943dentry->d_name.name = dname;
944
945dentry->d_name.len = name->len;
946dentry->d_name.hash = name->hash;
947memcpy(dname, name->name,
name->len);
948dname[name->len] = 0;
949
950atomic_set(&dentry->d_count,
1);
951dentry->d_flags = DCACHE_UNHASHED;
952spin_lock_init(&dentry->d_lock);
953dentry->d_inode = NULL;
954dentry->d_parent = NULL;
955dentry->d_sb = NULL;
956dentry->d_op = NULL;
957dentry->d_fsdata = NULL;
958dentry->d_mounted = 0;
959INIT_HLIST_NODE(&dentry->d_hash);
960INIT_LIST_HEAD(&dentry->d_lru);
961INIT_LIST_HEAD(&dentry->d_subdirs);
962INIT_LIST_HEAD(&dentry->d_alias);
963
964if (parent) {
965dentry->d_parent =
dget(parent);
966dentry->d_sb =
parent->d_sb;
967} else {
968INIT_LIST_HEAD(&dentry->d_u.d_child);
969}
970
971spin_lock(&dcache_lock);
972if (parent)
973list_add(&dentry->d_u.d_child, &parent->d_subdirs);
974dentry_stat.nr_dentry++;
975spin_unlock(&dcache_lock);
976
977return 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。