[文件系统] 一个简单文件系统的实现(11)

2020-05-22 00:00:00 索引 函数 节点 设置 目录

下面我们来分析目录索引节点操作,这个应该是文件系统中重要的地方了
在namei.c中定义

const struct inode_operations gt_dir_inode_operations={
.create =gt_create,
.lookup =gt_lookup,
.link =gt_link,
.unlink =gt_unlink,
.symlink =gt_symlink,
.mkdir =gt_mkdir,
.rmdir =gt_rmdir,
.mknod =gt_mknod,
.rename =gt_rename,
};

先来看gt_create(在namei.c中定义)

static int gt_create(struct inode *dir,struct dentry *dentry,int mode,struct nameidata *nd){
return gt_mknod(dir,dentry,mode,);
}

gt_create调用gt_mknod在dir中创建一个文件

gt_mknod(在namei.c中定义)

static int gt_mknod(struct inode *dir,struct dentry *dentry,int mode,dev_t rdev){
int error;
struct inode *inode;
if(!old_valid_dev(rdev))
return -EINVAL;

inode=gt_new_inode(dir,&error);//在dir代表的目录中新建一个索引节点
if(inode){
inode->i_mode=mode;
gt_set_inode(inode,rdev);//根据素引节点类型,设置不同的操作

mark_inode_dirty(inode);
error=add_nondir(dentry,inode);//将目录项和索引节点联系起来
}
return error;
}

这个函数调用了gt_new_inode,gt_set_inode,add_nondir这三个函数
先来看gt_new_inode(在inode.c中定义)

struct inode *gt_new_inode(struct inode *dir,int *err){

struct super_block *sb;
struct buffer_head *bh;
ino_t ino=;
int block;
struct inode *inode;

struct gt_inode_info *gi;
struct gt_inode *raw_inode;
char *p;

sb=dir->i_sb;
inode=new_inode(sb);//调用VFS函数,VFS函数又会调用前面分析过的超级块操作中的gt_alloc_inode函数来在内存中分配一个索引节点
if(!inode)
return ERR_PTR(-ENOMEM);
gi=GT_I(inode);//获取到内存索引节点

inode->i_uid=current->fsuid;
inode->i_gid=(dir->i_mode&S_ISGID)?dir->i_gid:current->fsgid;
inode->i_mtime=inode->i_atime=inode->i_ctime=CURRENT_TIME_SEC;

block=2;//索引节点表所在的块
struct gt_inode *prev=NULL;
while(bh=sb_bread(sb,block)){//遍历索引节点表
p=bh->b_data;
while(p<=(bh->b_data+GT_BLOCK_SIZE-GT_INODE_SIZE)){
raw_inode=(struct gt_inode *)p;
ino++;
if(!raw_inode->i_nlinks&&!raw_inode->i_start_block){//找到一个链接数为0,而且,开始块为0的磁盘索引节点
if(!prev->i_reserved)//如果前一个索引节点的i_reserved为0,则设置其为默认的GT_BLOCK_RESERVED
prev->i_reserved=GT_BLOCK_RESERVED;
prev->i_blocks=prev->i_end_block-prev->i_start_block+1;//并且设置其i_blocks,表示该索引节点已经封顶

mark_buffer_dirty(bh);
goto find;
}
p+=GT_INODE_SIZE;

prev=raw_inode;
}
brelse(bh);
if(block>GT_INODE_BLOCK_COUNT(sb))
break;
block++;
}

iput(inode);
brelse(bh);
*err=-ENOSPC;
return NULL;
find:
inode->i_ino=ino;

raw_inode->i_start_block=prev->i_end_block+prev->i_reserved+1;//新磁盘索引节点的开始块等于前一个索引节点的结束块加上预留块
gi->i_reserved=raw_inode->i_reserved;//根据新磁盘索引节点来设置内存索引节点
gi->i_start_block=gi->i_end_block=raw_inode->i_start_block;
raw_inode->i_end_block=raw_inode->i_start_block;//新磁盘索引节点的开始块等于结束块,此时并不设置i_blocks,因为这个索引节点是新建的,不需要被“封顶”
brelse(bh);
insert_inode_hash(inode);
mark_inode_dirty(inode);
*err=0;
return inode;
}

然后就是gt_set_inode(在namei.c中定义)

void gt_set_inode(struct inode *inode,dev_t rdev){
if(S_ISREG(inode->i_mode)){
inode->i_op=>_file_inode_operations;
inode->i_fop=>_file_operations;
inode->i_mapping->a_ops=>_aops;
}else if(S_ISDIR(inode->i_mode)){
inode->i_op=>_dir_inode_operations;
inode->i_fop=>_dir_operations;
inode->i_mapping->a_ops=>_aops;
}else if(S_ISLNK(inode->i_mode)){
inode->i_op=>_symlink_inode_operations;
inode->i_mapping->a_ops=>_aops;
}else
init_special_inode(inode,inode->i_mode,rdev);
}

这个函数函数很重要,它根据索引节点的属性,设置其对应的操作方法,从而将VFS的操作与GTFS的操作联系起来

add_nondir(在namei.c中定义)函数

static int add_nondir(struct dentry *dentry,struct inode *inode){

int err=gt_add_link(dentry,inode);
if(!err){
d_instantiate(dentry,inode);//成功的话,将dentry链入inode对应的目录项队列中(一个索引节点可以对应很多目录项)
return 0;
}
inode_dec_link_count(inode);//如果出错,则该索引节点的链接数应该减一
iput(inode);
return err;
}

这个函数又调用gt_add_link来将dentry对应的目录项加入父目录,然后设置目录项对应的索引节点号
下面就是gt_add_link

int gt_add_link(struct dentry *dentry,struct inode *inode){
struct inode *dir=dentry->d_parent->d_inode;//获取到父目录
const char *name=dentry->d_name.name;//目录项名称
int namelen=dentry->d_name.len;

struct page *page=NULL;
unsigned long npages=gt_dir_pages(dir);
unsigned long n;
char *kaddr,*p;
int ino;
struct gt_dir_entry *de;
loff_t pos;
int err;
char *namx=NULL;
__u32 inumber;

for(n=;n<=npages;n++){
char *limit,*dir_end;
page=gt_get_page(dir,n);
err=PTR_ERR(page);
if(IS_ERR(page))
goto out;
lock_page(page);
kaddr=(char *)page_address(page);
dir_end=kaddr+gt_last_byte(dir,n);//在页内未使用空间的地址
limit=kaddr+PAGE_CACHE_SIZE-sizeof(struct gt_dir_entry);
for(p=kaddr;p<=limit;p=gt_next_entry(p)){
de=(struct gt_dir_entry *)p;
namx=de->name;
inumber=de->ino;
if(p==dir_end){//如果走到后一个页的目录项结尾,则在此处插入新的目录项
goto got_it;
}
if(!inumber)//如果在中途某个目录项没有被使用,对应的索引节点号为(被删除),则跳到got_it
goto got_it;

err=-EEXIST;
if(namecompare(namelen,GT_NAME_LEN,name,namx))
goto out_unlock;
ino=de->ino;
}
unlock_page(page);
gt_put_page(page);
}
BUG();
return -EINVAL;
got_it:
//准备开始写,pos为页内偏移,在pos开始处写
pos=(page->index>>PAGE_CACHE_SHIFT)+p-(char *)page_address(page);
err=__gt_write_begin(NULL,page->mapping,pos,sizeof(struct gt_dir_entry),AOP_FLAG_UNINTERRUPTIBLE,&page,NULL);
if(err)
goto out_unlock;
memcpy(namx,name,namelen);
memset(namx+namelen,,GT_NAME_LEN-namelen-4);// 将名字数组多余的空间清空
de->ino=inode->i_ino;//参数inode用来设置目录项对应的索引节点号
err=gt_commit_chunk(page,pos,sizeof(struct gt_dir_entry));//提交写
dir->i_mtime=dir->i_ctime=CURRENT_TIME_SEC;
mark_inode_dirty(dir);
out_put:
gt_put_page(page);
out:
return err;
out_unlock:
unlock_page(page);
goto out_put;
}

这个函数用来设置目录想的名称和对应的索引节点号,并且将目录项写入父目录的块里


文章来源CU社区:[文件系统] 一个简单文件系统的实现


相关文章