linux0.11源码分析-目录查找

664 阅读4分钟

如果想找到一个文件,就要找到文件的inode,但是inode中并没有存文件的名字。文件的内容是什么,inode是不关心的,如果是普通文件就是普通文件的数据,如果是目录,里面存的就是目录项这个数据结构,要想找到指定名字的文件,必须从目录项中一级一级的查找。目录项中第一个的名字就是".."上级目录,第二个就是"."表示当前目录,后面的就是这个目录中存的其他文件inode。

dir_namei

比如 pathname = “/a/b/c/d.txt”就是在获取 c这个目录inode。

fs/namei.c

static struct m_inode * dir_namei(const char * pathname,int * namelen, const char ** name)
{
	char c;
	const char * basename;
	struct m_inode * dir;
    
	if (!(dir = get_dir(pathname)))
		return NULL;
        
	basename = pathname;
	while ((c=get_fs_byte(pathname++)))
		if (c=='/')
			basename=pathname;
            
	*namelen = pathname-basename-1;
	*name = basename;
	return dir;
}

get_dir

比如 pathname = “/a/b/c/d.txt,get_dir就是先确定是绝对路径还是相对路径,这个路径以"/"开始,所以是绝对路径,找到根目录inode,从这个inode中找到名字叫a的inode,然后从a这个inode目录项中找到名字叫b的inode,以此类推一直找到c这个inode。

fs/namei.c

static struct m_inode * get_dir(const char * pathname)
{
	char c; //当前路径的字符
	const char * thisname;
	struct m_inode * inode;
	struct buffer_head * bh; // 缓冲区
	int namelen,inr,idev;
	struct dir_entry * de; //目录项

    //根目录和当前目录是否存在
	if (!current->root || !current->root->i_count)
		panic("No root inode");
	if (!current->pwd || !current->pwd->i_count)
		panic("No cwd inode");
        
        // 如果第一个字符是/说明的是绝对位置
	if ((c=get_fs_byte(pathname))=='/') {
		inode = current->root; //当前目录是根目录
		pathname++; //下一个字符
	} else if (c)
		inode = current->pwd; //相对位置,那么当前位置就是当前工作目录
	else
		return NULL;	/* empty name is bad */

	inode->i_count++; //当前目录被引用一次,引用数加1
    
	while (1) {
		thisname = pathname;
        	// 如果不是目录或者没有权限进入目录,返回NULL
		if (!S_ISDIR(inode->i_mode) || !permission(inode,MAY_EXEC)) {
			iput(inode); //放回inode
			return NULL;
		}
        
		for(namelen=0;(c=get_fs_byte(pathname++))&&(c!='/');namelen++)
			/* nothing */ ;
         		
                // 到这里说明c是null或者c等于/,如果是空说明查找完毕,直接返回,否则根据目录名找到对应的目录项内容
		if (!c)
			return inode;
         
        	// node节点下查找,thisname为当前级的目录名,namelen为当前级目录名长度
		if (!(bh = find_entry(&inode,thisname,namelen,&de))) {
			iput(inode);
			return NULL;
		}
		inr = de->inode;  // 当前目录名部分的i节点号
		idev = inode->i_dev;
		brelse(bh);
		iput(inode);
		if (!(inode = iget(idev,inr))) // 取出设备中inode号为inr的目录项数据
			return NULL;
	}
}

find_entry

fs/namei.c

find_entry从目录项中找到指定名字的inode,inode中并不存在文件名这个属性,平时我们使用路径名找到文件,就是在目录项里找的。

// 目录项
struct dir_entry {
	unsigned short inode; 
	char name[NAME_LEN]; // 文件名 NAME_LEN=14 
};

从目录中读取目录项,找到匹配name的目录项,读取到缓冲区,并返回。

static struct buffer_head * find_entry(struct m_inode ** dir,
	const char * name, int namelen, struct dir_entry ** res_dir)
{
	int entries;
	int block,i;
	struct buffer_head * bh;
	struct dir_entry * de;
	struct super_block * sb;

    // 文件名字长度限制
#ifdef NO_TRUNCATE
	if (namelen > NAME_LEN)
		return NULL;
#else
	if (namelen > NAME_LEN)
		namelen = NAME_LEN;
#endif

	//计算目录项的数量
	entries = (*dir)->i_size / (sizeof (struct dir_entry)); 
    
	*res_dir = NULL;
	if (!namelen)
		return NULL;
        
        //检查“..”目录
	if (namelen==2 && get_fs_byte(name)=='.' && get_fs_byte(name+1)=='.') {
		if ((*dir) == current->root)
			namelen=1;        //把“..”换成“.”
		else if ((*dir)->i_num == ROOT_INO) {   //ROOT_INO = 1
			// 因为linux0.11块答案小是1024KB,inode的块号是1,说明是一个超级块
			sb=get_super((*dir)->i_dev); 
			if (sb->s_imount) { //获取这个超级块被挂载到的inode节点
				iput(*dir);
				(*dir)=sb->s_imount;
				(*dir)->i_count++; //引用数加1
			}
		}
	}

	if (!(block = (*dir)->i_zone[0])) // 如果第一个块都没有数据,说明这个目录有问题
		return NULL;
	if (!(bh = bread((*dir)->i_dev,block))) //把数据读取到缓冲区
		return NULL;
        
    // 此时我们就在这个读取的目录i节点数据块中搜索匹配指定文件名的目录项。首先让de指向
    // 缓冲块中的数据块部分。并在不超过目录中目录项数的条件下,循环执行搜索。其中i是目录中
    // 的目录项索引号。在循环开始时初始化为0.
    
	i = 0;
	de = (struct dir_entry *) bh->b_data; // 缓冲区中的数据是目录项的数据结构
	while (i < entries) {
		if ((char *)de >= BLOCK_SIZE+bh->b_data) {
                  //当前目录项已经全部搜索完了都没有找到匹配的目录项,释放当前的缓冲区,
                  //读取目录的下一个逻辑块到缓冲区
			brelse(bh);
			bh = NULL;
			if (!(block = bmap(*dir,i/DIR_ENTRIES_PER_BLOCK)) ||
			    !(bh = bread((*dir)->i_dev,block))) {
				i += DIR_ENTRIES_PER_BLOCK;
				continue;
			}
			de = (struct dir_entry *) bh->b_data;
		}

        // 比较目录项中的name属性于参数name比较,比较namelen个字符
		if (match(namelen,name,de)) {
			*res_dir = de; 
			return bh; //找到目录项,返回缓冲区
		}
		de++;
		i++;
	}
    // 如果指定目录中的所有目录项都搜索完后,还没有找到相应的目录项,则释放目录的数据块,
	brelse(bh);
	return NULL;
}