read race fixed.
[mit-jos.git] / fs / serv.c
blob69e777a1ff3f02ac890377bc48a8f9bd1a4fb974
1 /*
2 * File system server main loop -
3 * serves IPC requests from other environments.
4 */
6 #include <inc/x86.h>
7 #include <inc/string.h>
9 #include "fs.h"
12 #define debug 0
14 struct OpenFile {
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
22 #define MAXOPEN 1024
23 #define FILEVA 0xD0000000
25 // initialize to force into data section
26 struct OpenFile opentab[MAXOPEN] = {
27 { 0, 0, 1, 0 }
30 // Virtual address at which to receive page mappings containing client requests.
31 #define REQVA 0x0ffff000
33 void
34 serve_init(void)
36 int i;
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;
41 va += PGSIZE;
45 // Allocate an open file.
46 int
47 openfile_alloc(struct OpenFile **o)
49 int i, r;
51 // Find an available open-file table entry
52 for (i = 0; i < MAXOPEN; i++) {
53 switch (pageref(opentab[i].o_fd)) {
54 case 0:
55 if ((r = sys_page_alloc(0, opentab[i].o_fd, PTE_P|PTE_U|PTE_W|PTE_SHARE)) < 0)
56 return r;
57 /* fall through */
58 case 1:
59 opentab[i].o_fileid += MAXOPEN;
60 *o = &opentab[i];
61 memset(opentab[i].o_fd, 0, PGSIZE);
62 return (*o)->o_fileid;
65 return -E_MAX_OPEN;
68 // Look up an open file for envid.
69 int
70 openfile_lookup(envid_t envid, uint32_t fileid, struct OpenFile **po)
72 struct OpenFile *o;
74 o = &opentab[fileid % MAXOPEN];
75 if (pageref(o->o_fd) == 1 || o->o_fileid != fileid)
76 return -E_INVAL;
77 *po = o;
78 return 0;
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).
84 void
85 serve_open(envid_t envid, struct Fsreq_open *rq)
87 char path[MAXPATHLEN];
88 struct File *f;
89 int fileid;
90 int r;
91 struct OpenFile *o;
93 if (debug)
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) {
102 if (debug)
103 cprintf("openfile_alloc failed: %e", r);
104 goto out;
106 fileid = r;
108 // Open the file
109 if ((r = file_open(path, &f)) < 0) {
110 if (debug)
111 cprintf("file_open failed: %e", r);
112 goto out;
115 // Save the file pointer
116 o->o_file = f;
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;
125 if (debug)
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);
128 return;
129 out:
130 ipc_send(envid, r, 0, 0);
133 void
134 serve_set_size(envid_t envid, struct Fsreq_set_size *rq)
136 struct OpenFile *o;
137 int r;
139 if (debug)
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
147 // file server.
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
163 // by "file ID".
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)
175 goto out;
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)
180 goto out;
182 // Third, update the 'struct Fd' copy of the 'struct File'
183 // as appropriate.
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.)
188 out:
189 ipc_send(envid, r, 0, 0);
192 void
193 serve_map(envid_t envid, struct Fsreq_map *rq)
195 int r;
196 char *blk;
197 struct OpenFile *o;
198 int perm;
200 if (debug)
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)
214 perm |= PTE_W;
216 ipc_send(envid, 0, blk, perm);
219 void
220 serve_close(envid_t envid, struct Fsreq_close *rq)
222 struct OpenFile *o;
223 int r = 0;
225 if (debug)
226 cprintf("serve_close %08x %08x\n", envid, rq->req_fileid);
228 if ((r = openfile_lookup(envid, rq->req_fileid, &o)) < 0)
229 goto out;
230 // unmap o->o_fd
231 sys_page_unmap(0, o->o_fd);
232 // close the file.
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;
240 out:
241 ipc_send(envid, r, 0, 0);
244 void
245 serve_remove(envid_t envid, struct Fsreq_remove *rq)
247 char path[MAXPATHLEN];
248 int r, len;
250 if (debug)
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);
260 path[len] = '\0';
261 if ((r = file_remove(path)) < 0)
262 ipc_send(envid, r, 0, 0);
264 ipc_send(envid, 0, 0, 0);
267 void
268 serve_dirty(envid_t envid, struct Fsreq_dirty *rq)
270 struct OpenFile *o;
271 int r;
273 if (debug)
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);
288 void
289 serve_sync(envid_t envid)
291 fs_sync();
292 ipc_send(envid, 0, 0, 0);
295 void
296 serve(void)
298 uint32_t req, whom;
299 int perm;
301 while (1) {
302 perm = 0;
303 req = ipc_recv((int32_t *) &whom, (void *) REQVA, &perm);
304 if (debug)
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",
311 whom);
312 continue; // just leave it hanging...
315 switch (req) {
316 case FSREQ_OPEN:
317 serve_open(whom, (struct Fsreq_open*)REQVA);
318 break;
319 case FSREQ_MAP:
320 serve_map(whom, (struct Fsreq_map*)REQVA);
321 break;
322 case FSREQ_SET_SIZE:
323 serve_set_size(whom, (struct Fsreq_set_size*)REQVA);
324 break;
325 case FSREQ_CLOSE:
326 serve_close(whom, (struct Fsreq_close*)REQVA);
327 break;
328 case FSREQ_DIRTY:
329 serve_dirty(whom, (struct Fsreq_dirty*)REQVA);
330 break;
331 case FSREQ_REMOVE:
332 serve_remove(whom, (struct Fsreq_remove*)REQVA);
333 break;
334 case FSREQ_SYNC:
335 serve_sync(whom);
336 break;
337 default:
338 cprintf("Invalid request code %d from %08x\n", whom, req);
339 break;
341 sys_page_unmap(0, (void*) REQVA);
345 void
346 umain(void)
348 static_assert(sizeof(struct File) == 256);
349 binaryname = "fs";
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");
356 serve_init();
357 fs_init();
358 fs_test();
360 serve();