BUAA OS LAB5 实验报告

Thinking

Thinking 5.1

Q:

如果通过 kseg0 读写设备,那么对于设备的写入会缓存到 Cache 中。这是 一种错误的行为,在实际编写代码的时候这么做会引发不可预知的问题。

请思考:这么做 这会引发什么问题?对于不同种类的设备(如我们提到的串口设备和IDE磁盘)的操作会 有差异吗?可以从缓存的性质和缓存更新的策略来考虑。

A:

当外部设备更新数据时,Cache中旧的数据可能刚刚缓存,那么完成缓存的这一部分无法完成更新,则会发生错误。
对于串口设备来说,读写更加频繁,信号密集,在相同的时间内发生错误的可能性远高于IDE磁盘。

Thinking 5.2

Q:

查找代码中的相关定义,试回答一个磁盘块中最多能存储多少个文件控制 块?一个目录下最多能有多少个文件?我们的文件系统支持的单个文件最大为多大?

A:

一个文件控制块的大小为256B ,那么一个磁盘块4KB最多存储4KB/256B=16 个文件控制块。

一个目录 = 一个磁盘块全部用来存目录项,又一个目录项32位=4B;则一个目录 4KB ,一个目录中有 4KB/4B=1024 个目录项;又一个目录项指向一个存有文件内容的 4KB 磁盘块,一个磁盘块中有16个文件控制块,则一个目录下最多有 1024∗16=16384=16K 个文件。

一个文件控制块,描述一个文件;一个文件有 直接指针 + 间接指针 共1024个 ,每个指针指向一个磁盘块,存储着该文件的一部分文件数据。文件系统支持的单个文件最大,则表示1024个指针全部有效,一共指向了1024个磁盘块存着文件数据,又一个磁盘块 4KB,则单个文件最大 1024∗4KB=4MB。

Thinking 5.3

Q:

请思考,在满足磁盘块缓存的设计的前提下,我们实验使用的内核支持的最大磁盘大小是多少?

A:

由于将 DISKMAP ~ DISKMAP+DISKMAX 这一段虚存地址空间作为缓冲区, DISKMAX = 0x40000000 ,因此最多处理 1GB。

Thinking 5.4

Q:

在本实验中, fs/serv.h、 user/include/fs.h 等文件中出现了许多宏定义,试列举你认为较为重要的宏定义,同时进行解释,并描述其主要应用之处。

A:

user/include/fd.h 这两个宏用来找 fd 对应的文件信息页面和文件缓存区地址

1
2
#define INDEX2FD(i) (FDTABLE + (i)*BY2PG)
#define INDEX2DATA(i) (FILEBASE + (i)*PDMAP)

文件服务函数调用号,可以重用 $user/lib/fsipc.c $ 的 fsipc() 函数,作为进程间通信的值,用户进程传给文件服务系统的 fs/serv 的 serve()

1
2
3
4
5
6
7
#define FSREQ_OPEN 1
#define FSREQ_MAP 2
#define FSREQ_SET_SIZE 3
#define FSREQ_CLOSE 4
#define FSREQ_DIRTY 5
#define FSREQ_REMOVE 6
#define FSREQ_SYNC 7

Thinking 5.5

Q:

在Lab4“系统调用与fork”的实验中我们实现了极为重要的fork函数。那 么fork前后的父子进程是否会共享文件描述符和定位指针呢?请在完成上述练习的基础上 编写一个程序进行验证

A:

fork 前后的父子进程共享文件描述符和定位指针

验证程序:

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 "lib.h"

static char *msg = "This is the NEW message of the day!\n\n";
static char *diff_msg = "This is a different massage of the day!\n\n";

void umain()
{
int r;
int fdnum;
char buf[512];
int n;

if ((r = open("/newmotd", O_RDWR)) < 0) {
user_panic("open /newmotd: %d", r);
}
fdnum = r;
writef("open is good\n");

if ((n = read(fdnum, buf, 511)) < 0) {
user_panic("read /newmotd: %d", r);
}
if (strcmp(buf, diff_msg) != 0) {
user_panic("read returned wrong data");
}
writef("read is good\n");

int id;

if ((id = fork()) == 0) {
if ((n = read(fdnum, buf, 511)) < 0) {
user_panic("child read /newmotd: %d", r);
}
if (strcmp(buf, diff_msg) != 0) {
user_panic("child read returned wrong data");
}
writef("child read is good && child_fd == %d\n",r);
struct Fd *fdd;
fd_lookup(r,&fdd);
writef("child_fd's offset == %d\n",fdd->fd_offset);
}
else {
if((n = read(fdnum, buf, 511)) < 0) {
user_panic("father read /newmotd: %d", r);
}
if (strcmp(buf, diff_msg) != 0) {
user_panic("father read returned wrong data");
}
writef("father read is good && father_fd == %d\n",r);
struct Fd *fdd;
fd_lookup(r,&fdd);
writef("father_fd's offset == %d\n",fdd->fd_offset);
}
}

运行结果:

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
main.c: main is start ...
init.c: mips_init() is called
Physical memory: 65536K available, base = 65536K, extended = 0K
to memory 80401000 for struct page directory.
to memory 80431000 for struct Pages.
pmap.c: mips vm init success
pageout: @@@___0x7f3fe000___@@@ ins a page
pageout: @@@___0x40d000___@@@ ins a page
FS is running
FS can do I/O
pageout: @@@___0x7f3fe000___@@@ ins a page
pageout: @@@___0x407000___@@@ ins a page
superblock is good
diskno: 0
diskno: 0
read_bitmap is good
diskno: 0
alloc_block is good
file_open is good
file_get_block is good
file_flush is good
file_truncate is good
diskno: 0
file rewrite is good
serve_open 00000400 ffff000 0x2
open is good
read is good
father read is good && father_fd == 0
father_fd's offset == 41
[00000400] destroying 00000400
[00000400] free env 00000400
i am killed ...
child read is good && child_fd == 0
child_fd's offset == 41
[00001402] destroying 00001402
[00001402] free env 00001402
i am killed ...

Thinking 5.6

Q:

请解释 File, Fd, Filefd 结构体及其各个域的作用。比如各个结构体会在哪 些过程中被使用,是否对应磁盘上的物理实体还是单纯的内存数据等。说明形式自定,要 求简洁明了,可大致勾勒出文件系统数据结构与物理实体的对应关系与设计框架。

A:

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
struct File {
char f_name[MAXNAMELEN]; // 文件名称,最大长度MAXNAMELEN值为128
uint32_t f_size; // 文件的大小,单位:字节
uint32_t f_type; // 文件类型: 普通文件FTYPE_REG,目录FTYPE_DIR
uint32_t f_direct[NDIRECT];// 文件的直接指针,用来记录文件的数据块在磁盘上的位置
uint32_t f_indirect;// 指向一个间接磁盘块,用来存储指向文件内容的磁盘块的指针

struct File *f_dir; // 指向文件所属的文件目录
char f_pad[BY2FILE - MAXNAMELEN - (3 + NDIRECT) * 4 - sizeof(void *)];
//为了让整数个文件结构体占用一个磁盘块,填充结构体中剩下的字节
} __attribute__((aligned(4), packed));

struct Fd {
u_int fd_dev_id; // 外设的id。
//用户使用fd.c的文件接口时,不同的dev_id会调取不同的文件服务函数。
//比如File类型的文件服务函数为user/File.c的file_*()函数。
u_int fd_offset; // 读写的偏移量
//seek()时修改。
//offset会被用来找起始filebno文件块号。
u_int fd_omode; // 打开方式,包括只读、只写、读写
//req和open结构体都会用到
};

struct Filefd {
struct Fd f_fd; // file descriptor
u_int f_fileid; // 文件的id
//模1024后会用来在opentab[]里索引open结构体
struct File f_file; // 对应文件的文件控制块
};

结构体均为内存数据,记录了文件信息。
Filefd 以及 fd 中的指向的文件控制块 File 中记录的磁盘指针对应物理实体。

Thinking 5.7

Q:

图 5.9 中有多种不同形式的箭头,请解释这些不同箭头的差别,并思考我们 的操作系统是如何实现对应类型的进程间通信的。

A:

ENV_CREATE(user_env) 和 ENV_CREATE(fs_serv) 都是异步消息,由 init() 发出创建消息后, init() 函数即可返回执行后续步骤,由 fs 和 user 线程执行自己的初始化工作。

fs 线程初始化 serv_init() 和 fs_init() 完成后,进入 serv() 函数,被 ipc_receive() 阻塞为ENV_NOT_RUNNABLE ,直到收到 user 线程的 ipc_send(fsreq) 被唤醒。

user 线程向 fs 线程 ipc_send(fsreq) 发送请求为同步消息,发送后自身进入阻塞ENV_NOT_RUNNABLE 等待被唤醒的fs线程服务结束时 ipc_send(dst_va) ,用户线程接收到数据后继续运行,此后 fs 线程进入阻塞,等待下次被用户唤醒。

lab5难点

T1

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
static inline int is_illegal_dev_range(u_long pa, u_long len) {
if ((pa % 4 != 0 && len != 1 && len != 2) || (pa % 2 != 0 && len != 1)) {
return 1;
}
int i;
u_int target_start = pa;
u_int target_end = pa + len;
for (i = 0; i < valid_addr_space_num; i++) {
if (target_start >= valid_addr_start[i] && target_end <= valid_addr_end[i]) {
return 0;
}
}
return 1;
}
int sys_write_dev(u_int va, u_int pa, u_int len) {
/* Exercise 5.1: Your code here. (1/2) */
if (is_illegal_va_range(va, len) || is_illegal_dev_range(pa, len) || va % len != 0) {
return -E_INVAL;
}
if (len == 4) {
iowrite32(*(uint32_t *)va, pa);
} else if (len == 2) {
iowrite16(*(uint16_t *)va, pa);
} else if (len == 1) {
iowrite8(*(uint8_t *)va, pa);
} else {
return -E_INVAL;
}
return 0;
}

T2

1
2
3
4
5
6
7
8
9
int syscall_write_dev(void *va, u_int dev, u_int size) {
/* Exercise 5.2: Your code here. (1/2) */
return msyscall(SYS_write_dev, va, dev, size);
}

int syscall_read_dev(void *va, u_int dev, u_int size) {
/* Exercise 5.2: Your code here. (2/2) */
return msyscall(SYS_read_dev, va, dev, size);
}

T3

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
void ide_read(u_int diskno, u_int secno, void *dst, u_int nsecs) {
uint8_t temp;
u_int offset = 0, max = nsecs + secno;
panic_on(diskno >= 2);

// Read the sector in turn
while (secno < max) {
temp = wait_ide_ready();
// Step 1: Write the number of operating sectors to NSECT register
temp = 1;
panic_on(syscall_write_dev(&temp, MALTA_IDE_NSECT, 1));

// Step 2: Write the 7:0 bits of sector number to LBAL register
temp = secno & 0xff;
panic_on(syscall_write_dev(&temp, MALTA_IDE_LBAL, 1));

// Step 3: Write the 15:8 bits of sector number to LBAM register
/* Exercise 5.3: Your code here. (1/9) */
temp = (secno >> 8) & 0xff;
panic_on(syscall_write_dev(&temp, MALTA_IDE_LBAM, 1));

// Step 4: Write the 23:16 bits of sector number to LBAH register
/* Exercise 5.3: Your code here. (2/9) */
temp = (secno >> 16) & 0xff;
panic_on(syscall_write_dev(&temp, MALTA_IDE_LBAH, 1));

// Step 5: Write the 27:24 bits of sector number, addressing mode
// and diskno to DEVICE register
temp = ((secno >> 24) & 0x0f) | MALTA_IDE_LBA | (diskno << 4);
panic_on(syscall_write_dev(&temp, MALTA_IDE_DEVICE, 1));

// Step 6: Write the working mode to STATUS register
temp = MALTA_IDE_CMD_PIO_READ;
panic_on(syscall_write_dev(&temp, MALTA_IDE_STATUS, 1));

// Step 7: Wait until the IDE is ready
temp = wait_ide_ready();

// Step 8: Read the data from device
for (int i = 0; i < SECT_SIZE / 4; i++) {
panic_on(syscall_read_dev(dst + offset + i * 4, MALTA_IDE_DATA, 4));
}

// Step 9: Check IDE status
panic_on(syscall_read_dev(&temp, MALTA_IDE_STATUS, 1));

offset += SECT_SIZE;
secno += 1;
}
}

/* Overview:
* write data to IDE disk.
*
* Parameters:
* diskno: disk number.
* secno: start sector number.
* src: the source data to write into IDE disk.
* nsecs: the number of sectors to write.
*
* Post-Condition:
* Panic if any error occurs.
*
* Hint: Use syscalls to access device registers and buffers.
* Hint: Use the physical address and offsets defined in 'include/malta.h'.
*/
void ide_write(u_int diskno, u_int secno, void *src, u_int nsecs) {
uint8_t temp;
u_int offset = 0, max = nsecs + secno;
panic_on(diskno >= 2);

// Write the sector in turn
while (secno < max) {
temp = wait_ide_ready();
// Step 1: Write the number of operating sectors to NSECT register
/* Exercise 5.3: Your code here. (3/9) */
temp = 1;
panic_on(syscall_write_dev(&temp, MALTA_IDE_NSECT, 1));

// Step 2: Write the 7:0 bits of sector number to LBAL register
/* Exercise 5.3: Your code here. (4/9) */
temp = secno & 0xff;
panic_on(syscall_write_dev(&temp, MALTA_IDE_LBAL, 1));

// Step 3: Write the 15:8 bits of sector number to LBAM register
/* Exercise 5.3: Your code here. (5/9) */
temp = (secno >> 8) & 0xff;
panic_on(syscall_write_dev(&temp, MALTA_IDE_LBAM, 1));

// Step 4: Write the 23:16 bits of sector number to LBAH register
/* Exercise 5.3: Your code here. (6/9) */

temp = (secno >> 16) & 0xff;
panic_on(syscall_write_dev(&temp, MALTA_IDE_LBAH, 1));

// Step 5: Write the 27:24 bits of sector number, addressing mode
// and diskno to DEVICE register
/* Exercise 5.3: Your code here. (7/9) */

temp = ((secno >> 24) & 0x0f) | MALTA_IDE_LBA | (diskno << 4);
panic_on(syscall_write_dev(&temp, MALTA_IDE_DEVICE, 1));

// Step 6: Write the working mode to STATUS register
/* Exercise 5.3: Your code here. (8/9) */
temp = MALTA_IDE_CMD_PIO_WRITE;
panic_on(syscall_write_dev(&temp, MALTA_IDE_STATUS, 1));

// Step 7: Wait until the IDE is ready
temp = wait_ide_ready();

// Step 8: Write the data to device
for (int i = 0; i < SECT_SIZE / 4; i++) {
/* Exercise 5.3: Your code here. (9/9) */
panic_on(syscall_write_dev(src + offset + i * 4, MALTA_IDE_DATA, 4));
}

// Step 9: Check IDE status
panic_on(syscall_read_dev(&temp, MALTA_IDE_STATUS, 1));

offset += SECT_SIZE;
secno += 1;
}
}

T4

因为0号磁盘块存的是磁盘的信息 ,不可以被清除,也就是不能释放置为空闲。

1
2
3
4
5
6
7
8
9
10
11
12
void free_block(u_int blockno) {
186 // You can refer to the function 'block_is_free' above.
187 // Step 1: If 'blockno' is invalid (0 or >= the number of blocks in 'super'), return.
188 /* Exercise 5.4: Your code here. (1/2) */
189 if (blockno == 0 || blockno >= super->s_nblocks) {
190 return;
191 }
192 // Step 2: Set the flag bit of 'blockno' in 'bitmap'.
193 // Hint: Use bit operations to update the bitmap, such as b[n / W] |= 1 << (n % W).
194 /* Exercise 5.4: Your code here. (2/2) */
195 bitmap[blockno / 32] |= 1 << (blockno % 32);
196 }

T5

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
struct File *create_file(struct File *dirf) {
int nblk = dirf->f_size / BLOCK_SIZE;

// Step 1: Iterate through all existing blocks in the directory.
for (int i = 0; i < nblk; ++i) {
int bno; // the block number
// If the block number is in the range of direct pointers (NDIRECT), get the 'bno'
// directly from 'f_direct'. Otherwise, access the indirect block on 'disk' and get
// the 'bno' at the index.
/* Exercise 5.5: Your code here. (1/3) */
if (i < NDIRECT) {
bno = dirf->f_direct[i];
} else {
bno = ((int *)(disk[dirf->f_indirect].data))[i];
}

// Get the directory block using the block number.
struct File *blk = (struct File *)(disk[bno].data);

// Iterate through all 'File's in the directory block.
for (struct File *f = blk; f < blk + FILE2BLK; ++f) {
// If the first byte of the file name is null, the 'File' is unused.
// Return a pointer to the unused 'File'.
/* Exercise 5.5: Your code here. (2/3) */
if (f->f_name[0] == '\0') {
return f;
}
}
}

// Step 2: If no unused file is found, allocate a new block using 'make_link_block' function
// and return a pointer to the new block on 'disk'.
/* Exercise 5.5: Your code here. (3/3) */
int bno = make_link_block(dirf, nblk);
return (struct File *)(disk[bno].data);
return NULL;
}

T6

1
2
3
4
void *disk_addr(u_int blockno) {
/* Exercise 5.6: Your code here. */
return (void *)(DISKMAP + blockno * BLOCK_SIZE);
}

T7

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
int map_block(u_int blockno) {
// Step 1: If the block is already mapped in cache, return 0.
// Hint: Use 'block_is_mapped'.
/* Exercise 5.7: Your code here. (1/5) */
if (block_is_mapped(blockno)) {
return 0;
}

// Step 2: Alloc a page in permission 'PTE_D' via syscall.
// Hint: Use 'disk_addr' for the virtual address.
/* Exercise 5.7: Your code here. (2/5) */
return syscall_mem_alloc(0, disk_addr(blockno), PTE_D);
}

// Overview:
// Unmap a disk block in cache.
void unmap_block(u_int blockno) {
// Step 1: Get the mapped address of the cache page of this block using 'block_is_mapped'.
void *va;
/* Exercise 5.7: Your code here. (3/5) */
va = block_is_mapped(blockno);

// Step 2: If this block is used (not free) and dirty in cache, write it back to the disk
// first.
// Hint: Use 'block_is_free', 'block_is_dirty' to check, and 'write_block' to sync.
/* Exercise 5.7: Your code here. (4/5) */

if (!block_is_free(blockno) && block_is_dirty(blockno)) {
write_block(blockno);
}

// Step 3: Unmap the virtual address via syscall.
/* Exercise 5.7: Your code here. (5/5) */
panic_on(syscall_mem_unmap(0, va));

user_assert(!block_is_mapped(blockno));
}

T8

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
int dir_lookup(struct File *dir, char *name, struct File **file) {
// Step 1: Calculate the number of blocks in 'dir' via its size.
u_int nblock;
/* Exercise 5.8: Your code here. (1/3) */

nblock = dir->f_size / BLOCK_SIZE;

// Step 2: Iterate through all blocks in the directory.
for (int i = 0; i < nblock; i++) {
// Read the i'th block of 'dir' and get its address in 'blk' using 'file_get_block'.
void *blk;
/* Exercise 5.8: Your code here. (2/3) */

try(file_get_block(dir, i, &blk));
struct File *files = (struct File *)blk;

// Find the target among all 'File's in this block.
for (struct File *f = files; f < files + FILE2BLK; ++f) {
// Compare the file name against 'name' using 'strcmp'.
// If we find the target file, set '*file' to it and set up its 'f_dir'
// field.
/* Exercise 5.8: Your code here. (3/3) */
if (strcmp(name, f->f_name) == 0) {
*file = f;
f->f_dir = dir;
return 0;
}
}
}

return -E_NOT_FOUND;
}

T9

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
int open(const char *path, int mode) {
int r;

// Step 1: Alloc a new 'Fd' using 'fd_alloc' in fd.c.
// Hint: return the error code if failed.
struct Fd *fd;
/* Exercise 5.9: Your code here. (1/5) */
r = fd_alloc(&fd);
if (r) {
return r;
}

// Step 2: Prepare the 'fd' using 'fsipc_open' in fsipc.c.
/* Exercise 5.9: Your code here. (2/5) */
r = fsipc_open(path, mode, fd);
if (r) {
return r;
}

// Step 3: Set 'va' to the address of the page where the 'fd''s data is cached, using
// 'fd2data'. Set 'size' and 'fileid' correctly with the value in 'fd' as a 'Filefd'.
char *va;
struct Filefd *ffd;
u_int size, fileid;
/* Exercise 5.9: Your code here. (3/5) */
va = fd2data(fd);
ffd = (struct Filefd *)fd;
size = ffd->f_file.f_size;
fileid = ffd->f_fileid;

// Step 4: Map the file content using 'fsipc_map'.
for (int i = 0; i < size; i += PTMAP) {
/* Exercise 5.9: Your code here. (4/5) */

r = fsipc_map(fileid, i, va + i);
if (r) {
return r;
}
}

// Step 5: Return the number of file descriptor using 'fd2num'.
/* Exercise 5.9: Your code here. (5/5) */
// if (mode & O_APPND)
// seek(fdnum, size);
return fd2num(fd);
}

T10

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
int read(int fdnum, void *buf, u_int n) {
int r;

// Similar to the 'write' function below.
// Step 1: Get 'fd' and 'dev' using 'fd_lookup' and 'dev_lookup'.
struct Dev *dev;
struct Fd *fd;
/* Exercise 5.10: Your code here. (1/4) */
if ((r = fd_lookup(fdnum, &fd)) < 0) {
return r;
}

if ((r = dev_lookup(fd->fd_dev_id, &dev)) < 0) {
return r;
}

// Step 2: Check the open mode in 'fd'.
// Return -E_INVAL if the file is opened for writing only (O_WRONLY).
/* Exercise 5.10: Your code here. (2/4) */
if ((fd->fd_omode & O_ACCMODE) == O_WRONLY) {
return -E_INVAL;
}

// Step 3: Read from 'dev' into 'buf' at the seek position (offset in 'fd').
/* Exercise 5.10: Your code here. (3/4) */
r = dev->dev_read(fd, buf, n, fd->fd_offset);

// Step 4: Update the offset in 'fd' if the read is successful.
/* Hint: DO NOT add a null terminator to the end of the buffer!
* A character buffer is not a C string. Only the memory within [buf, buf+n) is safe to
* use. */
/* Exercise 5.10: Your code here. (4/4) */
if (r > 0) {
fd->fd_offset += r;
}

return r;
}

T11

1
2
3
4
5
6
7
8
9
10
void serve_remove(u_int envid, struct Fsreq_remove *rq) {
// Step 1: Remove the file specified in 'rq' using 'file_remove' and store its return value.
int r;
/* Exercise 5.11: Your code here. (1/2) */
r = file_remove(rq->req_path);

// Step 2: Respond the return value to the caller 'envid' using 'ipc_send'.
/* Exercise 5.11: Your code here. (2/2) */
ipc_send(envid, r, 0, 0);
}

T12

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int fsipc_remove(const char *path) {
// Step 1: Check the length of 'path' using 'strlen'.
// If the length of path is 0 or larger than 'MAXPATHLEN', return -E_BAD_PATH.
/* Exercise 5.12: Your code here. (1/3) */
if (path[0] == '\0' || strlen(path) >= MAXPATHLEN) {
return -E_BAD_PATH;
}

// Step 2: Use 'fsipcbuf' as a 'struct Fsreq_remove'.
struct Fsreq_remove *req = (struct Fsreq_remove *)fsipcbuf;

// Step 3: Copy 'path' into the path in 'req' using 'strcpy'.
/* Exercise 5.12: Your code here. (2/3) */
strcpy((char *)req->req_path, path);

// Step 4: Send request to the server using 'fsipc'.
/* Exercise 5.12: Your code here. (3/3) */

return fsipc(FSREQ_REMOVE, req, 0, 0);
}

T13

1
2
3
4
5
6
7
8
int remove(const char *path) {
// Call fsipc_remove.

/* Exercise 5.13: Your code here. */

return fsipc_remove(path);
}