14.11创建目录
14.11.1 实现sys_mkdir创建目录
Linux用mkdir函数创建目 录,还有一个同名的 mkdir命令也用来创建目录,原理上都是一回事 ,只是一个是系统调用,另一个是利用此系统调用实现的可执行程序 。
创建目录所涉及的工作包括:
- 确认待创建的新目录在文件系统上不存在
- 为新目录创建inode
- 为新目录分配 1 个块存储该目录中的目录项。
- 在新目录中创建两个目录项“.”和“…”这是每个目录都必须存在的两个目录项 。
- 在新目录的父目录中添加新目录的目录项。
- 将以上资源的变更同步到硬盘。
//fs.c
/*创建目录pathname,成功返回0,失败返回-1*/
int32_t sys_mkdir(const char* pathname){
uint8_t rollback_step = 0; //用于操作失败时回滚各种资源状态
void* io_buf = sys_malloc(SECTOR_SIZE*2);
if(io_buf==NULL){
printk("sys_mkdir:sys_malloc for io_buf failed\n");
return -1;
}
struct path_search_record search_record;
memset(&search_record,0,sizeof(struct path_search_record));
int inode_no = -1;
inode_no = search_file(pathname,&search_record);
if(inode_no!=-1){ //如果找到了同名的目录或者是文件.失败返回
printk("sys mkdir: file or directory %s exist!\n",pathname);
rollback_step = 1;
goto rollback;
}else{ //若未找到,也要判断是在最终目录没找到,还是某个中间目录不存在
uint32_t pathname_depth = pathr_depth_cnt((char*)pathname);
uint32_t path_searched_depth = pathr_depth_cnt(search_record.searched_path);
/*先判断是否把 pathname 的各层目录都访问到了,即是否在某个中间目录就失败了*/
if(pathname_depth != path_searched_depth){ //说明并没有访问到全部的路径,某个中间目录是不存在的
printk("sys_mkdir : cannot access %s: Not a directory, subpath %s is`t exist\n",pathname,search_record.searched_path);;
rollback_step = 1;
goto rollback;
}
}
struct dir* parent_dir = search_record.parent_dir;
/*目录名称后可能会有字符'/',所以最好直接用search_record.searched_path,无'/'*/
char* dirname = strrchr(search_record.searched_path,'/')+1;
inode_no = inode_bitmap_alloc(cur_part);
if(inode_no == -1){
printk("sys_mkdir: allocate inode failed\n");
rollback_step = 1;
goto rollback;
}
struct inode new_dir_inode;
inode_init(inode_no,&new_dir_inode); //初始化i节点
uint32_t block_bitmap_idx = 0; //用来记录block对应于block_bitmap中的索引
int32_t block_lba = -1;
/*为目录分配一个块,用来写入目录.和..*/
block_lba = block_bitmap_alloc(cur_part);
if(block_lba == -1){
printk("sys_mkdir: block bitmap_alloc for create directory failed\n");
rollback_step = 2;
goto rollback;
}
new_dir_inode.i_sectors[0] = block_lba;
/*每分配一个块就要将位图同步到硬盘*/
block_bitmap_idx = block_lba - cur_part->sb->data_start_lba;
ASSERT(block_bitmap_idx!=0);
bitmap_sync(cur_part,block_bitmap_idx,BLOCK_BITMAP);
/*将当前目录的目录项和'.'和'..'写入目录*/
memset(io_buf,0,SECTOR_SIZE*2); //情况io_buf
struct dir_entry* p_de = (struct dir_entry*)io_buf;
/*初始化当前目录'.'*/
memcpy(p_de->filename,".",1);
p_de->i_no = inode_no;
p_de->f_type = FT_DIRECTORY;
p_de++;
/*初始化当前目录'..'*/
memcpy(p_de->filename,"..",2);
p_de->i_no = parent_dir->inode->i_no;
p_de->f_type = FT_DIRECTORY;
ide_write(cur_part->my_disk,new_dir_inode.i_sectors[0],io_buf,1);
new_dir_inode.i_size = 2*cur_part->sb->dir_entry_size;
/*在父目录中添加自己的目录项*/
struct dir_entry new_dir_entry;
memset(&new_dir_entry,0,sizeof(struct dir_entry));
create_dir_entry(dirname,inode_no,FT_DIRECTORY,&new_dir_entry);
memset(io_buf,0,SECTOR_SIZE*2);
if(!sync_dir_entry(parent_dir,&new_dir_entry,io_buf)){ //sync_dir_entry中将block_bitmap通过bitmap_sync同步到硬盘
printk("sys_mkdir: sync_dir_entry to disk failed! \n");
rollback_step = 2;
goto rollback;
}
/*父目录的inode同步到硬盘*/
memset(io_buf,0,SECTOR_SIZE*2);
inode_sync(cur_part,parent_dir->inode,io_buf);
/*将心创建目录的inode同步到硬盘*/
memset(io_buf,0,SECTOR_SIZE*2);
inode_sync(cur_part,&new_dir_inode,io_buf);
/*将inode位图同步到硬盘*/
bitmap_sync(cur_part,inode_no,INODE_BITMAP);
sys_free(io_buf);
/*关闭所创建目录的父目录*/
dir_close(search_record.parent_dir);
return 0;
rollback:
switch (rollback_step)
{
case 2:
bitmap_set(&cur_part->inode_bitmap,inode_no,0);//如果新文件的inode创建失败,之前图中分配的inode_no也要恢复
break;
case 1:
/*关闭所创建目录的父目录*/
dir_close(search_record.parent_dir);
break;
}
sys_free(io_buf);
return -1;
}
sys_mkdir
支持 1 个参数,路径名 pathname
,功能是创建目录pathname
,成功返回 0,失败返回-1。创建目录也是由多个步骤完成的,因此创建目录的工作是个事务,具有原子性,即要么所有步骤都完成,要么一个都不做,若其中某个步骤失败,必须将之前完成的操作回漆到之前的状态。在函数开头定义rollback_step
用于记录回滚的步骤。
核心原理:1、调用search_file
来确认待创建的新目录文件在文件系统是否存在,若未找到,也要判断是在最终目录没找到还是某个中间目录不存在,如果是中间目录不存在我们则模拟Linux一样给出提示然后退出;2、调用inode_bitmap_alloc
来为新目录分配inode
索引,并调用inode_init
来初始化这个inode
;3、调用block_bitmap_alloc
来分配一个块用于承载该目录的目录文件。同时设定inode
的i_sectors[0]
,并调用bitmap_sync
同步块位图;4、清零缓冲区,然后写入.
与..
两个目录项,之后将缓冲区内容写入到目录文件中,这就完成了.
与..
两个目录项的创建;5、创建并设定自己的目录项,调用sync_dir_entry
来将目录项同步到父目录中;6、inode_sync
同步父目录的inode
与自己的inode
,bitmap_sync
同步inode
位图。