| /* Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. |
| * Portions Copyright © 1997-1999 Vita Nuova Limited |
| * Portions Copyright © 2000-2007 Vita Nuova Holdings Limited |
| * (www.vitanuova.com) |
| * Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others |
| * |
| * Modified for the Akaros operating system: |
| * Copyright (c) 2013-2014 The Regents of the University of California |
| * Copyright (c) 2013-2015 Google Inc. |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a copy |
| * of this software and associated documentation files (the "Software"), to deal |
| * in the Software without restriction, including without limitation the rights |
| * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| * copies of the Software, and to permit persons to whom the Software is |
| * furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice shall be included in |
| * all copies or substantial portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
| * SOFTWARE. */ |
| |
| #include <vfs.h> |
| #include <kfs.h> |
| #include <slab.h> |
| #include <kmalloc.h> |
| #include <kref.h> |
| #include <string.h> |
| #include <stdio.h> |
| #include <assert.h> |
| #include <error.h> |
| #include <cpio.h> |
| #include <pmap.h> |
| #include <smp.h> |
| #include <ip.h> |
| #include <umem.h> |
| |
| struct dev rootdevtab; |
| |
| static char *devname(void) |
| { |
| return rootdevtab.name; |
| } |
| |
| /* make it a power of 2 and nobody gets hurt */ |
| #define MAXFILE 1024 |
| int rootmaxq = MAXFILE; |
| |
| /* TODO: |
| * - synchronization! what needs protection from concurrent use, etc. |
| * - clean up documentation and whatnot |
| * - does remove, mkdir, rmdir work? |
| * - fill this with cpio stuff |
| * - figure out how to use the page cache |
| * - atime/ctime/mtime/etctime |
| */ |
| |
| /* this gives you some idea of how much I like linked lists. Just make |
| * a big old table. Later on we can put next and prev indices into the |
| * data if we want but, our current kfs is 1-3 levels deep and very small |
| * (< 200 entries) so I doubt we'll need to do that. It just makes debugging |
| * memory a tad easier. |
| */ |
| /* Da Rules. |
| * The roottab contains [name, qid, length, perm]. |
| * Length is filesize for files, # children/elements for dirs |
| * Qid is [path, vers, type]. Path is me. vers is next. Type is QTDIR for dir |
| * and QTFILE for file and 0 for empty. |
| * |
| * We have rootdata to help out with a couple things, notably storing the 'data' |
| * of the object (file or dir) and the first child index/qid.path for |
| * directories. |
| * |
| * Data is [dotdot, child, ptr] |
| * dotdot is .. |
| * ptr is data for files, first child for dirs. |
| * child is the qid.path of the first child of a directory. |
| * Possibly 0 == no child. |
| * To find the next sibling (in a directory), look at roottab[i].qid.vers. |
| */ |
| |
| /* we pack the qid as follows: path is the index, vers is ., and type is type */ |
| |
| /* Inferno seems to want to: perm |= DMDIR. It gets checked in other places. |
| * NxM didn't want this, IIRC. |
| * |
| * Also note that "" (/, #root, whatever) has no vers/next/sibling. |
| * |
| * If you want to add new entries, add it to the roottab such that the linked |
| * list of indexes is a cycle (change the last current one), then add an entry |
| * to rootdata, and then change the first rootdata entry to have another entry. |
| * Yeah, it's a pain in the ass. |
| * |
| * To add subdirectories, or any child of a directory, the files (e.g. env_dir1) |
| * go in roottab. Children of a parent are linked with their vers (note |
| * env_dir1 points to env_dir2), and the last item's vers = 0. These files need |
| * their dotdot set in rootdata to the qid of their parent. The directory that |
| * has children needs its child pointer set to the first qid in the list, and |
| * its data pointer must point to the roottab entry for the child. This also |
| * means that all child entries in roottab for a parent must be contiguous. |
| * |
| * Yeah, it's a pain in the ass. And, given this structure, it probably can't |
| * grow dynamically (I think we assume roottab[i] = entry for qid.path all over |
| * the place - imagine what happens if we wanted to squeeze in a new entry). */ |
| struct dirtab roottab[MAXFILE] = { |
| {"", { 0, 0, QTDIR}, 13, DMDIR | 0777}, |
| {"chan", { 1, 2, QTDIR}, 0, DMDIR | 0777}, |
| {"dev", { 2, 3, QTDIR}, 0, DMDIR | 0777}, |
| {"fd", { 3, 4, QTDIR}, 0, DMDIR | 0777}, |
| {"prog", { 4, 5, QTDIR}, 0, DMDIR | 0777}, |
| {"prof", { 5, 6, QTDIR}, 0, DMDIR | 0777}, |
| {"net", { 6, 7, QTDIR}, 0, DMDIR | 0777}, |
| {"net.alt", { 7, 8, QTDIR}, 0, DMDIR | 0777}, |
| {"nvfs", { 8, 9, QTDIR}, 0, DMDIR | 0777}, |
| {"env", { 9, 10, QTDIR}, 2, DMDIR | 0777}, |
| {"root", {10, 11, QTDIR}, 0, DMDIR | 0777}, |
| {"srv", {11, 12, QTDIR}, 0, DMDIR | 0777}, |
| {"mnt", {12, 13, QTDIR}, 0, DMDIR | 0777}, |
| {"proc", {13, 0, QTDIR}, 0, DMDIR | 0777}, |
| {"env_dir1", {14, 15, QTDIR}, 0, DMDIR | 0777}, |
| {"env_dir2", {15, 0, QTDIR}, 0, DMDIR | 0777}, |
| }; |
| |
| struct rootdata { |
| int dotdot; |
| int child; |
| void *ptr; |
| }; |
| |
| struct rootdata rootdata[MAXFILE] = { |
| {0, 1, &roottab[1]}, |
| {0, 0, NULL}, |
| {0, 0, NULL}, |
| {0, 0, NULL}, |
| {0, 0, NULL}, |
| {0, 0, NULL}, |
| {0, 0, NULL}, |
| {0, 0, NULL}, |
| {0, 0, NULL}, |
| {0, 14, &roottab[14]}, |
| {0, 0, NULL}, |
| {0, 0, NULL}, |
| {0, 0, NULL}, |
| {0, 0, NULL}, |
| {9, 0, NULL}, |
| {9, 0, NULL}, |
| }; |
| |
| /* this is super useful */ |
| void dumprootdev(void) |
| { |
| struct dirtab *r = roottab; |
| struct rootdata *rd = rootdata; |
| int i; |
| |
| printk("[ dirtab ] name: [pth, ver, typ], len, perm, .., chld, data pointer\n"); |
| for (i = 0; i < rootmaxq; i++, r++, rd++) { |
| if (i && (!r->name[0])) |
| continue; |
| printk("[%p]%30s: [%3d, %3d, %3d], %5d, %11o,", |
| r, |
| r->name, r->qid.path, r->qid.vers, r->qid.type, |
| r->length, r->perm); |
| printk(" %3d, %4d, %p\n", rd->dotdot, rd->child, rd->ptr); |
| } |
| } |
| |
| static int findempty(void) |
| { |
| int i; |
| for (i = 0; i < rootmaxq; i++) { |
| if (!roottab[i].qid.type) { |
| memset(&roottab[i], 0, sizeof(roottab[i])); |
| return i; |
| } |
| } |
| return -1; |
| } |
| |
| static void freeempty(int i) |
| { |
| roottab[i].qid.type = 0; |
| } |
| |
| static int newentry(int parent) |
| { |
| int n = findempty(); |
| int sib; |
| if (n < 0) |
| error(EFAIL, "#root. No more"); |
| printd("new entry is %d\n", n); |
| /* add the new one to the head of the linked list. vers is 'next' */ |
| roottab[n].qid.vers = rootdata[parent].child; |
| rootdata[parent].child = n; |
| return n; |
| } |
| |
| static int createentry(int dir, char *name, int omode, int perm) |
| { |
| int n = newentry(dir); |
| strlcpy(roottab[n].name, name, sizeof(roottab[n].name)); |
| roottab[n].length = 0; |
| roottab[n].perm = perm; |
| /* vers is already properly set. */ |
| mkqid(&roottab[n].qid, n, roottab[n].qid.vers, |
| perm & DMDIR ? QTDIR : QTFILE); |
| rootdata[n].dotdot = roottab[dir].qid.path; |
| rootdata[dir].ptr = &roottab[n]; |
| return n; |
| } |
| |
| static void rootinit(void) |
| { |
| } |
| |
| static struct chan *rootattach(char *spec) |
| { |
| struct chan *c; |
| if (*spec) |
| error(EINVAL, ERROR_FIXME); |
| c = devattach(devname(), spec); |
| mkqid(&c->qid, roottab[0].qid.path, roottab[0].qid.vers, QTDIR); |
| return c; |
| } |
| |
| /* rootgen is unlike other gens when it comes to the dirtab (tab) and ntab (nd). |
| * We actually are a linked list of entries that happen to be in a table. We |
| * can figure out where to start based on the chan's qid.path and start the gen |
| * from there. So we don't need the 'tab' from dev. Likewise, for directories, |
| * we can figure out nd - our length in the dirtab. */ |
| static int rootgen(struct chan *c, char *name, struct dirtab *tab_unused, |
| int nd_unused, int s, struct dir *dp) |
| { |
| int p, i; |
| struct rootdata *r; |
| int iter; |
| struct dirtab *tab; |
| |
| printd("rootgen from %s, for %s, s %d\n", roottab[c->qid.path].name, name, |
| s); |
| if (s == DEVDOTDOT) { |
| p = rootdata[c->qid.path].dotdot; |
| c->qid = roottab[p].qid; |
| name = roottab[p].name; |
| devdir(c, c->qid, name, 0, eve.name, 0777, dp); |
| return 1; |
| } |
| |
| if (c->qid.type != QTDIR) { |
| /* return ourselved the first time; after that, -1 */ |
| if (s) |
| return -1; |
| tab = &roottab[c->qid.path]; |
| devdir(c, tab->qid, tab->name, tab->length, eve.name, tab->perm, dp); |
| return 1; |
| } |
| |
| if (name != NULL) { |
| int path = c->qid.path; |
| isdir(c); |
| tab = &roottab[rootdata[path].child]; |
| /* we're starting at a directory. It might be '.' */ |
| for (iter = 0, i = rootdata[path].child; /* break */; iter++) { |
| if (strncmp(tab->name, name, KNAMELEN) == 0) { |
| printd("Rootgen returns 1 for %s\n", name); |
| devdir(c, tab->qid, tab->name, tab->length, eve.name, tab->perm, |
| dp); |
| printd("return 1 with [%d, %d, %d]\n", dp->qid.path, |
| dp->qid.vers, dp->qid.type); |
| return 1; |
| } |
| if (iter > rootmaxq) { |
| printk("BUG:"); |
| dumprootdev(); |
| printk("name %s\n", name); |
| return -1; |
| } |
| i = roottab[i].qid.vers; |
| if (!i) |
| break; |
| tab = &roottab[i]; |
| } |
| printd("rootgen: :%s: failed at path %d\n", name, path); |
| return -1; |
| } |
| /* Note we do not provide a "direct gen" for directories, which normally I'd |
| * do here. If we do, that will confuse devdirread, which expects us to |
| * list our children, not ourselves. stat() wants a "direct gen", e.g. for |
| * ls -l or stat of a directory. We can handle that in rootstat(). */ |
| if (s >= roottab[c->qid.path].length) |
| return -1; |
| for (i = rootdata[c->qid.path].child; i; i = roottab[i].qid.vers) { |
| tab = &roottab[i]; |
| if (s-- == 0) |
| break; |
| } |
| if (!i) { |
| warn("#root overflowed 'i', probably a bug"); |
| return -1; |
| } |
| printd("root scan find returns path %p name %s\n", tab->qid.path, tab->name); |
| devdir(c, tab->qid, tab->name, tab->length, eve.name, tab->perm, dp); |
| return 1; |
| } |
| |
| static struct walkqid *rootwalk(struct chan *c, struct chan *nc, char **name, |
| int nname) |
| { |
| return devwalk(c, nc, name, nname, NULL, 0, rootgen); |
| } |
| |
| /* Instead of using devstat, we use our own. This allows us to have stats on |
| * directories. devstat() just fakes it. Note that gen cannot return a direct |
| * gen for a directory, since that would break devdirread(). */ |
| static int rootstat(struct chan *c, uint8_t * dp, int n) |
| { |
| struct dir dir[1]; |
| ssize_t ret; |
| struct dirtab *entry = &roottab[c->qid.path]; |
| |
| /* TODO: this assumes eve is the user, which is what synthetic devices do. |
| * Likewise, it sets atime/mtime like a synth device. There might be other |
| * weird things, like qid.type and mode. */ |
| devdir(c, entry->qid, entry->name, entry->length, eve.name, entry->perm, |
| dir); |
| ret = convD2M(dir, dp, n); |
| if (!ret) |
| error(EFAIL, "#root failed to convert stat object to M"); |
| return ret; |
| } |
| |
| static struct chan *rootopen(struct chan *c, int omode) |
| { |
| return devopen(c, omode, NULL, 0, rootgen); |
| } |
| |
| static void rootcreate(struct chan *c, char *name, int omode, uint32_t perm) |
| { |
| struct dirtab *r = &roottab[c->qid.path], *newr; |
| struct rootdata *rd = &rootdata[c->qid.path]; |
| /* need to filter openmode so that it gets only the access-type bits */ |
| omode = openmode(omode); |
| c->mode = openmode(omode); |
| printd("rootcreate: c %p, name %s, omode %o, perm %x\n", |
| c, name, omode, perm); |
| /* find an empty slot */ |
| int path = c->qid.path; |
| int newfile; |
| newfile = createentry(path, name, omode, perm); |
| c->qid = roottab[newfile].qid; /* need to update c */ |
| r->length++; |
| if (newfile > rootmaxq) |
| rootmaxq = newfile; |
| printd("create: %s, newfile %d, dotdot %d, rootmaxq %d\n", name, newfile, |
| rootdata[newfile].dotdot, rootmaxq); |
| } |
| |
| /* |
| * sysremove() knows this is a nop |
| * fyi, this isn't true anymore! they need to set c->type = -1; |
| */ |
| static void rootclose(struct chan *c) |
| { |
| } |
| |
| static long rootread(struct chan *c, void *buf, long n, int64_t offset) |
| { |
| uint32_t p, len; |
| uint8_t *data; |
| |
| p = c->qid.path; |
| if (c->qid.type & QTDIR) |
| return devdirread(c, buf, n, NULL, 0, rootgen); |
| len = roottab[p].length; |
| if (offset < 0 || offset >= len) { |
| return 0; |
| } |
| if (offset + n > len) |
| n = len - offset; |
| data = rootdata[p].ptr; |
| /* we can't really claim it has to be a user address. Lots of |
| * kernel things read directly, e.g. /dev/reboot, #nix, etc. |
| * Address validation should be done in the syscall layer. |
| */ |
| memcpy(buf, data + offset, n); |
| return n; |
| } |
| |
| /* For now, just kzmalloc the right amount. Later, we should use |
| * pages so mmap will go smoothly. Would be really nice to have a |
| * kpagemalloc ... barret? |
| * we have kpage_alloc (gives a page) and kpage_alloc_addr (void*) |
| */ |
| static long rootwrite(struct chan *c, void *a, long n, int64_t off) |
| { |
| struct rootdata *rd = &rootdata[c->qid.path]; |
| struct dirtab *r = &roottab[c->qid.path]; |
| |
| if (off < 0) |
| error(EFAIL, "rootwrite: offset < 0!"); |
| |
| if (off + n > r->length) { |
| void *p; |
| p = krealloc(rd->ptr, off + n, MEM_WAIT); |
| if (! p) |
| error(EFAIL, "rootwrite: could not grow the file to %d bytes", |
| off + n); |
| rd->ptr = p; |
| r->length = off + n; |
| } |
| assert(current); |
| if (memcpy_from_user_errno(current, rd->ptr + off, a, n) < 0) |
| error(EFAIL, "%s: bad user addr %p", __FUNCTION__, a); |
| |
| return n; |
| } |
| |
| static int rootwstat(struct chan *c, uint8_t *m_buf, int m_buf_sz) |
| { |
| struct dirtab *file = &roottab[c->qid.path]; |
| struct dir *dir; |
| int m_sz; |
| |
| /* TODO: some security check, Eperm on error */ |
| |
| /* common trick in wstats. we want the dir and any strings in the M. the |
| * strings are smaller than entire M (strings plus other M). the strings |
| * will be placed right after the dir (dir[1]) */ |
| dir = kzmalloc(sizeof(struct dir) + m_buf_sz, MEM_WAIT); |
| m_sz = convM2D(m_buf, m_buf_sz, &dir[0], (char*)&dir[1]); |
| if (!m_sz) { |
| kfree(dir); |
| error(ENODATA, ERROR_FIXME); |
| } |
| /* TODO: handle more things than just the mode */ |
| if (!emptystr(dir->name)) { |
| printk("[%s] attempted rename of %s to %s\n", __FUNCTION__, |
| file->name, dir->name); /* strncpy for this btw */ |
| // XXX this isn't enough. they give us the full path, like |
| // attempted rename of SOME_FILE |
| // /root/SOME_DIR/SOME_NEW_FILE |
| // |
| // the first one is just the final part. we don't even know where we |
| // are in the directory hierarchy (is our parent called SOME_DIR?) |
| // we also don't know the name of our mount point for the destination |
| // |
| // 9ns rename creates the new_chan. that seems fucked |
| // might be ok... |
| // probably leaking shit |
| // mount detection is wrong |
| strlcpy(file->name, dir->name, KNAMELEN); |
| } |
| if (dir->mode != -1) |
| file->perm = dir->mode | (file->qid.type == QTDIR ? DMDIR : 0); |
| kfree(dir); |
| return m_sz; |
| } |
| |
| struct dev rootdevtab __devtab = { |
| .name = "root", |
| .reset = devreset, |
| .init = rootinit, |
| .shutdown = devshutdown, |
| .attach = rootattach, |
| .walk = rootwalk, |
| .stat = rootstat, |
| .open = rootopen, |
| .create = rootcreate, |
| .close = rootclose, |
| .read = rootread, |
| .bread = devbread, |
| .write = rootwrite, |
| .bwrite = devbwrite, |
| .remove = devremove, |
| .wstat = rootwstat, |
| .power = devpower, |
| .chaninfo = devchaninfo, |
| }; |