如果想找到一个文件,就要找到文件的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;
}