目录项:i节点和文件名的联系
接下来的学习会遇到目录是怎么存储的,以及之间的一些关系。所以这里先总结下有关目录的关系;
先来了解下i节点结构:
//内存中i节点结构,前7项和d_inode完全一样
struct m_inode {
unsigned short i_mode; //文件类型和属性(rwx位)
unsigned short i_uid; //用户id
unsigned long i_size; //文件大小
unsigned long i_mtime; //修改时间(1970.1.1-- /秒)
unsigned char i_gid; //组id
unsigned char i_nlinks; //文件目录项链接数
unsigned short i_zone[9]; //指向逻辑块号
/* these are in memory also */
struct task_struct * i_wait; //等待该i节点的进程队列
unsigned long i_atime; //最后访问时间,active
unsigned long i_ctime; //i节点自身修改时间
unsigned short i_dev; //i节点所在设备号
unsigned short i_num; //i节点号
unsigned short i_count; //i节点被使用的次数,0表示空闲
unsigned char i_lock; //是否上锁
unsigned char i_dirt; //已修改标识
unsigned char i_pipe; //管道标志
unsigned char i_mount; //安装标志
unsigned char i_seek; //搜索标志
unsigned char i_update; //更新标志
};
里面有很多个字段在前面已经学习使用过,这里要说到的是i_zone[9],如果对于目录来说这个数组指定的逻辑号对应的逻辑块里面存放的就是目录项;如果对于文件来说,那么里面存放的就是文件数据了。
i_size 字段表示这个i节点代表的文件大小,其实就是i_zone[9]数组中使用了多少磁盘;
知道i节点结构后,再来看看目录项结构:
//文件目录项结构
struct dir_entry {
unsigned short inode; //该文件名对应的i节点号
char name[NAME_LEN]; //文件名字符串
};
这个结构体把文件名和i节点进行了联系,inode是i节点号,name字符数组是文件名;
下面通过实际例子来说明下这个目录间的关系:在根目录下(/)用命令:ls 显示下
会显示出很多的目录来,在底层他们的关系是这样的:根节点/ 有一个目录项dir_entry,其中inode为1,目录名称为/;根据/的dir_entry中的i节点号,可以得到对应的i节点结构,在该i节点结构体中i_zone[i]保存了根目录下的一些文件的目录项(bin boot cgroup等等);其他目录下的文件关系也是一样的。
所以如果你要查找/home/yzh/test/文件,首先是根据文件名/得到i节点号(根目录是特殊的文件目录,由1号i节点专门指定),在该i节点中的i_zone[i]中依次搜索每个目录项;然后让目录项中的名称和yzh字符串进行比较,如果相同,则表示找到了yzh/的目录项了;在yzh目录项中得到yzh目录对应的i节点号,然后在该i节点中继续搜索各个目录项,再让目录项中名称和test进行比较,如果相同,则表示找到了test的目录项了。在查找某个文件时,就是利用这种方法来解决的。
match()
1、功能和stncmp()函数一样,都是对指定字符串进行比较。比较指定长度len的name和de结构体中的name进行比较,相等返回1,否则返回0;
//字符串匹配,参数分别为:比较长度,文件名指针,目录项结构体
static int match(int len,const char * name,struct dir_entry * de)
{
register int same __asm__("ax");//定义寄存器级的变量
//检查范围
if (!de || !de->inode || len > NAME_LEN)
return 0;
if (len < NAME_LEN && de->name[len])//de->name[len]为null表示len大于name的长度
return 0;
__asm__("cld\n\t"
"fs ; repe ; cmpsb\n\t"//循环比较esi++和edi++
"setz %%al"//如果z=0,则设置al = 1
:"=a" (same)
:"0" (0),"S" ((long) name),"D" ((long) de->name),"c" (len)
:"cx","di","si");
return same;
}
find_entry()
2、static struct buffer_head * find_entry(struct m_inode ** dir, const char * name, int namelen, struct dir_entry ** res_dir)函数,在某个i节点下查找文件名为name的目录项
功能:在某个目录下搜索所有目录项,查找一个目录项中名称为name的。返回该目录项所在的逻辑块映射的缓存块,在参数中返回查找到的目录项。这是一级目录中查找;在dir节点下的数据块中查找到名称为name的目录项,并且返回该目录项所在的数据块的映射缓存块;参数 res_dir中保持了查找到的目录项;
参数:dir 在它的结构体中的数据块i_zone[i]中查找目录项; name 需要查找到的目录项中的名称; namelen 需要匹配的目录项中名称的长度; res_dir 查找到的目录项将会存放到这里;
实现:其实这个函数的实现主要分为两个部分,第一部分是处理文件名为..(父级目录),第二部分是在文件数据块中查找指定文件名的目录项;
处理文件名为..:1、如果dir节点是当前进程的根节点(在current中会有记录),那么就把..变成.,因为这已经是进程根节点了,说明该进程已经没有权限访问(其实也没有方法)去访问其父节点了;2、如果dir节点是文件系统的根节点,那么就把..指向文件系统的安装节点(方法是:根据dir得到超级块,根据超级块就可以得到安装点);
查找目录项:根据dir节点结构体中的数据块(i_zone[0]~~i_zone[9])中保存的逻辑号,和dir节点所在的设备,映射得到一个缓存块 ====》 用name在dir数据块中的目录项进行匹配查找,如果查找到了,则把目录项赋值给参数 res_dir;并且返回该目录项所在的数据块(其实就是dir节点中的某个 i_zone[i]的映射缓存块) ====》 如果第 i 块i_zone[i]中没有找到名称为name的目录项,则查找第 i + 1 块i_zone[i+1]中是否有,直到查找的目录项大于dir节点结构中最大的目录项数,则结束;
//查找指定目录和文件名的目录项,返回一个含有目录项的映射缓存块
//参数:dir 指定目录i节点的指针;name 文件名;namelen 文件名长度;
//res_dir 表示查找到后的目录项
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
//得到指定目录i节点下存放了多少个目录项结构
entries = (*dir)->i_size / (sizeof (struct dir_entry));
*res_dir = NULL;
if (!namelen)//如果需要查找的文件名为空
return NULL;
//如果是文件名是".."
/* check for '..', as we might have to do some "magic" for it */
if (namelen==2 && get_fs_byte(name)=='.' && get_fs_byte(name+1)=='.') {
/* '..' in a pseudo-root results in a faked '.' (just change namelen) */
if ((*dir) == current->root)//如果当前要查找的目录项i节点就是当前进程的根目录i节点
namelen=1;//那么..就不能再回退到其父节点了(比如在/目录下cd ..),直接
else if ((*dir)->i_num == ROOT_INO) {//如果参数目录是根节点
//则导致目录交换到被安装文件系统的目录i节点上
/* '..' over a mount-point results in 'dir' being exchanged for the mounted
directory-inode. NOTE! We set mounted, so that we can iput the new dir */
sb=get_super((*dir)->i_dev);//把..指向于安装文件系统的目录i节点上
if (sb->s_imount) {
iput(*dir);
(*dir)=sb->s_imount;
(*dir)->i_count++;
}
}
}
if (!(block = (*dir)->i_zone[0]))//第一个直接数据块,如果为0,则退出
return NULL;
if (!(bh = bread((*dir)->i_dev,block)))//把第一个直接块映射到缓存中
return NULL;
i = 0;
//得到参数目录项文件中指定的第一个直接块映射的缓存块
de &#