转:
http://blog.chinaunix.net/uid-12567959-id-160998.html
do_lookup()根据父目录的路径,及文件名来找到文件的路径,也就是目录项和vfsmount,回忆一下,do_lookup()的调用环境,在link_path_walk()中有:
fs/namei.c nd->flags |= LOOKUP_CONTINUE; err = exec_permission(inode); if (err) break; this.name = name; c = *(const unsigned char *)name; 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; this.hash = end_name_hash(hash); /* remove trailing slashes? */ if (!c) goto last_component; while (*++name == '/'); if (!*name) goto last_with_slashes; err = do_lookup(nd, &this, &next); if (err) break;
fs/namei.c static int do_lookup(struct nameidata *nd, struct qstr *name, struct path *path) { struct vfsmount *mnt = nd->path.mnt; struct dentry *dentry, *parent; struct inode *dir; /* * See if the low-level filesystem might want * to use its own hash.. */ if (nd->path.dentry->d_op && nd->path.dentry->d_op->d_hash) { int err = nd->path.dentry->d_op->d_hash(nd->path.dentry, name); if (err < 0) return err; } dentry = __d_lookup(nd->path.dentry, name); if (!dentry) goto need_lookup; if (dentry->d_op && dentry->d_op->d_revalidate) goto need_revalidate; done: path->mnt = mnt; path->dentry = dentry; __follow_mount(path); return 0; need_lookup: parent = nd->path.dentry; dir = parent->d_inode; mutex_lock(&dir->i_mutex); /* * First re-do the cached lookup just in case it was created * while we waited for the directory semaphore.. * * FIXME! This could use version numbering or similar to * avoid unnecessary cache lookups. * * The "dcache_lock" is purely to protect the RCU list walker * from concurrent renames at this point (we mustn't get false * negatives from the RCU list walk here, unlike the optimistic * fast walk). * * so doing d_lookup() (with seqlock), instead of lockfree __d_lookup */ dentry = d_lookup(parent, name); if (!dentry) { struct dentry *new; /* Don't create child dentry for a dead directory. */ dentry = ERR_PTR(-ENOENT); if (IS_DEADDIR(dir)) goto out_unlock; new = d_alloc(parent, name); dentry = ERR_PTR(-ENOMEM); if (new) { dentry = dir->i_op->lookup(dir, new, nd); if (dentry) dput(new); else dentry = new; } out_unlock: mutex_unlock(&dir->i_mutex); if (IS_ERR(dentry)) goto fail; goto done; } /* * Uhhuh! Nasty case: the cache was re-populated while * we waited on the semaphore. Need to revalidate. */ mutex_unlock(&dir->i_mutex); if (dentry->d_op && dentry->d_op->d_revalidate) { dentry = do_revalidate(dentry, nd); if (!dentry) dentry = ERR_PTR(-ENOENT); } if (IS_ERR(dentry)) goto fail; goto done; need_revalidate: dentry = do_revalidate(dentry, nd); if (!dentry) goto need_lookup; if (IS_ERR(dentry)) goto fail; goto done; fail: return PTR_ERR(dentry); }
fs/dcache.c struct dentry * __d_lookup(struct dentry * parent, struct qstr * name) { unsigned int len = name->len; unsigned int hash = name->hash; const unsigned char *str = name->name; struct hlist_head *head = d_hash(parent,hash); struct dentry *found = NULL; struct hlist_node *node; struct dentry *dentry; rcu_read_lock(); hlist_for_each_entry_rcu(dentry, node, head, d_hash) { struct qstr *qstr; if (dentry->d_name.hash != hash) continue; if (dentry->d_parent != parent) continue; spin_lock(&dentry->d_lock); /* * Recheck the dentry after taking the lock - d_move may have * changed things. Don't bother checking the hash because we're * about to compare the whole name anyway. */ if (dentry->d_parent != parent) goto next; /* non-existing due to RCU? */ if (d_unhashed(dentry)) goto next; /* * It is safe to compare names since d_move() cannot * change the qstr (protected by d_lock). */ qstr = &dentry->d_name; if (parent->d_op && parent->d_op->d_compare) { if (parent->d_op->d_compare(parent, qstr, name)) goto next; } else { if (qstr->len != len) goto next; if (memcmp(qstr->name, str, len)) goto next; } atomic_inc(&dentry->d_count); found = dentry; spin_unlock(&dentry->d_lock); break; next: spin_unlock(&dentry->d_lock); } rcu_read_unlock(); return found; }
fs/dcache.c static inline struct hlist_head *d_hash(struct dentry *parent, unsigned long hash) { hash += ((unsigned long) parent ^ GOLDEN_RATIO_PRIME) / L1_CACHE_BYTES; hash = hash ^ ((hash ^ GOLDEN_RATIO_PRIME) >> D_HASHBITS); return dentry_hashtable + (hash & D_HASHMASK); }
fs/dcache.c struct dentry *d_alloc(struct dentry * parent, const struct qstr *name) { struct dentry *dentry; char *dname; dentry = kmem_cache_alloc(dentry_cache, GFP_KERNEL); if (!dentry) return NULL; if (name->len > DNAME_INLINE_LEN-1) { dname = kmalloc(name->len + 1, GFP_KERNEL); if (!dname) { kmem_cache_free(dentry_cache, dentry); return NULL; } } else { dname = dentry->d_iname; } dentry->d_name.name = dname; dentry->d_name.len = name->len; dentry->d_name.hash = name->hash; memcpy(dname, name->name, name->len); dname[name->len] = 0; atomic_set(&dentry->d_count, 1); dentry->d_flags = DCACHE_UNHASHED; spin_lock_init(&dentry->d_lock); dentry->d_inode = NULL; dentry->d_parent = NULL; dentry->d_sb = NULL; dentry->d_op = NULL; dentry->d_fsdata = NULL; dentry->d_mounted = 0; INIT_HLIST_NODE(&dentry->d_hash); INIT_LIST_HEAD(&dentry->d_lru); INIT_LIST_HEAD(&dentry->d_subdirs); INIT_LIST_HEAD(&dentry->d_alias); if (parent) { dentry->d_parent = dget(parent); dentry->d_sb = parent->d_sb; } else { INIT_LIST_HEAD(&dentry->d_u.d_child); } spin_lock(&dcache_lock); if (parent) list_add(&dentry->d_u.d_child, &parent->d_subdirs); dentry_stat.nr_dentry++; spin_unlock(&dcache_lock); return dentry; }(3)、若分配失败则返回-ENOMEM。
b. 若返回非NULL,检查返回值的类型,若是错误码,则返回错误码。若是有效地目录项,则设置path->mnt为nd->path.mnt,path->dentry为查找到的目录项dentry。然后在path上调用__follow_mount(path)并返回0。
在do_lookup函数中调用__d_lookup返回后,并找到相应的目录项后,会回到:
done: path->mnt = mnt;//设置path->mnt为nd->path.mnt path->dentry = dentry;//,path->dentry为查找到的目录项dentry __follow_mount(path); return 0;
static int __follow_mount(struct path *path) { int res = 0; while (d_mountpoint(path->dentry)) { struct vfsmount *mounted = lookup_mnt(path); if (!mounted) break; dput(path->dentry); if (res) mntput(path->mnt); path->mnt = mounted; path->dentry = dget(mounted->mnt_root); res = 1; } return res; }
/* * lookup_mnt increments the ref count before returning * the vfsmount struct. */ struct vfsmount *lookup_mnt(struct path *path) { struct vfsmount *child_mnt; spin_lock(&vfsmount_lock); if ((child_mnt = __lookup_mnt(path->mnt, path->dentry, 1))) mntget(child_mnt); spin_unlock(&vfsmount_lock); return child_mnt; }
lookup_mnt() 函数用于查找一个 VFS 目录树下某一目录最近一次被 mount 时的安装区域块的指针,这样当这个目录有新的挂载点时,查找的就是这个最新的挂载点下的目录。