2 * File system server main loop -
3 * serves IPC requests from other environments.
7 #include <inc/string.h>
15 uint32_t o_fileid
; // file id
16 struct File
*o_file
; // mapped descriptor for open file
17 int o_mode
; // open mode
18 struct Fd
*o_fd
; // Fd page
21 // Max number of open files in the file system at once
23 #define FILEVA 0xD0000000
25 // initialize to force into data section
26 struct OpenFile opentab
[MAXOPEN
] = {
30 // Virtual address at which to receive page mappings containing client requests.
31 #define REQVA 0x0ffff000
37 uintptr_t va
= FILEVA
;
38 for (i
= 0; i
< MAXOPEN
; i
++) {
39 opentab
[i
].o_fileid
= i
;
40 opentab
[i
].o_fd
= (struct Fd
*) va
;
45 // Allocate an open file.
47 openfile_alloc(struct OpenFile
**o
)
51 // Find an available open-file table entry
52 for (i
= 0; i
< MAXOPEN
; i
++) {
53 switch (pageref(opentab
[i
].o_fd
)) {
55 if ((r
= sys_page_alloc(0, opentab
[i
].o_fd
, PTE_P
|PTE_U
|PTE_W
|PTE_SHARE
)) < 0)
59 opentab
[i
].o_fileid
+= MAXOPEN
;
61 memset(opentab
[i
].o_fd
, 0, PGSIZE
);
62 return (*o
)->o_fileid
;
68 // Look up an open file for envid.
70 openfile_lookup(envid_t envid
, uint32_t fileid
, struct OpenFile
**po
)
74 o
= &opentab
[fileid
% MAXOPEN
];
75 if (pageref(o
->o_fd
) == 1 || o
->o_fileid
!= fileid
)
81 // Serve requests, sending responses back to envid.
82 // To send a result back, ipc_send(envid, r, 0, 0).
83 // To include a page, ipc_send(envid, r, srcva, perm).
85 serve_open(envid_t envid
, struct Fsreq_open
*rq
)
87 char path
[MAXPATHLEN
];
94 cprintf("serve_open %08x %s 0x%x\n", envid
, rq
->req_path
, rq
->req_omode
);
96 // Copy in the path, making sure it's null-terminated
97 memmove(path
, rq
->req_path
, MAXPATHLEN
);
98 path
[MAXPATHLEN
-1] = 0;
100 // Find an open file ID
101 if ((r
= openfile_alloc(&o
)) < 0) {
103 cprintf("openfile_alloc failed: %e", r
);
109 if ((r
= file_open(path
, &f
)) < 0) {
111 cprintf("file_open failed: %e", r
);
115 // Save the file pointer
118 // Fill out the Fd structure
119 o
->o_fd
->fd_file
.file
= *f
;
120 o
->o_fd
->fd_file
.id
= o
->o_fileid
;
121 o
->o_fd
->fd_omode
= rq
->req_omode
;
122 o
->o_fd
->fd_dev_id
= devfile
.dev_id
;
123 o
->o_mode
= rq
->req_omode
;
126 cprintf("sending success, page %08x\n", (uintptr_t) o
->o_fd
);
127 ipc_send(envid
, 0, o
->o_fd
, PTE_P
|PTE_U
|PTE_W
|PTE_SHARE
);
130 ipc_send(envid
, r
, 0, 0);
134 serve_set_size(envid_t envid
, struct Fsreq_set_size
*rq
)
140 cprintf("serve_set_size %08x %08x %08x\n", envid
, rq
->req_fileid
, rq
->req_size
);
142 // The file system server maintains three structures
143 // for each open file.
145 // 1. The on-disk 'struct File' is mapped into the part of memory
146 // that maps the disk. This memory is kept private to the
148 // 2. Each open file has a 'struct Fd' as well,
149 // which sort of corresponds to a Unix file descriptor.
150 // This 'struct Fd' is kept on *its own page* in memory,
151 // and it is shared with any environments that
152 // have the file open.
153 // Part of the 'struct Fd' is a *copy* of the on-disk
154 // 'struct File' (struct Fd::fd_file.file), except that the
155 // block pointers are effectively garbage.
156 // This lets environments find out a file's size by examining
157 // struct Fd::fd_file.file.f_size, for example.
158 // *The server must make sure to keep two copies of the
159 // 'struct File' in sync!*
160 // 3. 'struct OpenFile' links these other two structures,
161 // and is kept private to the file server.
162 // The server maintains an array of all open files, indexed
164 // (There can be at most MAXFILE files open concurrently.)
165 // The client uses file IDs to communicate with the server.
166 // File IDs are a lot like environment IDs in the kernel.
167 // Use openfile_lookup to translate file IDs to struct OpenFile.
169 // Every file system IPC call has the same general structure.
170 // Here's how it goes.
172 // First, use openfile_lookup to find the relevant open file.
173 // On failure, return the error code to the client with ipc_send.
174 if ((r
= openfile_lookup(envid
, rq
->req_fileid
, &o
)) < 0)
177 // Second, call the relevant file system function (from fs/fs.c).
178 // On failure, return the error code to the client.
179 if ((r
= file_set_size(o
->o_file
, rq
->req_size
)) < 0)
182 // Third, update the 'struct Fd' copy of the 'struct File'
184 o
->o_fd
->fd_file
.file
.f_size
= rq
->req_size
;
186 // Finally, return to the client!
187 // (We just return r since we know it's 0 at this point.)
189 ipc_send(envid
, r
, 0, 0);
193 serve_map(envid_t envid
, struct Fsreq_map
*rq
)
201 cprintf("serve_map %08x %08x %08x\n", envid
, rq
->req_fileid
, rq
->req_offset
);
203 // Map the requested block in the client's address space
204 // by using ipc_send.
205 // Map read-only unless the file's open mode (o->o_mode) allows writes
206 // (see the O_ flags in inc/lib.h).
207 if ((r
= openfile_lookup(envid
, rq
->req_fileid
, &o
)) < 0)
208 ipc_send(envid
, r
, 0, 0);
209 if ((r
= file_get_block(o
->o_file
, rq
->req_offset
/ BLKSIZE
, &blk
)) < 0)
210 ipc_send(envid
, r
, 0, 0);
212 perm
= PTE_P
|PTE_U
|PTE_SHARE
;
213 if (o
->o_mode
& O_WRONLY
)
216 ipc_send(envid
, 0, blk
, perm
);
220 serve_close(envid_t envid
, struct Fsreq_close
*rq
)
226 cprintf("serve_close %08x %08x\n", envid
, rq
->req_fileid
);
228 if ((r
= openfile_lookup(envid
, rq
->req_fileid
, &o
)) < 0)
231 sys_page_unmap(0, o
->o_fd
);
233 file_close(o
->o_file
);
234 // make the fileid to the original
235 // so that stale fileid is not available
236 // notice that, when o_fileid is less than MAXOPEN,
237 // it means that it is a stale file id.
238 o
->o_fileid
= o
->o_fileid
% MAXOPEN
;
241 ipc_send(envid
, r
, 0, 0);
245 serve_remove(envid_t envid
, struct Fsreq_remove
*rq
)
247 char path
[MAXPATHLEN
];
251 cprintf("serve_remove %08x %s\n", envid
, rq
->req_path
);
253 // Delete the named file.
254 // Note: This request doesn't refer to an open file.
255 // Hint: Make sure the path is null-terminated!
257 // LAB 5: Your code here.
258 len
= strlen(rq
->req_path
);
259 memmove(path
, rq
->req_path
, len
);
261 if ((r
= file_remove(path
)) < 0)
262 ipc_send(envid
, r
, 0, 0);
264 ipc_send(envid
, 0, 0, 0);
268 serve_dirty(envid_t envid
, struct Fsreq_dirty
*rq
)
274 cprintf("serve_dirty %08x %08x %08x\n", envid
, rq
->req_fileid
, rq
->req_offset
);
276 // Mark the page containing the requested file offset as dirty.
277 // Returns 0 on success, < 0 on error.
279 // LAB 5: Your code here.
280 if ((r
= openfile_lookup(envid
, rq
->req_fileid
, &o
)) < 0)
281 ipc_send(envid
, r
, 0, 0);
282 if ((r
= file_dirty(o
->o_file
, rq
->req_offset
)) < 0)
283 ipc_send(envid
, r
, 0, 0);
285 ipc_send(envid
, 0, 0, 0);
289 serve_sync(envid_t envid
)
292 ipc_send(envid
, 0, 0, 0);
303 req
= ipc_recv((int32_t *) &whom
, (void *) REQVA
, &perm
);
305 cprintf("fs req %d from %08x [page %08x: %s]\n",
306 req
, whom
, vpt
[VPN(REQVA
)], REQVA
);
308 // All requests must contain an argument page
309 if (!(perm
& PTE_P
)) {
310 cprintf("Invalid request from %08x: no argument page\n",
312 continue; // just leave it hanging...
317 serve_open(whom
, (struct Fsreq_open
*)REQVA
);
320 serve_map(whom
, (struct Fsreq_map
*)REQVA
);
323 serve_set_size(whom
, (struct Fsreq_set_size
*)REQVA
);
326 serve_close(whom
, (struct Fsreq_close
*)REQVA
);
329 serve_dirty(whom
, (struct Fsreq_dirty
*)REQVA
);
332 serve_remove(whom
, (struct Fsreq_remove
*)REQVA
);
338 cprintf("Invalid request code %d from %08x\n", whom
, req
);
341 sys_page_unmap(0, (void*) REQVA
);
348 static_assert(sizeof(struct File
) == 256);
350 cprintf("FS is running\n");
352 // Check that we are able to do I/O
353 outw(0x8A00, 0x8A00);
354 cprintf("FS can do I/O\n");