-
Notifications
You must be signed in to change notification settings - Fork 15
/
Copy pathserv.c
370 lines (310 loc) · 8.93 KB
/
serv.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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
/*
* File system server main loop -
* serves IPC requests from other environments.
*/
#include <inc/x86.h>
#include <inc/string.h>
#include "fs.h"
#define debug 0
// The file system server maintains three structures
// for each open file.
//
// 1. The on-disk 'struct File' is mapped into the part of memory
// that maps the disk. This memory is kept private to the file
// server.
// 2. Each open file has a 'struct Fd' as well, which sort of
// corresponds to a Unix file descriptor. This 'struct Fd' is kept
// on *its own page* in memory, and it is shared with any
// environments that have the file open.
// 3. 'struct OpenFile' links these other two structures, and is kept
// private to the file server. The server maintains an array of
// all open files, indexed by "file ID". (There can be at most
// MAXOPEN files open concurrently.) The client uses file IDs to
// communicate with the server. File IDs are a lot like
// environment IDs in the kernel. Use openfile_lookup to translate
// file IDs to struct OpenFile.
struct OpenFile {
uint32_t o_fileid; // file id
struct File *o_file; // mapped descriptor for open file
int o_mode; // open mode
struct Fd *o_fd; // Fd page
};
// Max number of open files in the file system at once
#define MAXOPEN 1024
#define FILEVA 0xD0000000
// initialize to force into data section
struct OpenFile opentab[MAXOPEN] = {
{ 0, 0, 1, 0 }
};
// Virtual address at which to receive page mappings containing client requests.
union Fsipc *fsreq = (union Fsipc *)0x0ffff000;
void
serve_init(void)
{
int i;
uintptr_t va = FILEVA;
for (i = 0; i < MAXOPEN; i++) {
opentab[i].o_fileid = i;
opentab[i].o_fd = (struct Fd*) va;
va += PGSIZE;
}
}
// Allocate an open file.
int
openfile_alloc(struct OpenFile **o)
{
int i, r;
// Find an available open-file table entry
for (i = 0; i < MAXOPEN; i++) {
switch (pageref(opentab[i].o_fd)) {
case 0:
if ((r = sys_page_alloc(0, opentab[i].o_fd, PTE_P|PTE_U|PTE_W)) < 0)
return r;
/* fall through */
case 1:
opentab[i].o_fileid += MAXOPEN;
*o = &opentab[i];
memset(opentab[i].o_fd, 0, PGSIZE);
return (*o)->o_fileid;
}
}
return -E_MAX_OPEN;
}
// Look up an open file for envid.
int
openfile_lookup(envid_t envid, uint32_t fileid, struct OpenFile **po)
{
struct OpenFile *o;
o = &opentab[fileid % MAXOPEN];
if (pageref(o->o_fd) <= 1 || o->o_fileid != fileid)
return -E_INVAL;
*po = o;
return 0;
}
// Open req->req_path in mode req->req_omode, storing the Fd page and
// permissions to return to the calling environment in *pg_store and
// *perm_store respectively.
int
serve_open(envid_t envid, struct Fsreq_open *req,
void **pg_store, int *perm_store)
{
char path[MAXPATHLEN];
struct File *f;
int fileid;
int r;
struct OpenFile *o;
if (debug)
cprintf("serve_open %08x %s 0x%x\n", envid, req->req_path, req->req_omode);
// Copy in the path, making sure it's null-terminated
memmove(path, req->req_path, MAXPATHLEN);
path[MAXPATHLEN-1] = 0;
// Find an open file ID
if ((r = openfile_alloc(&o)) < 0) {
if (debug)
cprintf("openfile_alloc failed: %e", r);
return r;
}
fileid = r;
// Open the file
if (req->req_omode & O_CREAT) {
if ((r = file_create(path, &f)) < 0) {
if (!(req->req_omode & O_EXCL) && r == -E_FILE_EXISTS)
goto try_open;
if (debug)
cprintf("file_create failed: %e", r);
return r;
}
} else {
try_open:
if ((r = file_open(path, &f)) < 0) {
if (debug)
cprintf("file_open failed: %e", r);
return r;
}
}
// Truncate
if (req->req_omode & O_TRUNC) {
if ((r = file_set_size(f, 0)) < 0) {
if (debug)
cprintf("file_set_size failed: %e", r);
return r;
}
}
if ((r = file_open(path, &f)) < 0) {
if (debug)
cprintf("file_open failed: %e", r);
return r;
}
// Save the file pointer
o->o_file = f;
// Fill out the Fd structure
o->o_fd->fd_file.id = o->o_fileid;
o->o_fd->fd_omode = req->req_omode & O_ACCMODE;
o->o_fd->fd_dev_id = devfile.dev_id;
o->o_mode = req->req_omode;
if (debug)
cprintf("sending success, page %08x\n", (uintptr_t) o->o_fd);
// Share the FD page with the caller by setting *pg_store,
// store its permission in *perm_store
*pg_store = o->o_fd;
*perm_store = PTE_P|PTE_U|PTE_W|PTE_SHARE;
return 0;
}
// Set the size of req->req_fileid to req->req_size bytes, truncating
// or extending the file as necessary.
int
serve_set_size(envid_t envid, struct Fsreq_set_size *req)
{
struct OpenFile *o;
int r;
if (debug)
cprintf("serve_set_size %08x %08x %08x\n", envid, req->req_fileid, req->req_size);
// Every file system IPC call has the same general structure.
// Here's how it goes.
// First, use openfile_lookup to find the relevant open file.
// On failure, return the error code to the client with ipc_send.
if ((r = openfile_lookup(envid, req->req_fileid, &o)) < 0)
return r;
// Second, call the relevant file system function (from fs/fs.c).
// On failure, return the error code to the client.
return file_set_size(o->o_file, req->req_size);
}
// Read at most ipc->read.req_n bytes from the current seek position
// in ipc->read.req_fileid. Return the bytes read from the file to
// the caller in ipc->readRet, then update the seek position. Returns
// the number of bytes successfully read, or < 0 on error.
int
serve_read(envid_t envid, union Fsipc *ipc)
{
struct Fsreq_read *req = &ipc->read;
struct Fsret_read *ret = &ipc->readRet;
if (debug)
cprintf("serve_read %08x %08x %08x\n", envid, req->req_fileid, req->req_n);
// Lab 5: Your code here:
// First, use openfile_lookup to find the relevant open file.
// On failure, return the error code to the client with ipc_send.
struct OpenFile *o;
int r;
if ((r = openfile_lookup(envid, req->req_fileid, &o)) < 0){
return r;
}
r = file_read(o->o_file, ret->ret_buf, req->req_n, o->o_fd->fd_offset);
if(r > 0)
o->o_fd->fd_offset += r;
return r;
}
// Write req->req_n bytes from req->req_buf to req_fileid, starting at
// the current seek position, and update the seek position
// accordingly. Extend the file if necessary. Returns the number of
// bytes written, or < 0 on error.
int
serve_write(envid_t envid, struct Fsreq_write *req)
{
if (debug)
cprintf("serve_write %08x %08x %08x\n", envid, req->req_fileid, req->req_n);
// LAB 5: Your code here.
struct OpenFile *o;
int r;
if ((r = openfile_lookup(envid, req->req_fileid, &o)) < 0){
return r;
}
if (debug)
cprintf("serve_write buf %d\n", *(int *)req->req_buf);
r = file_write(o->o_file, req->req_buf, req->req_n, o->o_fd->fd_offset);
if(r > 0)
o->o_fd->fd_offset += r;
return r;
}
// Stat ipc->stat.req_fileid. Return the file's struct Stat to the
// caller in ipc->statRet.
int
serve_stat(envid_t envid, union Fsipc *ipc)
{
struct Fsreq_stat *req = &ipc->stat;
struct Fsret_stat *ret = &ipc->statRet;
struct OpenFile *o;
int r;
if (debug)
cprintf("serve_stat %08x %08x\n", envid, req->req_fileid);
if ((r = openfile_lookup(envid, req->req_fileid, &o)) < 0)
return r;
strcpy(ret->ret_name, o->o_file->f_name);
ret->ret_size = o->o_file->f_size;
ret->ret_isdir = (o->o_file->f_type == FTYPE_DIR);
return 0;
}
// Flush all data and metadata of req->req_fileid to disk.
int
serve_flush(envid_t envid, struct Fsreq_flush *req)
{
struct OpenFile *o;
int r;
if (debug)
cprintf("serve_flush %08x %08x\n", envid, req->req_fileid);
if ((r = openfile_lookup(envid, req->req_fileid, &o)) < 0)
return r;
file_flush(o->o_file);
return 0;
}
int
serve_sync(envid_t envid, union Fsipc *req)
{
fs_sync();
return 0;
}
typedef int (*fshandler)(envid_t envid, union Fsipc *req);
fshandler handlers[] = {
// Open is handled specially because it passes pages
/* [FSREQ_OPEN] = (fshandler)serve_open, */
[FSREQ_READ] = serve_read,
[FSREQ_STAT] = serve_stat,
[FSREQ_FLUSH] = (fshandler)serve_flush,
[FSREQ_WRITE] = (fshandler)serve_write,
[FSREQ_SET_SIZE] = (fshandler)serve_set_size,
[FSREQ_SYNC] = serve_sync
};
#define NHANDLERS (sizeof(handlers)/sizeof(handlers[0]))
void
serve(void)
{
uint32_t req, whom;
int perm, r;
void *pg;
while (1) {
perm = 0;
req = ipc_recv((int32_t *) &whom, fsreq, &perm);
if (debug)
cprintf("fs req %d from %08x [page %08x: %s]\n",
req, whom, uvpt[PGNUM(fsreq)], fsreq);
// All requests must contain an argument page
if (!(perm & PTE_P)) {
cprintf("Invalid request from %08x: no argument page\n",
whom);
continue; // just leave it hanging...
}
pg = NULL;
if (req == FSREQ_OPEN) {
r = serve_open(whom, (struct Fsreq_open*)fsreq, &pg, &perm);
} else if (req < NHANDLERS && handlers[req]) {
r = handlers[req](whom, fsreq);
} else {
cprintf("Invalid request code %d from %08x\n", req, whom);
r = -E_INVAL;
}
ipc_send(whom, r, pg, perm);
sys_page_unmap(0, fsreq);
}
}
void
umain(int argc, char **argv)
{
static_assert(sizeof(struct File) == 256);
binaryname = "fs";
cprintf("FS is running\n");
// Check that we are able to do I/O
outw(0x8A00, 0x8A00);
cprintf("FS can do I/O\n");
serve_init();
fs_init();
serve();
}