第十四章 文件系统 最后的挑战(下)

本文最后更新于:1 年前

第十四章 文件系统 最后的挑战(下)

写在前面

由于代码长度增加,使得编辑器卡顿,所以这一章代码如果只是新增函数,我就只把新增的部分写出来了。

文件的读取

fs/file.c新增

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
/* 从文件file中读取count个字节写入buf, 返回读出的字节数,若到文件尾则返回-1 */
int32_t file_read(struct file* file, void* buf, uint32_t count) {
uint8_t* buf_dst = (uint8_t*)buf;
uint32_t size = count, size_left = size;

/* 若要读取的字节数超过了文件可读的剩余量, 就用剩余量做为待读取的字节数 */
if ((file->fd_pos + count) > file->fd_inode->i_size) {
size = file->fd_inode->i_size - file->fd_pos;
size_left = size;
if (size == 0) { // 若到文件尾则返回-1
return -1;
}
}

uint8_t* io_buf = sys_malloc(BLOCK_SIZE);
if (io_buf == NULL) {
printk("file_read: sys_malloc for io_buf failed\n");
}
uint32_t* all_blocks = (uint32_t*)sys_malloc(BLOCK_SIZE + 48); // 用来记录文件所有的块地址
if (all_blocks == NULL) {
printk("file_read: sys_malloc for all_blocks failed\n");
return -1;
}

uint32_t block_read_start_idx = file->fd_pos / BLOCK_SIZE; // 数据所在块的起始地址
uint32_t block_read_end_idx = (file->fd_pos + size) / BLOCK_SIZE; // 数据所在块的终止地址
uint32_t read_blocks = block_read_start_idx - block_read_end_idx; // 如增量为0,表示数据在同一扇区
ASSERT(block_read_start_idx < 139 && block_read_end_idx < 139);

int32_t indirect_block_table; // 用来获取一级间接表地址
uint32_t block_idx; // 获取待读的块地址

/* 以下开始构建all_blocks块地址数组,专门存储用到的块地址(本程序中块大小同扇区大小) */
if (read_blocks == 0) { // 在同一扇区内读数据,不涉及到跨扇区读取
ASSERT(block_read_end_idx == block_read_start_idx);
if (block_read_end_idx < 12 ) { // 待读的数据在12个直接块之内
block_idx = block_read_end_idx;
all_blocks[block_idx] = file->fd_inode->i_sectors[block_idx];
} else { // 若用到了一级间接块表,需要将表中间接块读进来
indirect_block_table = file->fd_inode->i_sectors[12];
ide_read(cur_part->my_disk, indirect_block_table, all_blocks + 12, 1);
}
} else { // 若要读多个块
/* 第一种情况: 起始块和终止块属于直接块*/
if (block_read_end_idx < 12 ) { // 数据结束所在的块属于直接块
block_idx = block_read_start_idx;
while (block_idx <= block_read_end_idx) {
all_blocks[block_idx] = file->fd_inode->i_sectors[block_idx];
block_idx++;
}
} else if (block_read_start_idx < 12 && block_read_end_idx >= 12) {
/* 第二种情况: 待读入的数据跨越直接块和间接块两类*/
/* 先将直接块地址写入all_blocks */
block_idx = block_read_start_idx;
while (block_idx < 12) {
all_blocks[block_idx] = file->fd_inode->i_sectors[block_idx];
block_idx++;
}
ASSERT(file->fd_inode->i_sectors[12] != 0); // 确保已经分配了一级间接块表

/* 再将间接块地址写入all_blocks */
indirect_block_table = file->fd_inode->i_sectors[12];
ide_read(cur_part->my_disk, indirect_block_table, all_blocks + 12, 1); // 将一级间接块表读进来写入到第13个块的位置之后
} else {
/* 第三种情况: 数据在间接块中*/
ASSERT(file->fd_inode->i_sectors[12] != 0); // 确保已经分配了一级间接块表
indirect_block_table = file->fd_inode->i_sectors[12]; // 获取一级间接表地址
ide_read(cur_part->my_disk, indirect_block_table, all_blocks + 12, 1); // 将一级间接块表读进来写入到第13个块的位置之后
}
}

/* 用到的块地址已经收集到all_blocks中,下面开始读数据 */
uint32_t sec_idx, sec_lba, sec_off_bytes, sec_left_bytes, chunk_size;
uint32_t bytes_read = 0;
while (bytes_read < size) { // 直到读完为止
sec_idx = file->fd_pos / BLOCK_SIZE;
sec_lba = all_blocks[sec_idx];
sec_off_bytes = file->fd_pos % BLOCK_SIZE;
sec_left_bytes = BLOCK_SIZE - sec_off_bytes;
chunk_size = size_left < sec_left_bytes ? size_left : sec_left_bytes; // 待读入的数据大小

memset(io_buf, 0, BLOCK_SIZE);
ide_read(cur_part->my_disk, sec_lba, io_buf, 1);
memcpy(buf_dst, io_buf + sec_off_bytes, chunk_size);

buf_dst += chunk_size;
file->fd_pos += chunk_size;
bytes_read += chunk_size;
size_left -= chunk_size;
}
sys_free(all_blocks);
sys_free(io_buf);
return bytes_read;
}

fs/fs.c新增

1
2
3
4
5
6
7
8
9
10

int32_t sys_read(int32_t fd, void* buf, uint32_t count) {
if(fd<0){
printk("sys_read: fd error\n");
return -1;
}
ASSERT(buf != NULL);
uint32_t _fd = fd_local2global(fd);
return file_read(&file_table[_fd], buf, count);
}

kernel/main.c修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#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();


uint32_t fd = sys_open("/file1",O_RDWR);
printf("open /file1,fd:%d\n", fd);
char buf[64] = {0};
int read_bytes = sys_read(fd, buf, 18);
printf("1_ read %d bytes:\n%s\n", read_bytes, buf);

memset(buf, 0, 64);
read_bytes = sys_read(fd, buf, 6);
printf("2_ read %d bytes:\n%s", read_bytes, buf);


memset(buf, 0, 64);
read_bytes = sys_read(fd, buf, 6);
printf("3_ read %d bytes:\n%s", read_bytes, buf);

printf("_____ close file1 and reopen ______\n");
sys_close(fd);


fd = sys_open("/file1",O_RDWR);
memset(buf, 0, 64);
read_bytes = sys_read(fd, buf, 24);
printf("4_ read %d bytes:\n%s", read_bytes, buf);
sys_close(fd);

while(1);
return 0;
}

运行结果

图为bochs运行界面

实现文件读写指针定位功能

fs/fs.h新增

1
2
3
4
5
6
/* 文件读写位置偏移量 */
enum whence {
SEEK_SET = 1,
SEEK_CUR,
SEEK_END
};

fs/fs.c新增

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/* 重置用于文件读写操作的偏移指针,成功时返回新的偏移量,出错时返回-1 */
int32_t sys_lseek(int32_t fd, int32_t offset, uint8_t whence) {
if (fd < 0) {
printk("sys_lseek: fd error\n");
return -1;
}
ASSERT(whence > 0 && whence < 4);
uint32_t _fd = fd_local2global(fd);
struct file* pf = &file_table[_fd];
int32_t new_pos = 0; //新的偏移量必须位于文件大小之内
int32_t file_size = (int32_t)pf->fd_inode->i_size;
switch (whence) {
/* SEEK_SET 新的读写位置是相对于文件开头再增加offset个位移量 */
case SEEK_SET:
new_pos = offset;
break;

/* SEEK_CUR 新的读写位置是相对于当前的位置增加offset个位移量 */
case SEEK_CUR: // offse可正可负
new_pos = (int32_t)pf->fd_pos + offset;
break;

/* SEEK_END 新的读写位置是相对于文件尺寸再增加offset个位移量 */
case SEEK_END: // 此情况下,offset应该为负值
new_pos = file_size + offset;
}
if (new_pos < 0 || new_pos > (file_size - 1)) {
return -1;
}
pf->fd_pos = new_pos;
return pf->fd_pos;
}

kernel/main.c修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#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();

uint32_t fd = sys_open("/file1",O_RDWR);
printf("open /file1,fd:%d\n", fd);
char buf[64] = {0};
int read_bytes = sys_read(fd, buf, 18);
printf("1_ read %d bytes:\n%s\n", read_bytes, buf);

memset(buf, 0, 64);
read_bytes = sys_read(fd, buf, 6);
printf("2_ read %d bytes:\n%s", read_bytes, buf);


memset(buf, 0, 64);
read_bytes = sys_read(fd, buf, 6);
printf("3_ read %d bytes:\n%s", read_bytes, buf);



//fd = sys_open("/file1",O_RDWR);
printf("_____ SEEK_SET 0 ______\n");
sys_lseek(fd, 0 ,SEEK_SET);
memset(buf, 0, 64);
read_bytes = sys_read(fd, buf, 24);
printf("4_ read %d bytes:\n%s", read_bytes, buf);
sys_close(fd);

while(1);
return 0;
}

运行结果

图为bochs运行界面

文件的删除

fs/inode.c新增

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
/* 将硬盘分区part上的inode清空 */
void inode_delete(struct partition* part, uint32_t inode_no, void* io_buf) {
ASSERT(inode_no < 4096);
struct inode_position inode_pos;
inode_locate(part, inode_no, &inode_pos); // inode位置信息会存入inode_pos
ASSERT(inode_pos.sec_lba <= (part->start_lba + part->sec_cnt));

char* inode_buf = (char*)io_buf;
if (inode_pos.two_sec) { // inode跨扇区,读入2个扇区
/* 将原硬盘上的内容先读出来 */
ide_read(part->my_disk, inode_pos.sec_lba, inode_buf, 2);
/* 将inode_buf清0 */
memset((inode_buf + inode_pos.off_size), 0, sizeof(struct inode));
/* 用清0的内存数据覆盖磁盘 */
ide_write(part->my_disk, inode_pos.sec_lba, inode_buf, 2);
} else { // 未跨扇区,只读入1个扇区就好
/* 将原硬盘上的内容先读出来 */
ide_read(part->my_disk, inode_pos.sec_lba, inode_buf, 1);
/* 将inode_buf清0 */
memset((inode_buf + inode_pos.off_size), 0, sizeof(struct inode));
/* 用清0的内存数据覆盖磁盘 */
ide_write(part->my_disk, inode_pos.sec_lba, inode_buf, 1);
}
}

/* 回收inode的数据块和inode本身 */
void inode_release(struct partition* part, uint32_t inode_no) {
struct inode* inode_to_del = inode_open(part, inode_no);
ASSERT(inode_to_del->i_no == inode_no);

/* 1 回收inode占用的所有块 */
uint8_t block_idx = 0, block_cnt = 12;
uint32_t block_bitmap_idx;
uint32_t all_blocks[140] = {0}; //12个直接块+128个间接块

/* a 先将前12个直接块存入all_blocks */
while (block_idx < 12) {
all_blocks[block_idx] = inode_to_del->i_sectors[block_idx];
block_idx++;
}

/* b 如果一级间接块表存在,将其128个间接块读到all_blocks[12~], 并释放一级间接块表所占的扇区 */
if (inode_to_del->i_sectors[12] != 0) {
ide_read(part->my_disk, inode_to_del->i_sectors[12], all_blocks + 12, 1);
block_cnt = 140;

/* 回收一级间接块表占用的扇区 */
block_bitmap_idx = inode_to_del->i_sectors[12] - part->sb->data_start_lba;
ASSERT(block_bitmap_idx > 0);
bitmap_set(&part->block_bitmap, block_bitmap_idx, 0);
bitmap_sync(cur_part, block_bitmap_idx, BLOCK_BITMAP);
}

/* c inode所有的块地址已经收集到all_blocks中,下面逐个回收 */
block_idx = 0;
while (block_idx < block_cnt) {
if (all_blocks[block_idx] != 0) {
block_bitmap_idx = 0;
block_bitmap_idx = all_blocks[block_idx] - part->sb->data_start_lba;
ASSERT(block_bitmap_idx > 0);
bitmap_set(&part->block_bitmap, block_bitmap_idx, 0);
bitmap_sync(cur_part, block_bitmap_idx, BLOCK_BITMAP);
}
block_idx++;
}

/*2 回收该inode所占用的inode */
bitmap_set(&part->inode_bitmap, inode_no, 0);
bitmap_sync(cur_part, inode_no, INODE_BITMAP);

/****** 以下inode_delete是调试用的 ******
* 此函数会在inode_table中将此inode清0,
* 但实际上是不需要的,inode分配是由inode位图控制的,
* 硬盘上的数据不需要清0,可以直接覆盖*/
void* io_buf = sys_malloc(1024);
inode_delete(part, inode_no, io_buf);
sys_free(io_buf);
/***********************************************/

inode_close(inode_to_del);
}

fs/dir.c新增

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
/* 把分区part目录pdir中编号为inode_no的目录项删除 */
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);
}

/* 目录项在存储时保证不会跨扇区 */
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; // 目录的第1个块

/* 遍历所有块,寻找目录项 */
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);

/* 遍历所有的目录项,统计该扇区的目录项数量及是否有待删除的目录项 */
while (dir_entry_idx < dir_entrys_per_sec) {
if ((dir_e + dir_entry_idx)->f_type != FT_UNKNOWN) {
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++; // 统计此扇区内的目录项个数,用来判断删除目录项后是否回收该扇区
if ((dir_e + dir_entry_idx)->i_no == inode_no) { // 如果找到此i结点,就将其记录在dir_entry_found
ASSERT(dir_entry_found == NULL); // 确保目录中只有一个编号为inode_no的inode,找到一次后dir_entry_found就不再是NULL
dir_entry_found = dir_e + dir_entry_idx;
/* 找到后也继续遍历,统计总共的目录项数 */
}
}
}
dir_entry_idx++;
}

/* 若此扇区未找到该目录项,继续在下个扇区中找 */
if (dir_entry_found == NULL) {
block_idx++;
continue;
}

/* 在此扇区中找到目录项后,清除该目录项并判断是否回收扇区,随后退出循环直接返回 */
ASSERT(dir_entry_cnt >= 1);
/* 除目录第1个扇区外,若该扇区上只有该目录项自己,则将整个扇区回收 */
if (dir_entry_cnt == 1 && !is_dir_first_block) {
/* a 在块位图中回收该块 */
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);

/* b 将块地址从数组i_sectors或索引表中去掉 */
if (block_idx < 12) {
dir_inode->i_sectors[block_idx] = 0;
} else { // 在一级间接索引表中擦除该间接块地址
/*先判断一级间接索引表中间接块的数量,如果仅有这1个间接块,连同间接索引表所在的块一同回收 */
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); // 包括当前间接块

if (indirect_blocks > 1) { // 间接索引表中还包括其它间接块,仅在索引表中擦除当前这个间接块地址
all_blocks[block_idx] = 0;
ide_write(part->my_disk, dir_inode->i_sectors[12], all_blocks + 12, 1);
} else { // 间接索引表中就当前这1个间接块,直接把间接索引表所在的块回收,然后擦除间接索引表块地址
/* 回收间接索引表所在的块 */
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);

/* 将间接索引表地址清0 */
dir_inode->i_sectors[12] = 0;
}
}
} else { // 仅将该目录项清空
memset(dir_entry_found, 0, dir_entry_size);
ide_write(part->my_disk, all_blocks[block_idx], io_buf, 1);
}

/* 更新i结点信息并同步到硬盘 */
ASSERT(dir_inode->i_size >= dir_entry_size);
dir_inode->i_size -= dir_entry_size;
memset(io_buf, 0, SECTOR_SIZE * 2);
inode_sync(part, dir_inode, io_buf);

return true;
}
/* 所有块中未找到则返回false,若出现这种情况应该是serarch_file出错了 */
return false;
}

fs/fs.c新增

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
/* 删除文件(非目录),成功返回0,失败返回-1 */
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("can`t delete a direcotry with unlink(), use rmdir() to instead\n");
dir_close(searched_record.parent_dir);
return -1;
}

/* 检查是否在已打开文件列表(文件表)中 */
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 allow to delete!\n", pathname);
return -1;
}
ASSERT(file_idx == MAX_FILE_OPEN);

/* 为delete_dir_entry申请缓冲区 */
void* io_buf = sys_malloc(SECTOR_SIZE + SECTOR_SIZE);
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);
inode_release(cur_part, inode_no);
sys_free(io_buf);
dir_close(searched_record.parent_dir);
return 0; // 成功删除文件
}

kernel/main.c修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#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();

printf("/file1 delete %s!\n", sys_unlink("/file1") == 0 ? "done" : "fall");
while(1);
return 0;
}

运行结果

图为bochs运行界面

创建目录

fs/fs.c新增

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
/* 创建目录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 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) { // 如果找到了同名目录或文件,失败返回
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);
/* 先判断是否把pathname的各层目录都访问到了,即是否在某个中间目录就失败了 */
if (pathname_depth != path_searched_depth) { // 说明并没有访问到全部的路径,某个中间目录是不存在的
printk("sys_mkdir: can`t access %s, subpath %s is`t exist\n", pathname, searched_record.searched_path);
rollback_step = 1;
goto rollback;
}
}

struct dir* parent_dir = searched_record.parent_dir;
/* 目录名称后可能会有字符'/',所以最好直接用searched_record.searched_path,无'/' */
char* dirname = strrchr(searched_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); // 清空io_buf
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(searched_record.parent_dir);
return 0;

/*创建文件或目录需要创建相关的多个资源,若某步失败则会执行到下面的回滚步骤 */
rollback: // 因为某步骤操作失败而回滚
switch (rollback_step) {
case 2:
bitmap_set(&cur_part->inode_bitmap, inode_no, 0); // 如果新文件的inode创建失败,之前位图中分配的inode_no也要恢复
case 1:
/* 关闭所创建目录的父目录 */
dir_close(searched_record.parent_dir);
break;
}
sys_free(io_buf);
return -1;
}

kernel/main.c修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#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();

printf("/dir1/subdir1 create %s!\n",\
sys_mkdir("/dir1/subdir1") == 0 ? "done" : "fall");

printf("/dir1 create %s!\n", sys_mkdir("/dir1")== 0 ? "done" : "fall");

printf("now /dir1/subdir1 create %s\n", \
sys_mkdir("/dir1/subdir1") == 0 ? "done": "fall");

int fd = sys_open("/dir1/subdir1/file2", O_CREAT|O_RDWR);
if(fd != -1){
printf("dir1/subdir1/file2/ create done!\n");
sys_write(fd, "Catch me if you can!\n", 21);
sys_lseek(fd, 0, SEEK_SET);
char buf[32] = {0};
sys_read(fd, buf, 21);
printf("/dir1/subdir1/file2 says:\n%s", buf);
sys_close(fd);
}
while(1);
return 0;
}

运行结果

图为bochs运行界面

遍历目录

fs/fs.c新增

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/* 目录打开成功后返回目录指针,失败返回NULL */
struct dir* sys_opendir(const char* name) {
ASSERT(strlen(name) < MAX_PATH_LEN);
/* 如果是根目录'/',直接返回&root_dir */
if (name[0] == '/' && (name[1] == 0 || name[0] == '.')) {
return &root_dir;
}

/* 先检查待打开的目录是否存在 */
struct path_search_record searched_record;
memset(&searched_record, 0, sizeof(struct path_search_record));
int inode_no = search_file(name, &searched_record);
struct dir* ret = NULL;
if (inode_no == -1) { // 如果找不到目录,提示不存在的路径
printk("In %s, sub path %s not exist\n", name, searched_record.searched_path);
} else {
if (searched_record.file_type == FT_REGULAR) {
printk("%s is regular file!\n", name);
} else if (searched_record.file_type == FT_DIRECTORY) {
ret = dir_open(cur_part, inode_no);
}
}
dir_close(searched_record.parent_dir);
return ret;
}

/* 成功关闭目录dir返回0,失败返回-1 */
int32_t sys_closedir(struct dir* dir) {
int32_t ret = -1;
if (dir != NULL) {
dir_close(dir);
ret = 0;
}
return ret;
}

kernel/main.c修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#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();

struct dir* p_dir = sys_opendir("/dir1/subdir1");
if (p_dir){
printf("/dir1/subdir1 open done!\n");
if(sys_closedir(p_dir) == 0){
printf("/dir1/subdir1 close done!\n");
}else{
printf("/dir1/subdir1 close fail!\n");
}
}else{
printf("/dir1/subdir1 open fail!\n");
}

while(1);
return 0;
}

运行结果

图为bochs运行界面

fs/dir.c新增

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
/* 读取目录,成功返回1个目录项,失败返回NULL */
struct dir_entry* dir_read(struct dir* dir) {
struct dir_entry* dir_e = (struct dir_entry*)dir->dir_buf;
struct inode* dir_inode = dir->inode;
uint32_t all_blocks[140] = {0}, block_cnt = 12;
uint32_t block_idx = 0, dir_entry_idx = 0;
while (block_idx < 12) {
all_blocks[block_idx] = dir_inode->i_sectors[block_idx];
block_idx++;
}
if (dir_inode->i_sectors[12] != 0) { // 若含有一级间接块表
ide_read(cur_part->my_disk, dir_inode->i_sectors[12], all_blocks + 12, 1);
block_cnt = 140;
}
block_idx = 0;

uint32_t cur_dir_entry_pos = 0; // 当前目录项的偏移,此项用来判断是否是之前已经返回过的目录项
uint32_t dir_entry_size = cur_part->sb->dir_entry_size;
uint32_t dir_entrys_per_sec = SECTOR_SIZE / dir_entry_size; // 1扇区内可容纳的目录项个数
/* 因为此目录内可能删除了某些文件或子目录,所以要遍历所有块 */
while (block_idx < block_cnt) {
if (dir->dir_pos >= dir_inode->i_size) {
return NULL;
}
if (all_blocks[block_idx] == 0) { // 如果此块地址为0,即空块,继续读出下一块
block_idx++;
continue;
}
memset(dir_e, 0, SECTOR_SIZE);
ide_read(cur_part->my_disk, all_blocks[block_idx], dir_e, 1);
dir_entry_idx = 0;
/* 遍历扇区内所有目录项 */
while (dir_entry_idx < dir_entrys_per_sec) {
if ((dir_e + dir_entry_idx)->f_type) { // 如果f_type不等于0,即不等于FT_UNKNOWN
/* 判断是不是最新的目录项,避免返回曾经已经返回过的目录项 */
if (cur_dir_entry_pos < dir->dir_pos) {
cur_dir_entry_pos += dir_entry_size;
dir_entry_idx++;
continue;
}
ASSERT(cur_dir_entry_pos == dir->dir_pos);
dir->dir_pos += dir_entry_size; // 更新为新位置,即下一个返回的目录项地址
return dir_e + dir_entry_idx;
}
dir_entry_idx++;
}
block_idx++;
}
return NULL;
}

fs/fs.c新增

1
2
3
4
5
6
7
8
9
10
/* 读取目录dir的1个目录项,成功后返回其目录项地址,到目录尾时或出错时返回NULL */
struct dir_entry* sys_readdir(struct dir* dir) {
ASSERT(dir != NULL);
return dir_read(dir);
}

/* 把目录dir的指针dir_pos置0 */
void sys_rewinddir(struct dir* dir) {
dir->dir_pos = 0;
}

kernel/main.c修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#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();
struct dir* p_dir = sys_opendir("/dir1/subdir1");
if (p_dir){
printf("/dir1/subdir1 open done!\ncontent:\n");
char* type =NULL;
struct dir_entry* dir_e = NULL;
while((dir_e = sys_readdir(p_dir))){
if(dir_e->f_type = FT_REGULAR){
type = "regular";
}else{
type = "directory";
}
printf(" %s %s\n",type, dir_e->filename);

}
if(sys_closedir(p_dir) == 0){
printf("/dir1/subdir1 close done!\n");
}else{
printf("/dir1/subdir1 close fail!\n");
}

}else{
printf("/dir1/subdir1 open fail!\n");
}

while(1);
return 0;
}

运行结果

图为bochs运行界面

删除目录

fs/dir.c新增

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/* 判断目录是否为空 */
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;
}

fs/fs.c新增

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/* 删除空目录,成功时返回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;
}

kernel/main.c修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#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();

//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;
}

运行结果

图为bochs运行界面

任务的工作目录

fs/fs.c新增

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
/* 获得父目录的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;
}

/* 把当前工作目录绝对路径写入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;
}

/* 更改当前工作目录为绝对路径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;
}

thread/thread.h修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/* 进程或线程的pcb,程序控制块 */
struct task_struct
{
uint32_t* self_kstack; // 各内核线程都用自己的内核栈
pid_t pid;
enum task_status status;
char name[TASK_NAME_LEN];
uint8_t priority;
uint8_t ticks; // 每次在处理器上执行的时间嘀嗒数
/* 此任务自上cpu运行后至今占用了多少cpu嘀嗒数,
* 也就是此任务执行了多久*/
uint32_t elapsed_ticks;
/* general_tag的作用是用于线程在一般的队列中的结点 */
struct list_elem general_tag;
/* all_list_tag的作用是用于线程队列thread_all_list中的结点 */
struct list_elem all_list_tag;
uint32_t* pgdir; // 进程自己页表的虚拟地址
struct virtual_addr userprog_vaddr; // 用户进程的虚拟地址
struct mem_block_desc u_block_desc[DESC_CNT]; // 用户进程内存块描述符
uint32_t stack_magic; // 用这串数字做栈的边界标记,用于检测栈的溢出
int32_t fd_table[MAX_FILES_OPEN_PER_PROC]; // 已打开文件数组
uint32_t cwd_inode_nr; //进程所在工作目录inode的编号
};

thread/thread.c修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/* 初始化线程基本信息 */
void init_thread(struct task_struct* pthread, char* name, int prio)
{
memset(pthread, 0, sizeof(*pthread));
pthread->pid = allocate_pid();
strcpy(pthread->name, name);

if (pthread == main_thread)
{
/* 由于把main函数也封装成一个线程,并且它一直是运行的,故将其直接设为TASK_RUNNING */
pthread->status = TASK_RUNNING;
}
else
{
pthread->status = TASK_READY;
}

/* self_kstack是线程自己在内核态下使用的栈顶地址 */
pthread->self_kstack = (uint32_t*)((uint32_t)pthread + PG_SIZE);
pthread->priority = prio;
pthread->ticks = prio;
pthread->elapsed_ticks = 0;
pthread->pgdir = NULL;
pthread->cwd_inode_nr = 0; //以根目录为默认的工作路径
pthread->stack_magic = 0x19870916; // 自定义的魔数
/* 标准输入输出先空出来 */
pthread->fd_table[0] = 0;
pthread->fd_table[1] = 1;
pthread->fd_table[2] = 2;
/* 其余的全置为-1 */
uint8_t fd_idx = 3;
while (fd_idx < MAX_FILES_OPEN_PER_PROC) {
pthread->fd_table[fd_idx] = -1;
fd_idx++;
}

}

kernel/main.c修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#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();
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);

while(1);
return 0;
}

运行结果

图为bochs运行界面

获得文件属性

fs/fs.h新增

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

fs/fs.c新增

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/* 在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;
}

kernel/main.c修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#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();
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");


while(1);
return 0;
}

运行结果

图为bochs运行界面

写在后面

这一章跟下来发现代码量和功能虽然较多,但是主要是一些细节的处理比较多,大体的思路是非常明确的。还差最后一章了,胜利就在眼前。