/* * File system server main loop - * serves IPC requests from other environments. */ #include #include #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: return 0; } // 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. panic("serve_write not implemented"); } // 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 }; 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 < ARRAY_SIZE(handlers) && 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(); fs_test(); serve(); }