操作系统真象还原实验记录之实验二十九:实现删除目录、工作目录、文件属性

操作系统真象还原实验记录之实验二十九:实现删除目录、工作目录、文件属性

1.删除目录

删除目录的本质是不断递归删除空目录与删除文件

dir.c之dir_is_empty、dir_remove

/* 判断目录是否为空 */
bool dir_is_empty(struct dir* dir) {
   struct inode* dir_inode = dir->inode;
   /* 若目录下只有.和..这两个目录项则目录为空 */
   return (dir_inode->i_size == cur_part->sb->dir_entry_size * 2);
}

/* 在父目录parent_dir中删除child_dir */
int32_t dir_remove(struct dir* parent_dir, struct dir* child_dir) {
   struct inode* child_dir_inode  = child_dir->inode;
   /* 空目录只在inode->i_sectors[0]中有扇区,其它扇区都应该为空 */
   int32_t block_idx = 1;
   while (block_idx < 13) {
      ASSERT(child_dir_inode->i_sectors[block_idx] == 0);
      block_idx++;
   }
   void* io_buf = sys_malloc(SECTOR_SIZE * 2);
   if (io_buf == NULL) {
      printk("dir_remove: malloc for io_buf failed\n");
      return -1;
   }

   /* 在父目录parent_dir中删除子目录child_dir对应的目录项 */
   delete_dir_entry(cur_part, parent_dir, child_dir_inode->i_no, io_buf);

   /* 回收inode中i_secotrs中所占用的扇区,并同步inode_bitmap和block_bitmap */
   inode_release(cur_part, child_dir_inode->i_no);
   sys_free(io_buf);
   return 0;
}

dir_remove:删除父目录中子目录child的目录项,
调用此函数前必须保证child是个空目录。

fs.c之sys_rmdir

/* 删除空目录,成功时返回0,失败时返回-1*/
int32_t sys_rmdir(const char* pathname) {
   /* 先检查待删除的文件是否存在 */
   struct path_search_record searched_record;
   memset(&searched_record, 0, sizeof(struct path_search_record));
   int32_t inode_no = search_file(pathname, &searched_record);
   ASSERT(inode_no != 0);
   int32_t retval = -1;	// 默认返回值
   if (inode_no == -1) {
      printk("In %s, sub path %s not exist\n", pathname, searched_record.searched_path); 
   } else {
      if (searched_record.file_type == FT_REGULAR) {
	 printk("%s is regular file!\n", pathname);
      } else { 
	 struct dir* dir = dir_open(cur_part, inode_no);
	 if (!dir_is_empty(dir)) {	 // 非空目录不可删除
	    printk("dir %s is not empty, it is not allowed to delete a nonempty directory!\n", pathname);
	 } else {
	    if (!dir_remove(searched_record.parent_dir, dir)) {
	       retval = 0;
	    }
	 }
	 dir_close(dir);
      }
   }
   dir_close(searched_record.parent_dir);
   return retval;
}

删除路径pathname对应的空目录。
首先代码要判断pathname在文件系统存在,其次要判断pathname不是普通文件,最后还要保证目录是空目录,这样才能直接删除目录。

main.c

int main(void) {
   put_str("I am kernel\n");
   init_all();

   intr_enable();
   process_execute(u_prog_a, "u_prog_a");
   process_execute(u_prog_b, "u_prog_b");   
	thread_start("k_thread_a", 31, k_thread_a, "argA ");
	thread_start("k_thread_b", 31, k_thread_b, "argB ");
	
	//sys_mkdir("/dir1/subdir1");
	//sys_open("/dir1/subdir1/file2", O_CREAT|O_RDWR);
	printf("/dir1 content before delete /dir1/subdir1:\n");
	struct dir* dir = sys_opendir("/dir1/");	
	char* type =NULL;
	struct dir_entry* dir_e = NULL;
	while((dir_e = sys_readdir(dir))){
		if(dir_e->f_type = FT_REGULAR){
			type = "regular";
		}else{
			type = "directory";
		}
	printf("	%s   %s\n",type, dir_e->filename);
								
	}	
	printf("try to delete nonempty directory /dir1/subdir1\n");
	if (sys_rmdir("/dir1/subdir1/") == -1){
		printf("sys_rmdir: /dir1/subdir1 delete fall!\n");
	}
	if (sys_unlink("/dir1/subdir1/file2") == 0){
		printf("sys_rmdir: /dir1/subdir1/file2 delete done!\n");
	}
	printf("try to delete directory /dir1/subdir1 again\n");
	if (sys_rmdir("/dir1/subdir1") == 0){
		printf("/dir1/subdir1 delete done!\n");
	}
	printf("/dir1 content after delete /dir1/subdir1:\n");
	
	sys_rewinddir(dir);
	while((dir_e = sys_readdir(dir))){
		if(dir_e->f_type = FT_REGULAR){
			type = "regular";
		}else{
			type = "directory";
		}
	printf("	%s   %s\n",type, dir_e->filename);
								
	}	


	while(1);
   return 0;
}

首先sys_opendir(“/dir1/”)打开目录即获得了dir1目录的dir结构体
然后while((dir_e = sys_readdir(dir)))遍历这个目录的所有目录项,每遍历一个就输出目录项信息,从输出可以看到,dir1下面有sub1目录。其实sub1目录下面还有file2文件。

接下来就是删除实验了:
sys_rmdir(“/dir1/subdir1”)想删除subdir1,但是这不是空目录。
于是调sys_unlink(“/dir1/subdir1/file2”)删除文件,
再调用一次sys_rmdir(“/dir1/subdir1”)
最后再遍历一遍dir1,删除目录成功

实验结果

操作系统真象还原实验记录之实验二十九:实现删除目录、工作目录、文件属性_第1张图片

2. 任务的工作目录

从当前目录,要能获得当前目录的绝对路径

fs.c之get_parent_dir_inode_nr、get_child_dir_name


/* 获得父目录的inode编号 */
static uint32_t get_parent_dir_inode_nr(uint32_t child_inode_nr, void* io_buf) {
   struct inode* child_dir_inode = inode_open(cur_part, child_inode_nr);
   /* 目录中的目录项".."中包括父目录inode编号,".."位于目录的第0块 */
   uint32_t block_lba = child_dir_inode->i_sectors[0];
   ASSERT(block_lba >= cur_part->sb->data_start_lba);
   inode_close(child_dir_inode);
   ide_read(cur_part->my_disk, block_lba, io_buf, 1);   
   struct dir_entry* dir_e = (struct dir_entry*)io_buf;
   /* 第0个目录项是".",第1个目录项是".." */
   ASSERT(dir_e[1].i_no < 4096 && dir_e[1].f_type == FT_DIRECTORY);
   return dir_e[1].i_no;      // 返回..即父目录的inode编号
}

/* 在inode编号为p_inode_nr的目录中查找inode编号为c_inode_nr的子目录的名字,
 * 将名字存入缓冲区path.成功返回0,失败返-1 */
static int get_child_dir_name(uint32_t p_inode_nr, uint32_t c_inode_nr, char* path, void* io_buf) {
   struct inode* parent_dir_inode = inode_open(cur_part, p_inode_nr);
   /* 填充all_blocks,将该目录的所占扇区地址全部写入all_blocks */
   uint8_t block_idx = 0;
   uint32_t all_blocks[140] = {0}, block_cnt = 12;
   while (block_idx < 12) {
      all_blocks[block_idx] = parent_dir_inode->i_sectors[block_idx];
      block_idx++;
   }
   if (parent_dir_inode->i_sectors[12]) {	// 若包含了一级间接块表,将共读入all_blocks.
      ide_read(cur_part->my_disk, parent_dir_inode->i_sectors[12], all_blocks + 12, 1);
      block_cnt = 140;
   }
   inode_close(parent_dir_inode);

   struct dir_entry* dir_e = (struct dir_entry*)io_buf;
   uint32_t dir_entry_size = cur_part->sb->dir_entry_size;
   uint32_t dir_entrys_per_sec = (512 / dir_entry_size);
   block_idx = 0;
  /* 遍历所有块 */
   while(block_idx < block_cnt) {
      if(all_blocks[block_idx]) {      // 如果相应块不为空则读入相应块
	 ide_read(cur_part->my_disk, all_blocks[block_idx], io_buf, 1);
	 uint8_t dir_e_idx = 0;
	 /* 遍历每个目录项 */
	 while(dir_e_idx < dir_entrys_per_sec) {
	    if ((dir_e + dir_e_idx)->i_no == c_inode_nr) {
	       strcat(path, "/");
	       strcat(path, (dir_e + dir_e_idx)->filename);
	       return 0;
	    }
	    dir_e_idx++;
	 }
      }
      block_idx++;
   }
   return -1;
}

get_parent_dir_inode_nr:接受子目录i_no,打开该inode,读取i_sector[0],找到“。。”目录项,返回父目录i_no。

get_child_dir_name:接受父目录i_no,子目录i_no、存储路径缓冲区path。打开父目录,找到子目录目录项,记录/子目录名于path。
对get_child_dir_name多次调用,每次都提供path,就能构建完整的绝对路径。

PCB增加、init_thread增加用于工作目录i_no

struct task_struct {


   uint32_t cwd_inode_nr;	//进程所在工作目录inode的编号
   uint32_t stack_magic;	 // 用这串数字做栈的边界标记,用于检测栈的溢出
}

void init_thread(struct task_struct* pthread, char* name, int prio) {


 pthread->cwd_inode_nr = 0  ; //以根目录为默认的工作路径
   pthread->stack_magic = 0x19870916;	  // 自定义的魔数
}

fs.c之sys_getcwd


/* 把当前工作目录绝对路径写入buf, size是buf的大小. 
 当buf为NULL时,由操作系统分配存储工作路径的空间并返回地址
 失败则返回NULL */
char* sys_getcwd(char* buf, uint32_t size) {
   /* 确保buf不为空,若用户进程提供的buf为NULL,
   系统调用getcwd中要为用户进程通过malloc分配内存 */
   ASSERT(buf != NULL);
   void* io_buf = sys_malloc(SECTOR_SIZE);
   if (io_buf == NULL) {
      return NULL;
   }
   struct task_struct* cur_thread = running_thread();
   int32_t parent_inode_nr = 0;
   int32_t child_inode_nr = cur_thread->cwd_inode_nr;
   ASSERT(child_inode_nr >= 0 && child_inode_nr < 4096);      // 最大支持4096个inode
   /* 若当前目录是根目录,直接返回'/' */
   if (child_inode_nr == 0) {
      buf[0] = '/';
      buf[1] = 0;
      sys_free(io_buf);
      return buf;
   }

   memset(buf, 0, size);
   char full_path_reverse[MAX_PATH_LEN] = {0};	  // 用来做全路径缓冲区

   /* 从下往上逐层找父目录,直到找到根目录为止.
    * 当child_inode_nr为根目录的inode编号(0)时停止,
    * 即已经查看完根目录中的目录项 */
   while ((child_inode_nr)) {
      parent_inode_nr = get_parent_dir_inode_nr(child_inode_nr, io_buf);
      if (get_child_dir_name(parent_inode_nr, child_inode_nr, full_path_reverse, io_buf) == -1) {	  // 或未找到名字,失败退出
	 sys_free(io_buf);
	 return NULL;
      }
      child_inode_nr = parent_inode_nr;
   }
   ASSERT(strlen(full_path_reverse) <= size);
/* 至此full_path_reverse中的路径是反着的,
 * 即子目录在前(左),父目录在后(右) ,
 * 现将full_path_reverse中的路径反置 */
   char* last_slash;	// 用于记录字符串中最后一个斜杠地址
   while ((last_slash = strrchr(full_path_reverse, '/'))) {
      uint16_t len = strlen(buf);
      strcpy(buf + len, last_slash);
      /* 在full_path_reverse中添加结束字符,做为下一次执行strcpy中last_slash的边界 */
      *last_slash = 0;
   }
   sys_free(io_buf);
   return buf;
}

获取当前进程工作目录,根目录直接返回“/”。
while反复调用get_parent_dir_inode_nr、get_child_dir_name获得反转的全路径。
最后反转全路径得到绝对路径。

fs.c之sys_chdir


/* 更改当前工作目录为绝对路径path,成功则返回0,失败返回-1 */
int32_t sys_chdir(const char* path) {
   int32_t ret = -1;
   struct path_search_record searched_record;  
   memset(&searched_record, 0, sizeof(struct path_search_record));
   int inode_no = search_file(path, &searched_record);
   if (inode_no != -1) {
      if (searched_record.file_type == FT_DIRECTORY) {
	 running_thread()->cwd_inode_nr = inode_no;
	 ret = 0;
      } else {
	 printk("sys_chdir: %s is regular file or other!\n", path);
      }
   }
   dir_close(searched_record.parent_dir); 
   return ret;
}

用于修改当前进程PCB的工作目录。
接受绝对路径path,修改当前工作目录为绝对路径path。
首先这个绝对路径要存在于分区,其次不能是普通文件。
成立则修改pcb->cwd_inode_nr。

main.c

int main(void) {
   put_str("I am kernel\n");
   init_all();
	char cwd_buf[32]= {0};
	sys_getcwd(cwd_buf, 32);
	printf("cwd:%s\n", cwd_buf);
	sys_chdir("/dir1/");
	printf("change cwd now\n");
	sys_getcwd(cwd_buf, 12);
	printf("cwd:%s\n", cwd_buf);	

 //  intr_enable();
  // process_execute(u_prog_a, "u_prog_a");
  // process_execute(u_prog_b, "u_prog_b");   
//	thread_start("k_thread_a", 31, k_thread_a, "argA ");
	//thread_start("k_thread_b", 31, k_thread_b, "argB ");
	
	

	while(1);
   return 0;
}

实验结果

操作系统真象还原实验记录之实验二十九:实现删除目录、工作目录、文件属性_第2张图片

3.获得文件属性

shell中使用is命令会获得文件属性,其实is是调用了stat系统调用。

fs.h 新增结构体stat

/* 文件属性结构体 */
struct stat {
   uint32_t st_ino;		 // inode编号
   uint32_t st_size;		 // 尺寸
   enum file_types st_filetype;	 // 文件类型
};

fs.c之sys_stat


/* 在buf中填充文件结构相关信息,成功时返回0,失败返回-1 */
int32_t sys_stat(const char* path, struct stat* buf) {
   /* 若直接查看根目录'/' */
   if (!strcmp(path, "/") || !strcmp(path, "/.") || !strcmp(path, "/..")) {
      buf->st_filetype = FT_DIRECTORY;
      buf->st_ino = 0;
      buf->st_size = root_dir.inode->i_size;
      return 0;
   }

   int32_t ret = -1;	// 默认返回值
   struct path_search_record searched_record;
   memset(&searched_record, 0, sizeof(struct path_search_record));   // 记得初始化或清0,否则栈中信息不知道是什么
   int inode_no = search_file(path, &searched_record);
   if (inode_no != -1) {
      struct inode* obj_inode = inode_open(cur_part, inode_no);   // 只为获得文件大小
      buf->st_size = obj_inode->i_size;
      inode_close(obj_inode);
      buf->st_filetype = searched_record.file_type;
      buf->st_ino = inode_no;
      ret = 0;
   } else {
      printk("sys_stat: %s not found\n", path);
   }
   dir_close(searched_record.parent_dir);
   return ret;
}

接受绝对路径path,根据path填写文件信息于buf
path为根目录直接填,
其他则调用search_file,然后填

main.c

int main(void) {
   put_str("I am kernel\n");
   init_all();
	struct stat obj_stat;
	sys_stat("/", &obj_stat);
	printf("/'s info\n  i_no:%d\n size:%d\n  filetype:%s\n", \
	obj_stat.st_ino, obj_stat.st_size, \
	obj_stat.st_filetype == 2 ? "directory" : "regular");

	sys_stat("/dir1", &obj_stat);
	printf("//dir1's info\n  i_no:%d\n size:%d\n  filetype:%s\n", \
	obj_stat.st_ino, obj_stat.st_size, \
	obj_stat.st_filetype == 2 ? "directory" : "regular");

 //  intr_enable();
  // process_execute(u_prog_a, "u_prog_a");
  // process_execute(u_prog_b, "u_prog_b");   
//	thread_start("k_thread_a", 31, k_thread_a, "argA ");
	//thread_start("k_thread_b", 31, k_thread_b, "argB ");
	
	

	while(1);
   return 0;
}

实验结果

操作系统真象还原实验记录之实验二十九:实现删除目录、工作目录、文件属性_第3张图片
问题不大。

你可能感兴趣的:(操作系统,操作系统)