/* #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;
}