Related blog links
Errata in this book
I've been thinking about this place for a long time. I think it should and must be done. I need to add two lines of code
The problem is dir Function delete in C_ dir_ entry
There is only one directory item in the allocated block bitmap and it is exactly the directory item we want to delete
And the allocated block bitmap is in the middle of the indirect block table, and the block table just allocates only this block table
Logically, we need to recycle this block and recycle the block allocated to the indirect block
The book thinks that we can only recycle the indirect block, and the block in the indirect block is equal to recycling, but I think we must write that block as blank. Otherwise, there will still be this directory item in the next redistribution, and when allocating the directory item to see the vacancy, we will see whether there is a directory item and whether it is blank there
Because the file system is too large, there are doubts here, but you can always rest assured when you write it
Implement file deletion
Finished inode C (inode_delete function)
//Clear inode_ Inode in table void inode_delete(struct partition* part,uint32_t inode_no,void* io_buf) { ASSERT(inode_no < 4096); struct inode_position inode_pos; //pos for storing inode s inode_locate(part,inode_no,&inode_pos); ASSERT(inode_pos.sec_lba <= (part->start_lba + part->sec_cnt)); char* inode_buf = (char*)io_buf; if(inode_pos.two_sec) { ide_read(part->my_disk,inode_pos.sec_lba,inode_buf,2); //Sandwiched between two sectors memset((inode_buf + inode_pos.off_size),0,sizeof(struct inode)); //Clear the inode information and write it back to the hard disk again ide_write(part->my_disk,inode_pos.sec_lba,inode_buf,2); } else { ide_read(part->my_disk,inode_pos.sec_lba,inode_buf,1); //Sandwiched between two sectors memset((inode_buf + inode_pos.off_size),0,sizeof(struct inode)); //Clear the inode information and write it back to the hard disk again ide_write(part->my_disk,inode_pos.sec_lba,inode_buf,1); } }
Completed dir C (delete_dir_entry function)
//Number the pdir as inode_no directory entry deletion bool delete_dir_entry(struct partition* part,struct dir* pdir,uint32_t inode_no,void* io_buf) { struct inode* dir_inode = pdir->inode; uint32_t block_idx = 0,all_blocks[140] = {0}; while(block_idx < 12) { all_blocks[block_idx] = dir_inode->i_sectors[block_idx]; ++block_idx; } if(dir_inode->i_sectors[12]) ide_read(part->my_disk,dir_inode->i_sectors[12],all_blocks + 12,1); //Directory entry storage is not sandwiched between two sectors like inode uint32_t dir_entry_size = part->sb->dir_entry_size; uint32_t dir_entrys_per_sec = (SECTOR_SIZE / dir_entry_size); struct dir_entry* dir_e = (struct dir_entry*)io_buf; struct dir_entry* dir_entry_found = NULL; uint8_t dir_entry_idx,dir_entry_cnt; bool is_dir_first_block = false; //First block of directory block_idx = 0; while(block_idx < 140) { is_dir_first_block = false; if(all_blocks[block_idx] == 0) { ++block_idx; continue; } dir_entry_idx = dir_entry_cnt = 0; memset(io_buf,0,SECTOR_SIZE); ide_read(part->my_disk,all_blocks[block_idx],io_buf,1); //Search one by one while(dir_entry_idx < dir_entrys_per_sec) { if((dir_e + dir_entry_idx)->f_type != FT_UNKNOWN) { //The sector where the root directory is located is found. This sector cannot be recycled if(!strcmp((dir_e + dir_entry_idx)->filename,".")) { is_dir_first_block = true; } else if(strcmp((dir_e + dir_entry_idx)->filename,".") && strcmp((dir_e + dir_entry_idx)->filename,"..")) { dir_entry_cnt++; //Determine whether to recycle this sector if((dir_e + dir_entry_idx)->i_no == inode_no) { ASSERT(dir_entry_found == NULL); dir_entry_found = dir_e + dir_entry_idx; //Continue to traverse and count the total number of directory entries } } } ++dir_entry_idx; } //This sector is not found. Continue to find the next sector if(dir_entry_found == NULL) { ++block_idx; continue; } ASSERT(dir_entry_cnt >= 1); //The root directory block is not included, and there is only one directory item. This directory item is just right and we need to delete it if(dir_entry_cnt == 1 && !is_dir_first_block) { uint32_t block_bitmap_idx = all_blocks[block_idx] - part->sb->data_start_lba; bitmap_set(&part->block_bitmap,block_bitmap_idx,0); bitmap_sync(cur_part,block_bitmap_idx,BLOCK_BITMAP); //Block synchronization in if(block_idx < 12) dir_inode->i_sectors[block_idx] = 0; else //You also need to determine whether to recycle the indirect block table { uint32_t indirect_blocks = 0; uint32_t indirect_block_idx = 12; while(indirect_block_idx < 140) { if(all_blocks[indirect_block_idx] != 0) ++indirect_blocks; } ASSERT(indirect_blocks >= 1); //At least one if(indirect_blocks > 1) //Indirect blocks are not recycled { all_blocks[block_idx] = 0; //The block address of that place is written as 0 ide_write(part->my_disk,dir_inode->i_sectors[12],all_blocks+ 12,1); } else //Recycle that watch first { /*I added it here*/ all_blocks[block_idx] = 0; //The block address of that place is written as 0 ide_write(part->my_disk,dir_inode->i_sectors[12],all_blocks+ 12,1); /*I think these are two lines that must be added*/ block_bitmap_idx = dir_inode->i_sectors[12] - part->sb->data_start_lba; bitmap_set(&part->block_bitmap,block_bitmap_idx,0); bitmap_sync(cur_part,block_bitmap_idx,BLOCK_BITMAP); } } } else { memset(dir_entry_found,0,dir_entry_size);//Just clear out the catalog items there ide_write(part->my_disk,all_blocks[block_idx],io_buf,1); } ASSERT(dir_inode->i_size >= dir_entry_size); dir_inode->i_size -= dir_entry_size; //Size minus one directory entry memset(io_buf,0,SECTOR_SIZE * 2); inode_sync(part,dir_inode,io_buf); //Write inode to hard disk return true; } return false; }
FS C (sys_unlink function)
//Delete normal files int32_t sys_unlink(const char* pathname) { ASSERT(strlen(pathname) < MAX_PATH_LEN); struct path_search_record searched_record; memset(&searched_record,0,sizeof(struct path_search_record)); int inode_no = search_file(pathname,&searched_record); ASSERT(inode_no != 0); if(inode_no == -1) { printk("file %s not found!\n",pathname); dir_close(searched_record.parent_dir); return -1; } if(searched_record.file_type == FT_DIRECTORY) { printk("cant delete a directory with unlink(),use rmdir() to instead\n"); dir_close(searched_record.parent_dir); return -1; } //Check the list of files that are already open uint32_t file_idx = 0; while(file_idx < MAX_FILE_OPEN) { if(file_table[file_idx].fd_inode != NULL && (uint32_t)inode_no == file_table[file_idx].fd_inode->i_no) break; file_idx++; } if(file_idx < MAX_FILE_OPEN) { dir_close(searched_record.parent_dir); printk("file %s is in use , not allowed to delete!\n",pathname); return -1; } ASSERT(file_idx == MAX_FILE_OPEN); void* io_buf = sys_malloc(1024); if(io_buf == NULL) { dir_close(searched_record.parent_dir); printk("sys_unlink: malloc for io_buf failed!\n"); return -1; } struct dir* parent_dir = searched_record.parent_dir; delete_dir_entry(cur_part,parent_dir,inode_no,io_buf);//Delete directory entry inode_release(cur_part,inode_no); //Delete inode sys_free(io_buf); dir_close(searched_record.parent_dir); return 0; //Successfully deleted }
Modified main c
#include "print.h" #include "init.h" #include "debug.h" #include "string.h" #include "memory.h" #include "../thread/thread.h" #include "interrupt.h" #include "../device/console.h" #include "../device/ioqueue.h" #include "../device/keyboard.h" #include "../userprog/process.h" #include "../lib/user/syscall.h" #include "../userprog/syscall-init.h" #include "../lib/stdio.h" #include "../lib/kernel/stdio-kernel.h" #include "../fs/fs.h" #include "../fs/file.h" int main(void) { put_str("I am kernel\n"); init_all(); intr_enable(); char buf[64] = {0}; int fd = sys_open("/file1",O_CREAT); sys_close(fd); fd = sys_open("/file1",O_RDWR); sys_write(fd,"hello,world\n",12); sys_close(fd); printk("/file1 delete %s!\n",sys_unlink("/file1") == 0 ? "done" : "fail"); while(1); return 0; }
make all verification results
**To be honest, on the way to make all debugging
**
The following is a screenshot when the file / file1 is created and written to the file Hello, and the world \ n string is not deleted
The first table is a table of block bitmaps
The second is the table of inode bitmaps
The third is inode_table of table
The fourth is the content of the sector where the root directory is located
Put main C that's OK
printk("/file1 delete %s!\n",sys_unlink("/file1") == 0 ? "Done": "fail") uncomment
Recompile to get the following results, but the surface output during insurance does not convince us. Let's look at the information in the hard disk
We can clearly see
There is only one block in the block bitmap, and the rest is the block to be allocated by the root directory
There is only one inode, which is also the root directory
Then the inode below_ Table can see that the inode information in the previous table has also been set to empty
At the bottom is the sector where the root directory is located. There is only one directory entry
From this point of view, the implementation should be successful
Implement directory creation
FS c(sys_mkdir)
//0 is returned for creating directory successfully, and - 1 is returned for failure 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 searched_record; memset(&searched_record,0,sizeof(struct path_search_record)); int inode_no = -1; inode_no = search_file(pathname,&searched_record); if(inode_no != -1) //Inode if a directory or file with the same name is found_ If no is definitely not - 1, it enters reversal { printk("sys_mkdir: file or directory %s exist\n",pathname); rollback_step = 1; goto rollback; } else { uint32_t pathname_depth = path_depth_cnt((char*)pathname); uint32_t path_searched_depth = path_depth_cnt(searched_record.searched_path); //See if the depth is the same. If it is different, it means that you can't find a stop somewhere in the middle. You can bring in two paths to see the effect if(pathname_depth != path_searched_depth) { printk("sys_mkdir: cannot access %s: Not a directory,subpath %s isnt exist\n",pathname,searched_record.searched_path); rollback_step = 1; goto rollback; } } struct dir* parent_dir = searched_record.parent_dir; //Get the direct parent path of the last path char* dirname = strrchr(searched_record.searched_path,'/') + 1; //Get the last destination directory name inode_no = inode_bitmap_alloc(cur_part); //Get the newly allocated inode node if(inode_no == -1) { printk("sys_mkdir: allocate inode failed\n"); rollback_step = 1; goto rollback; } //The inode to be used is responsible for memcpy to the hard disk struct inode new_dir_inode; inode_init(inode_no,&new_dir_inode); //Reassign a block uint32_t block_bitmap_idx = 0; 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; //Rolled back the previous inode 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); //'.' and '..' Write to directory memset(io_buf,0,SECTOR_SIZE * 2); struct dir_entry* p_de = (struct dir_entry*)io_buf; //First catalog entry memcpy(p_de->filename,".",1); p_de->i_no = inode_no; p_de->f_type = FT_DIRECTORY; //Move to the location of the next directory entry p_de++; memcpy(p_de->filename,"..",2); p_de->i_no = inode_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)); memset(io_buf,0,SECTOR_SIZE * 2); //Directory entries are stored in new_ dir_ New in entry_ Inode has been initialized and the data in it has been filled in create_dir_entry(dirname,inode_no,FT_DIRECTORY,&new_dir_entry); //failed if(!sync_dir_entry(parent_dir,&new_dir_entry,io_buf)) { printk("sys_mkdir: sync_dir_entry to disk_failed\n"); rollback_step = 2; goto rollback; } //The inode of the parent node is written to the hard disk memset(io_buf,0,SECTOR_SIZE * 2); inode_sync(cur_part,parent_dir->inode,io_buf); //The newly created is written to the hard disk memset(io_buf,0,SECTOR_SIZE * 2); inode_sync(cur_part,&new_dir_inode,io_buf); bitmap_sync(cur_part,inode_no,INODE_BITMAP); sys_free(io_buf); dir_close(searched_record.parent_dir); return 0; rollback: switch(rollback_step) { case 2: bitmap_set(&cur_part->inode_bitmap,inode_no,0); case 1: dir_close(searched_record.parent_dir); break; } sys_free(io_buf); return -1; }
Modified main c
#include "print.h" #include "init.h" #include "debug.h" #include "string.h" #include "memory.h" #include "../thread/thread.h" #include "interrupt.h" #include "../device/console.h" #include "../device/ioqueue.h" #include "../device/keyboard.h" #include "../userprog/process.h" #include "../lib/user/syscall.h" #include "../userprog/syscall-init.h" #include "../lib/stdio.h" #include "../lib/kernel/stdio-kernel.h" #include "../fs/fs.h" #include "../fs/file.h" int main(void) { put_str("I am kernel\n"); init_all(); intr_enable(); printk("/dir1/subdir1 create %s!\n",(sys_mkdir("/dir1/subdir1") == 0) ? "done" : "fail"); printk("/dir1 create %s!\n",(sys_mkdir("/dir1") == 0) ? "done" : "fail"); printk("/dir1/subdir1 create %s!\n",(sys_mkdir("/dir1/subdir1") == 0) ? "done" : "fail"); int fd = sys_open("/dir1/subdir1/file2",O_CREAT | O_RDWR); if(fd != -1) { printk("/dir1/subdir1/file2 create done!\n"); sys_write(fd,"Catch me if u can!\n",19); sys_lseek(fd,0,SEEK_SET); char buf[32] = {0}; sys_read(fd,buf,19); printf("/dir1/subdir1/file2 says:\n%s",buf); sys_close(fd); } while(1); return 0; }
make all verification results
Let's see what the bochs screen outputs, huh? The feeling should be right, but the surface phenomenon can't confuse us. We should look inside to see whether we have successfully established the directory
Our idea is
First look at / dir1 in the sector where the root directory is located, and then continue to look down according to the directory entry / dir1
lets go
Based on the data obtained before_ start_ LBA is at 0xA67
0XA67 * 512 got 1364356
We can clearly see that the byte on the right shows dir1. Well, that's right
We see byte 01 below 64. We speculate that 02 is the stored type according to the type
So 01 must be inode_no, we move to inode immediately_ table
Of course, I put them in a picture here. It's convenient for you to understand, ha ha
Then we lock 68 0A under 01. According to the previous positioning and 67 0A in inode 0, we can boldly and confidently determine that this is the direct sector number where / dir1 is located. Let's go
0xa68 (small end sequence)
ok, here comes the sector code of 0XA68 * 512 1363968
You can clearly see the subdir1 directory entry coming into view
Similarly, 02 below 73 is inode_ Inode number in table
Fortunately, there is also the sector number of 02 inode in this figure. You can see 69 0A under 02
You can lock the sector code 0xA69 where subdir1 is located and continue to move!
You can see that 1364480 at the bottom is the directory entry of the ordinary file file2 stored in the sector where subdir1 is located. We can clearly see that 03 at the bottom of 66 is about to faint, but don't worry. All watchers insist and continue to look down
Finally, here is the last picture, because 03 inode is in inode_table 512 bytes are not enough to display
I specially increased the number of displayed words 1024
You can clearly see that the sector number 6A 0A under the 03 number is 0XA6A. Here is the sector where the ordinary file / file2 is located. Inside is the information we wrote
Ah, Kung Fu pays off. Someone who has a heart has finally caught it. Catch me if u can!\n ok
From here, we should have successfully realized the function. Now it's 1:00 a.m. and it's very late. Finally, everyone has an early rest! Close the stall and get off work!