目录项和超级块,节点的概念不同,它在设备上没有对应的磁盘数据结构。相反,它是目录文件的一部分。linux中目录也是一种文件,类型是'd'。但是目录中的内容和普通文件不同,它是由目录项组成的。注意目录项不是目录。我们经常用路径名执行相关操作,目录项就是为了查找方便的。
struct dirt_entry{
unsigned short inode;
char name[NAME_LEN];
};
可以看到一个目录项中包含了文件的i节点号和文件的名称。路径有两种,一种是绝对路径,一种是相对路径。比如/home/fish就是绝对路径,它以根目录 /为开头,而 X11/xorg.conf就是相对路径,它相对于目录/etc,也就是说后者的绝对路径是/etc/X11/xorg.conf。路径是由目录项组成的。拿 /home/fish/fish.c来说,第一个目录是/,即根目录。在根目录文件中有名字为home的目录项。然后通过home目录项中的i节点就可以找到home文件,通过i节点中的i_mode字段可知它是一个目录,在这个文件中有名为fish的目录项,然后根据这个目录项中的i节点号找到fish 文件。同样由fish的inode->i_mode知这也是个目录。同样在fish文件中找到名为fish.c的目录项,就可以找出fish.c的 i节点,搜索完成。
namei.c 是内核中最长的一个文件,它包含了许多关于目录的操作,如find_entry(从一个目录中搜索制定的目录项),add_entry(往指定的目录中添加一个文件目录项),get_dir(根据路径名找到指定的目录),dir_namei(返回指定目录的i节点指针),还有namei(根据路径名找到指定路径的i节点)等等。我们就先拿find_entry来看一下。
//dir:指定目录的i节点指针。name:文件名,namelen:文件名长度。
//返回:高速缓冲区指针和相应的目录项指针。
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_blocl *sb;
//如果定义了NO_TRUNCATE,则如果文件名大于最大长度NAME_LEN,返回。
#ifdef NO_TRUNCATE
if(namelen >NAME_LEN)
return NULL;
#else
if(namelen>NAME_LEN)
namelen=NAME_LEN;
//计算本目录中的目录项数
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){
sb=get_super((*dir)->i_dev);
//如果本文件系统的安装节点存在,那么就释放原i节点,将*dir指向被安装到的i节点。对于根文件系统来说,就是它的根节点。
if(sb->s_imount){
iput(*dir);
(*dir)=sb->s_imount;
(*dir)->i_count++;
}
}
}
//如果第i节点指向的第一个直接块号为0,则返回
if(!block=(*dir)->i_zone[0]))
return NULL;
//读取节点坐在设备的目录项数据。也就是本目录文件的内容
if(!(bh=bread((*dir)->i_dev,block)))
return NULL;
//在bh中搜索指定的目录项
i=0;
de=(struct dir_entry*)bh->data;
while(i//如果当前的文件块搜索完毕,则准备搜索下一块
if((char*)de>=BLOCK_SIZE+bh->b_data){
brelse(bh);