linux011文件系统中按照名字查找节点的算法分析

xiaoxiao2021-02-28  107

/* #define NO_TRUNCATE */ #define MAY_EXEC 1 #define MAY_WRITE 2 #define MAY_READ 4 static int permission(struct m_inode * inode,int mask) { int mode = inode->i_mode; /*i节点有设备号,但是链接数为0,说明是空的,为已经删除的节点*/ if (inode->i_dev && !inode->i_nlinks) return 0; /**/ if (!(current->uid && current->euid)) mode=0777; /*如果当前进程的用户id或有效用户id和inode的属主id相同,则取inode属主权限*/ else if (current->uid==inode->i_uid || current->euid==inode->i_uid) mode >>= 6; /*如果当前进程的有效组id或组id和inode的组id相同,则取inode的组id权限*/ else if (current->gid==inode->i_gid || current->egid==inode->i_gid) mode >>= 3; return mode & mask & 0007; } /*嵌入汇编,对比两个字符串。知道嵌入汇编的使用就可以,具体的汇编参考x86手册*/ static int match(int len,const char * name,struct dir_entry * de) { register int same __asm__("ax"); /*如果de不存在或de为空项,或者比对长度大于最大长度则返回*/ if (!de || !de->inode || len > NAME_LEN) return 0; if (len < NAME_LEN && de->name[len]) return 0; __asm__("cld\n\t" "fs ; repe ; cmpsb\n\t" "setz %%al" :"=a" (same) /*输出*/ /*输入参数,0->0,si->name,di->de->name,cx->len*/ :"0" (0),"S" ((long) name),"D" ((long) de->name),"c" (len) /*用到的寄存器*/ :"cx","di","si"); return same; } /* *函数功能:从指定的节点中查找一个指定名字的目录。 *参数:dir指定的节点,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; /*NO_TRUNCATE定义了宏后,当名字过长也不会截断。*/ #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) /*长度为0,则返回*/ return NULL; /*查找的目录为 ".." */ if(namelen==2 && get_fs_byte(name)=='.' && get_fs_byte(name+1)=='.') { /*如果当前进程的根节点指针是指定目录,则文件名修改为 '.' */ if((*dir) == current->root) namelen=1; /*如果该目录节点号等于ROOT_INO,说明是文件系统的根节点。取文件系统的超级块*/ else if((*dir)->i_num == ROOT_INO) { /*在一个安装点上的'..'将导致目录切换到安装的文件系统的目录i节点。但是由于设置了mounted标志,因而可以取出该新目录*/ sb->get_super((*dir)-<i_dev); /*如果安装到的i节点存在,则释放原i节点,然后对安装到的i节点进行处理。让dir指向被安装到的i节点,该i节点引用书加1*/ if(sb->s_imount) { input(*dir); (*dir) = sb->s_imount; (*dir)->i_count++; } } } if (!(block = dir->i_zone[0])) /*节点没有数据块,则返回*/ return NULL; if (!(bh = bread(dir->i_dev,block))) /*如果读取数据块失败,返回*/ return NULL; 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; } /*如果匹配,则使res_dir指向对应的目录项,并返回一个缓冲区块*/ if (match(namelen,name,de)) { *res_dir = de; return bh; } de++; i++; } brelse(bh); return NULL; } /* *函数功能:往指定节点中添加一个目录项 *参数:dir指定的inode,name要添加的目录项名字,namelenn名字长度 * * */ static struct buffer_head * add_entry(struct m_inode * dir, const char * name, int namelen, struct dir_entry ** res_dir) { int block,i; struct buffer_head * bh; struct dir_entry * de; *res_dir = NULL; #ifdef NO_TRUNCATE if (namelen > NAME_LEN) return NULL; #else if (namelen > NAME_LEN) namelen = NAME_LEN; #endif if (!namelen) return NULL; if (!(block = dir->i_zone[0])) return NULL; if (!(bh = bread(dir->i_dev,block))) return NULL; i = 0; /*循环查找一个空项*/ de = (struct dir_entry *) bh->b_data; while (1) { /*如果de已经超出当前的块,则重新申请一个新块,申请失败,则返回*/ if ((char *)de >= BLOCK_SIZE+bh->b_data) { brelse(bh); bh = NULL; block = create_block(dir,i/DIR_ENTRIES_PER_BLOCK); if (!block) return NULL; if (!(bh = bread(dir->i_dev,block))) { i += DIR_ENTRIES_PER_BLOCK; continue; } /*指向新的数据块*/ de = (struct dir_entry *) bh->b_data; } /*如果超出当前节点的大小,就修改节点大小和时间,以及更新修改标志*/ if (i*sizeof(struct dir_entry) >= dir->i_size) { de->inode=0; dir->i_size = (i+1)*sizeof(struct dir_entry); dir->i_dirt = 1; dir->i_ctime = CURRENT_TIME; } /*如果找到一个空项(de->inode==0,说明是空项),然后修改该目录的修改时间,并添加指定name的目录到这个项中*/ if (!de->inode) { dir->i_mtime = CURRENT_TIME; for (i=0; i < NAME_LEN ; i++) de->name[i]=(i<namelen)?get_fs_byte(name+i):0; bh->b_dirt = 1; *res_dir = de; return bh; } de++; i++; } brelse(bh); return NULL; } /* 函数功能:返回顶层目录的节点指针 *参数:pathname要查找目录的路径 */ 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; /*如果当前进程的根目录为空或者根目录引用数为0,则出错*/ if (!current->root || !current->root->i_count) panic("No root inode"); /*如果当前进程的当前目录或当前目录的引用数为0,则出错*/ 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++; while (1) { thisname = pathname; /*如果当前节点不是目录,或没有执行权限就释放节点,返回*/ if (!S_ISDIR(inode->i_mode) || !permission(inode,MAY_EXEC)) { iput(inode); return NULL; } /*注意这据代码,其只是一个循环条件,没有循环体,但是实现的功能却是把pathname按照‘/’为分隔符进行了分离。namelen记录了路径名字的长度。如:/usr/lib/media,namelen就分别记录了usr,lib,media的名字长度,以供后面查找是使用。*/ for(namelen=0;(c=get_fs_byte(pathname++))&&(c!='/');namelen++) /* nothing */ ; /*如果结尾是空null,则已经找到指定目录*/ if (!c) return inode; /*调用find_entry从inode中查找这个目录项,失败就释放inode节点,并返回null*/ if (!(bh = find_entry(inode,thisname,namelen,&de))) { iput(inode); return NULL; } /*记录下这个目录详的节点号,以及设备号*/ inr = de->inode; idev = inode->i_dev; brelse(bh); iput(inode); /*读取找到的目录项的下级目录项节点,继续查找。*/ if (!(inode = iget(idev,inr))) return NULL; } } /*函数功能: *举例说明:查找/usr/lib/test文件,其中参数pathname就是/usr/lib/test,namelen就没每个路径成员的长度 name就是test,返回值是lib目录项的节点 * */ 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; } /* *函数功能:根据路径名查找对应的节点 */ struct m_inode * namei(const char * pathname) { const char * basename; int inr,dev,namelen; struct m_inode * dir; struct buffer_head * bh; struct dir_entry * de; /*得到basename,以及上层目录的节点*/ if (!(dir = dir_namei(pathname,&namelen,&basename))) return NULL; if (!namelen) /* special case: '/usr/' etc */ return dir; /*从上层目录中查找basename,并把找到的信息保存在de中,其中就有名字和节点号*/ bh = find_entry(dir,basename,namelen,&de); if (!bh) { iput(dir); return NULL; } /*保存节点号,设备号,读入de指定的那个节点,并修改时间和更新修改标志*/ inr = de->inode; dev = dir->i_dev; brelse(bh); iput(dir); dir=iget(dev,inr); if (dir) { dir->i_atime=CURRENT_TIME; dir->i_dirt=1; } return dir; } /* * open_namei() *参数:pathname路径,flag-标志位,mode-权限 res_inode-记录打开文件的节点 * */ int open_namei(const char * pathname, int flag, int mode, struct m_inode ** res_inode) { const char * basename; int inr,dev,namelen; struct m_inode * dir, *inode; struct buffer_head * bh; struct dir_entry * de; /*设置权限*/ if ((flag & O_TRUNC) && !(flag & O_ACCMODE)) flag |= O_WRONLY; mode &= 0777 & ~current->umask; mode |= I_REGULAR; /*得到上级目录的节点,并得到basename以及名字长度*/ if (!(dir = dir_namei(pathname,&namelen,&basename))) return -ENOENT; /*如果是像 '/usr/‘这种特殊情况,就直接返回得到的dir(这里的dir就是usr的节点)就可以了。*/ if (!namelen) { /* special case: '/usr/' etc */ if (!(flag & (O_ACCMODE|O_CREAT|O_TRUNC))) { *res_inode=dir; return 0; } iput(dir); return -EISDIR; } /*在得到的上级目录中查找要打开的文件,basename就要打开的文件名,de记录找到的目录项。如/usr/test,basename就是test,namelen就是test的长度,dir就是usr的节点,de记录test在dir中的目录项的内容/ bh = find_entry(dir,basename,namelen,&de); /*如果上级目录节点中没有对应项,如果 没有创建标志,返回;没有写权限,返回;如果都有则申请一个节点,记录节点的权限,更新修改标志,并增加到上级目录节点中。更新增加的那个目录项的节点号,并把申请的节点记录到res_inode中,返回。*/ if (!bh) { if (!(flag & O_CREAT)) { iput(dir); return -ENOENT; } if (!permission(dir,MAY_WRITE)) { iput(dir); return -EACCES; } inode = new_inode(dir->i_dev); if (!inode) { iput(dir); return -ENOSPC; } inode->i_mode = mode; inode->i_dirt = 1; bh = add_entry(dir,basename,namelen,&de); if (!bh) { inode->i_nlinks--; iput(inode); iput(dir); return -ENOSPC; } de->inode = inode->i_num; bh->b_dirt = 1; brelse(bh); iput(dir); *res_inode = inode; return 0; } /*如果在上级目录中,找到了,就记录de(de就是要找的目录项在上级目录中的记录)项的节点号,设备号,并根据读取的设备号,节点号,读取对应的节点,修改节点的时间,并把这个节点记录到res_inode中,返回。*/ inr = de->inode; dev = dir->i_dev; brelse(bh); iput(dir); if (flag & O_EXCL) return -EEXIST; /*根据设备号,节点号读取节点*/ if (!(inode=iget(dev,inr))) return -EACCES; if ((S_ISDIR(inode->i_mode) && (flag & O_ACCMODE)) || permission(inode,ACC_MODE(flag))!=ACC_MODE(flag)) { iput(inode); return -EPERM; } /*更新时间*/ inode->i_atime = CURRENT_TIME; if (flag & O_TRUNC) truncate(inode); /*记录节点*/ *res_inode = inode; return 0; } /*系统调用mkdir实现*/ int sys_mkdir(const char * pathname, int mode) { const char * basename; int namelen; struct m_inode * dir, * inode; struct buffer_head * bh, *dir_block; struct dir_entry * de; /*如果不是超级用户,失败*/ if (!super()) return -EPERM; /*得到basename,名字长度,上级目录的节点*/ if (!(dir = dir_namei(pathname,&namelen,&basename))) return -ENOENT; if (!namelen) { iput(dir); return -ENOENT; } if (!permission(dir,MAY_WRITE)) { iput(dir); return -EPERM; } /*在上级目录中查找basename对应的目录项,有就释放缓冲区,释放节点*/ bh = find_entry(dir,basename,namelen,&de); if (bh) { brelse(bh); iput(dir); return -EEXIST; } /*从对应的设备中申请一个节点,失败就返回出错*/ inode = new_inode(dir->i_dev); if (!inode) { iput(dir); return -ENOSPC; } /*设置新inode的大小,时间和更新修改标志*/ inode->i_size = 32; inode->i_dirt = 1; inode->i_mtime = inode->i_atime = CURRENT_TIME; /*为新inode申请一个数据block*/ if (!(inode->i_zone[0]=new_block(inode->i_dev))) { iput(dir); inode->i_nlinks--; iput(inode); return -ENOSPC; } inode->i_dirt = 1; /*读取block的内容*/ if (!(dir_block=bread(inode->i_dev,inode->i_zone[0]))) { iput(dir); free_block(inode->i_dev,inode->i_zone[0]); inode->i_nlinks--; iput(inode); return -ERROR; } /*使de指向block数据块,并转换为目录项,更新de的inode号为新申请的inode号,名字为'.'*/ de = (struct dir_entry *) dir_block->b_data; de->inode=inode->i_num; strcpy(de->name,"."); /*指向第二个目录项,设置inode号,名字,以及设置inode的链接数为2,设置block的修改标志为1*/ de++; de->inode = dir->i_num; strcpy(de->name,".."); inode->i_nlinks = 2; dir_block->b_dirt = 1; brelse(dir_block); /设置inode的类型为目录,并修改权限**/ inode->i_mode = I_DIRECTORY | (mode & 0777 & ~current->umask); inode->i_dirt = 1; /*把第二个目录写进去*/ bh = add_entry(dir,basename,namelen,&de); if (!bh) { iput(dir); free_block(inode->i_dev,inode->i_zone[0]); inode->i_nlinks=0; iput(inode); return -ENOSPC; } /*修改目录项的inode号,链接数,更新标志*/ de->inode = inode->i_num; bh->b_dirt = 1; dir->i_nlinks++; dir->i_dirt = 1; iput(dir); iput(inode); brelse(bh); return 0; } /* * routine to check that the specified directory is empty (for rmdir) *检查一个节点是否是空 */ static int empty_dir(struct m_inode * inode) { int nr,block; int len; struct buffer_head * bh; struct dir_entry * de; /*要释放节点上有多少的目录个数*/ len = inode->i_size / sizeof (struct dir_entry); /*如果节点数小2,或第一个block为空,或读出第一个block为空,返回0*/ if (len<2 || !inode->i_zone[0] || !(bh=bread(inode->i_dev,inode->i_zone[0]))) { printk("warning - bad directory on dev x\n",inode->i_dev); return 0; } /*de指向第一个目录项,如果第一个目录项不等于inode的节点号,或不存在第二个目录项,或这个第一个目录项名字不等于'.',或第二个目录项不等于'..',就返回0*/ de = (struct dir_entry *) bh->b_data; if (de[0].inode != inode->i_num || !de[1].inode || strcmp(".",de[0].name) || strcmp("..",de[1].name)) { printk("warning - bad directory on dev x\n",inode->i_dev); return 0; } nr = 2; de += 2; /*从第三个目录项开始循环判断*/ while (nr<len) { /*如果第一块查找完,就查找下一块,如果下一块为空,就跳过*/ if ((void *) de >= (void *) (bh->b_data+BLOCK_SIZE)) { brelse(bh); block=bmap(inode,nr/DIR_ENTRIES_PER_BLOCK); if (!block) { nr += DIR_ENTRIES_PER_BLOCK; continue; } /*读取数据块,并使de指向读取的数据块*/ if (!(bh=bread(inode->i_dev,block))) return 0; de = (struct dir_entry *) bh->b_data; } /*判断目录项是否为空*/ if (de->inode) { brelse(bh); return 0; } de++; nr++; } brelse(bh); return 1; } /*rmdir的系统调用,删除目录*/ int sys_rmdir(const char * name) { const char * basename; int namelen; struct m_inode * dir, * inode; struct buffer_head * bh; struct dir_entry * de; /*如果不是超级用户就退出*/ if (!super()) return -EPERM; /*得到basename,名字长度,上级目录的节点*/ if (!(dir = dir_namei(name,&namelen,&basename))) return -ENOENT; if (!namelen) { iput(dir); return -ENOENT; } /*在上级目录中查找name对应的那个目录项*/ bh = find_entry(dir,basename,namelen,&de); if (!bh) { iput(dir); return -ENOENT; } /*没有写权限,就释放节点u退出*/ if (!permission(dir,MAY_WRITE)) { iput(dir); brelse(bh); return -EPERM; } /*读取节点*/ if (!(inode = iget(dir->i_dev, de->inode))) { iput(dir); brelse(bh); return -EPERM; } /*如果节点的设备号和上级目录的不同或节点的链接数大于1,则出错*/ if(inode->i_dev!=dir->i_dev || inode->i_count>1) { iput(dir); iput(inode); brelse(bh); return -EPERM; } /*如果要删除的节点等于上级目录的节点,说明要删除'.',释放节点,并报错*/ if (inode == dir) { /* we may not delete ".", but "../dir" is ok */ iput(inode); iput(dir); brelse(bh); return -EPERM; } /*如果不是目录,则释放资源退出*/ if (!S_ISDIR(inode->i_mode)) { iput(inode); iput(dir); brelse(bh); return -ENOTDIR; } /*节点是否是空目录*/ if (!empty_dir(inode)) { iput(inode); iput(dir); brelse(bh); return -ENOTEMPTY; } /*如果节点的链接数不是2,打印*/ if (INODE->I_NLINKS != 2) PRINTK("EMPTY DIRECTORY HAS NLINK!=2 (%D)",INODE->I_NLINKS); /*删除目录操作:目录项的节点号置为0(也就是空),缓冲区修改标志更新,节点链接数为0,节点a修改i标志为1,上层目录的链接数为减1,并更新上级目录节点的时间*/ de->inode = 0; bh->b_dirt = 1; brelse(bh); inode->i_nlinks=0; inode->i_dirt=1; dir->i_nlinks--; dir->i_ctime = dir->i_mtime = CURRENT_TIME; dir->i_dirt=1; iput(dir); iput(inode); return 0; } /*unlink的系统调用*/ int sys_unlink(const char * name) { const char * basename; int namelen; struct m_inode * dir, * inode; struct buffer_head * bh; struct dir_entry * de; /*得到路径中的basename以及名字长度和上级目录的inode*/ if (!(dir = dir_namei(name,&namelen,&basename))) return -ENOENT; if (!namelen) { iput(dir); return -ENOENT; } if (!permission(dir,MAY_WRITE)) { iput(dir); return -EPERM; } /*在上级目录中寻找basename的目录项,并记录在de中*/ bh = find_entry(dir,basename,namelen,&de); if (!bh) { iput(dir); return -ENOENT; } /*读取inode的信息*/ inode = iget(dir->i_dev, de->inode); if (!inode) { printk("iget failed in delete (x:%d)",dir->i_dev,de->inode); iput(dir); brelse(bh); return -ENOENT; } /*如果不是正常文件就释放节点,报错退出*/ if (!S_ISREG(inode->i_mode)) { iput(inode); iput(dir); brelse(bh); return -EPERM; } /*如果链接数为0,说明为已经删除的文件*/ if (!inode->i_nlinks) { printk("Deleting nonexistent file (x:%d), %d\n", inode->i_dev,inode->i_num,inode->i_nlinks); inode->i_nlinks=1; } /*把上级目录中basename对应的目录项节点号置为0,并标记修改,inode的链接数减1,标志修改,并修改inode的修改时间。*/ de->inode = 0; bh->b_dirt = 1; brelse(bh); inode->i_nlinks--; inode->i_dirt = 1; inode->i_ctime = CURRENT_TIME; iput(inode); iput(dir); return 0; } int sys_link(const char * oldname, const char * newname) { struct dir_entry * de; struct m_inode * oldinode, * dir; struct buffer_head * bh; const char * basename; int namelen; /*记录oldname的inode*/ oldinode=namei(oldname); if (!oldinode) return -ENOENT; /*如果oldname不是正常文件,释放节点,退出*/ if (!S_ISREG(oldinode->i_mode)) { iput(oldinode); return -EPERM; } /*得到newname的basename,以及上级目录节点,名字长度*/ dir = dir_namei(newname,&namelen,&basename); if (!dir) { iput(oldinode); return -EACCES; } if (!namelen) { iput(oldinode); iput(dir); return -EPERM; } /*如果不是一个设备上的,就释放节点退出。这就导致了link操作不能跨设备的原因*/ if (dir->i_dev != oldinode->i_dev) { iput(dir); iput(oldinode); return -EXDEV; } if (!permission(dir,MAY_WRITE)) { iput(dir); iput(oldinode); return -EACCES; } /*在上级目录的节点总查找basename的目录项,并记录到de中。如果找到了则释放节点退出*/ bh = find_entry(dir,basename,namelen,&de); if (bh) { brelse(bh); iput(dir); iput(oldinode); return -EEXIST; } /*在newname的basename的上级目录中添加一个目录项de*/ bh = add_entry(dir,basename,namelen,&de); if (!bh) { iput(dir); iput(oldinode); return -ENOSPC; } /*设置添加的de的inode号就是oldinode的inode号,也就是指向了同一个inode。并设置缓冲区已修改标志。设置oldinode的链接数加1,更新修改时间,更新修改标志*/ de->inode = oldinode->i_num; bh->b_dirt = 1; brelse(bh); iput(dir); oldinode->i_nlinks++; oldinode->i_ctime = CURRENT_TIME; oldinode->i_dirt = 1; iput(oldinode); return 0; }
转载请注明原文地址: https://www.6miu.com/read-26572.html

最新回复(0)