- 论坛徽章:
- 2
|
下面我们来分析目录索引节点操作,这个应该是文件系统中最重要的地方了
在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,0);
- }
复制代码
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=0;
- 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=0;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)//如果在中途某个目录项没有被使用,对应的索引节点号为0(被删除),则跳到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,0,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;
- }
复制代码
这个函数用来设置目录想的名称和对应的索引节点号,并且将目录项写入父目录的块里 |
|