根据一个路径定位到一个文件的inode和数据块,这个过程看起来简单,但代码比较复杂。路径定位的过程就是依次从dentry对象的子节点匹配下一级目录,直到定位到最终的节点。
进程的fs->root和fs->pwd这2个dentry对象分别指向进程的根目录和当前目录,以根目录/开头时就从fs->root作为起点,否则表示相对路径就从fs->pwd开始。
比较重要的几个结构体:qstr,path,nameidata
include/linux/dcache.h
struct qstr {
union {
struct {
HASH_LEN_DECLARE;
};
u64 hash_len;
};
const unsigned char *name;
};
qstr表示路径字符串的项目,在路径定位过程中,以/bin/bash为例,qstr对象的name先指向内核解析出的bin字符串,len为3,后面的以此类推。
static int link_path_walk(const char *name, struct nameidata *nd)
{
while (*name=='/')
name++;
if (!*name)
return 0;
for(;;) {
u64 hash_len;
int type;
err = may_lookup(nd);
if (err)
break;
hash_len = hash_name(name);
继续看nameidata的定义
enum { MAX_NESTED_LINKS = 8 };
struct nameidata {
struct path path;
struct qstr last;
struct path root;
struct inode *inode; /* path.dentry.d_inode */
unsigned int flags;
unsigned seq, m_seq;
int last_type;
unsigned depth;
char *saved_names[MAX_NESTED_LINKS + 1];
};
指针数组saved_names指向每一级别的符号链接,最大为8,这是为了避免定位符号链接陷入死循环,结合depth使用,depth表示符号链接的深度,最大为8.
struct path {
struct vfsmount *mnt;
struct dentry *dentry;
};
现在来看具体定位路径的过程do_path_lookup函数:
static int do_path_lookup(int dfd, const char *name,
unsigned int flags, struct nameidata *nd)
{
struct filename filename = { .name = name };
return filename_lookup(dfd, &filename, flags, nd);
}
中间的路径字符解析代码比较繁琐暂不分析了,上面代码都是在内存中查找和搜索,如果找不到就读取磁盘数据查找,接下来看具体文件系统的lookup实现:
const struct inode_operations ext4_dir_inode_operations = {
.lookup = ext4_lookup,
static struct dentry *ext4_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)
{
struct inode *inode;
struct ext4_dir_entry_2 *de;
struct buffer_head *bh;
if (dentry->d_name.len > EXT4_NAME_LEN)
return ERR_PTR(-ENAMETOOLONG);
bh = ext4_find_entry(dir, &dentry->d_name, &de, NULL);
通过文件名dentry->d_name读取磁盘上面的信息获取到具体的direntry数据装入到磁盘direntry结构体ext4_dir_entry_2中,具体就是里面有个大循环进行搜索。
if (bh) {
__u32 ino = le32_to_cpu(de->inode);
brelse(bh);
inode = ext4_iget(dir->i_sb, ino);
}
return d_splice_alias(inode, dentry);
获取到inode号后,通过函数ext4_iget获取到VFS中的inode结构,最后通过d_splice_alias把inode加入到direntry树中返回。