| #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 <fcall.h> |
| |
| //extern uint32_t kerndate; |
| uint32_t kerndate = 0; |
| |
| void |
| mkqid(struct qid *q, int64_t path, uint32_t vers, int type) |
| { |
| q->type = type; |
| q->vers = vers; |
| q->path = path; |
| } |
| |
| void |
| devdir(struct chan *c, struct qid qid, char *n, int64_t length, char *user, |
| long perm, struct dir *db) |
| { |
| db->name = n; |
| if (c->flag & CMSG) |
| qid.type |= QTMOUNT; |
| db->qid = qid; |
| /* |
| * When called via devwalk c->dev is NULL |
| * until the walk succeeds. |
| */ |
| if (c->dev != NULL) |
| db->type = c->dev->dc; |
| else |
| db->type = -1; |
| db->dev = c->devno; |
| db->mode = perm; |
| db->mode |= qid.type << 24; |
| db->atime = 0; //seconds |
| db->mtime = kerndate; |
| db->length = length; |
| db->uid = user; |
| db->gid = eve; |
| db->muid = user; |
| } |
| |
| /* |
| * (here, struct devgen is the prototype; devgen is the function in dev.c.) |
| * |
| * a struct devgen is expected to return the directory entry for ".." |
| * if you pass it s==DEVDOTDOT (-1). otherwise... |
| * |
| * there are two contradictory rules. |
| * |
| * (i) if c is a directory, a struct devgen is expected to list its children |
| * as you iterate s. |
| * |
| * (ii) whether or not c is a directory, a struct devgen is expected to list |
| * its siblings as you iterate s. |
| * |
| * devgen always returns the list of children in the root |
| * directory. thus it follows (i) when c is the root and (ii) otherwise. |
| * many other struct devgens follow (i) when c is a directory and (ii) otherwise. |
| * |
| * devwalk assumes (i). it knows that devgen breaks (i) |
| * for children that are themselves directories, and explicitly catches them. |
| * |
| * devstat assumes (ii). if the struct devgen in question follows (i) |
| * for this particular c, devstat will not find the necessary info. |
| * with our particular struct devgen functions, this happens only for |
| * directories, so devstat makes something assuming |
| * c->name, c->qid, eve, DMDIR|0555. |
| * |
| * devdirread assumes (i). the callers have to make sure |
| * that the struct devgen satisfies (i) for the chan being read. |
| */ |
| /* |
| * the zeroth element of the table MUST be the directory itself for .. |
| */ |
| int |
| devgen(struct chan *c, char *name, struct dirtab *tab, int ntab, int i, |
| struct dir *dp) |
| { |
| if (tab == 0) |
| return -1; |
| if (i == DEVDOTDOT) { |
| /* nothing */ |
| } else if (name) { |
| for (i = 1; i < ntab; i++) |
| if (strcmp(tab[i].name, name) == 0) |
| break; |
| if (i == ntab) |
| return -1; |
| tab += i; |
| } else { |
| /* skip over the first element, that for . itself */ |
| i++; |
| if (i >= ntab) |
| return -1; |
| tab += i; |
| } |
| devdir(c, tab->qid, tab->name, tab->length, eve, tab->perm, dp); |
| return 1; |
| } |
| |
| void devreset() |
| { |
| } |
| |
| void devinit() |
| { |
| } |
| |
| void devshutdown() |
| { |
| } |
| |
| struct chan *devattach(int dc, char *spec) |
| { |
| struct chan *c; |
| char *buf; |
| int len = strlen(spec) + 2 + 1; /* 2 for #c, 1 for \0 */ |
| /* |
| * There are no error checks here because |
| * this can only be called from the driver of dc |
| * which pretty much guarantees devtabget will |
| * succeed. |
| */ |
| c = newchan(); |
| mkqid(&c->qid, 0, 0, QTDIR); |
| c->dev = devtabget(dc, 0); |
| if (spec == NULL) |
| spec = ""; |
| buf = kzmalloc(len, KMALLOC_WAIT); |
| snprintf(buf, len, "#%c%s", dc, spec); |
| c->path = newpath(buf); |
| kfree(buf); |
| return c; |
| } |
| |
| struct chan *devclone(struct chan *c) |
| { |
| struct chan *nc; |
| |
| if (c->flag & COPEN) { |
| panic("devclone: file of type %c already open\n", |
| c->dev != NULL ? c->dev->dc : -1); |
| } |
| |
| nc = newchan(); |
| |
| /* |
| * The caller fills dev in if and when necessary. |
| nc->dev = NULL; //XDYNXX |
| */ |
| nc->devno = c->devno; |
| nc->mode = c->mode; |
| nc->qid = c->qid; |
| nc->offset = c->offset; |
| nc->umh = NULL; |
| nc->aux = c->aux; |
| nc->mqid = c->mqid; |
| nc->mc = c->mc; |
| return nc; |
| } |
| |
| struct walkqid *devwalk(struct chan *c, struct chan *nc, char **name, int nname, |
| struct dirtab *tab, int ntab, devgen_t * gen) |
| { |
| ERRSTACK(2); |
| int i, j; |
| volatile int alloc; /* to keep waserror from optimizing this out */ |
| struct walkqid *wq; |
| char *n; |
| struct dir dir; |
| |
| if (nname > 0) |
| isdir(c); |
| alloc = 0; |
| wq = kzmalloc(sizeof(struct walkqid) + (nname) * sizeof(struct qid), |
| KMALLOC_WAIT); |
| if (waserror()) { |
| if (alloc && wq->clone != NULL) |
| cclose(wq->clone); |
| kfree(wq); |
| poperror(); |
| return NULL; |
| } |
| if (nc == NULL) { |
| nc = devclone(c); |
| /* |
| * nc->dev remains NULL for now. //XDYNX |
| */ |
| alloc = 1; |
| } |
| wq->clone = nc; |
| dir.qid.path = 0; |
| for (j = 0; j < nname; j++) { |
| if (!(nc->qid.type & QTDIR)) { |
| if (j == 0) |
| error(Enotdir); |
| goto Done; |
| } |
| n = name[j]; |
| if (strcmp(n, ".") == 0) { |
| Accept: |
| wq->qid[wq->nqid++] = nc->qid; |
| continue; |
| } |
| if (strcmp(n, "..") == 0) { |
| /* |
| * Use c->dev->name in the error because |
| * nc->dev should be NULL here. |
| */ |
| if ((*gen) (nc, NULL, tab, ntab, DEVDOTDOT, &dir) != 1) { |
| printk("devgen walk .. in dev%s %#llux broken\n", |
| c->dev->name, nc->qid.path); |
| error("broken devgen"); |
| } |
| nc->qid = dir.qid; |
| goto Accept; |
| } |
| /* |
| * Ugly problem: If we're using devgen, make sure we're |
| * walking the directory itself, represented by the first |
| * entry in the table, and not trying to step into a sub- |
| * directory of the table, e.g. /net/net. struct devgen itself |
| * should take care of the problem, but it doesn't have |
| * the necessary information (that we're doing a walk). |
| */ |
| if (gen == devgen && nc->qid.path != tab[0].qid.path) |
| goto Notfound; |
| dir.qid.path = 0; |
| for (i = 0;; i++) { |
| switch ((*gen) (nc, n, tab, ntab, i, &dir)) { |
| case -1: |
| Notfound: |
| if (j == 0) |
| error(Enonexist); |
| set_errstr(Enonexist); |
| goto Done; |
| case 0: |
| continue; |
| case 1: |
| printd("DEVWALK looking for %s, found %s, path %p", n, |
| dir.name, dir.qid.path); |
| if (strcmp(n, dir.name) == 0) { |
| printd(" MATCH!\n"); |
| nc->qid = dir.qid; |
| goto Accept; |
| } |
| printd("\n"); |
| continue; |
| } |
| } |
| } |
| /* |
| * We processed at least one name, so will return some data. |
| * If we didn't process all nname entries succesfully, we drop |
| * the cloned channel and return just the struct qids of the walks. |
| */ |
| Done: |
| poperror(); |
| if (wq->nqid < nname) { |
| if (alloc) |
| cclose(wq->clone); |
| wq->clone = NULL; |
| } else if (wq->clone) { |
| /* attach cloned channel to same device */ |
| //what goes here: //XDYNX |
| // ->dev must be NULL because can't walk an open chan, right? |
| // what about ref count on dev? |
| wq->clone->dev = c->dev; |
| //if(wq->clone->dev) //XDYNX |
| // devtabincr(wq->clone->dev); |
| } |
| return wq; |
| } |
| |
| long |
| devstat(struct chan *c, uint8_t * db, long n, struct dirtab *tab, int ntab, |
| devgen_t * gen) |
| { |
| int i; |
| struct dir dir; |
| char *p, *elem; |
| |
| printd("\nFresh Devstat, chan path %p\n", c->qid.path); |
| dir.qid.path = 0; |
| for (i = 0;; i++) { |
| switch ((*gen) (c, NULL, tab, ntab, i, &dir)) { |
| case -1: |
| if (c->qid.type & QTDIR) { |
| if (c->path == NULL) |
| elem = "???"; |
| else if (strcmp(c->path->s, "/") == 0) |
| elem = "/"; |
| else |
| for (elem = p = c->path->s; *p; p++) |
| if (*p == '/') |
| elem = p + 1; |
| devdir(c, c->qid, elem, 0, eve, DMDIR | 0555, &dir); |
| n = convD2M(&dir, db, n); |
| if (n == 0) |
| error(Ebadarg); |
| return n; |
| } |
| error(Enonexist); |
| case 0: |
| printd("DEVSTAT got 0\n"); |
| break; |
| case 1: |
| printd("DEVSTAT gen returns path %p name %s, want path %p\n", |
| dir.qid.path, dir.name, c->qid.path); |
| if (c->qid.path == dir.qid.path) { |
| if (c->flag & CMSG) |
| dir.mode |= DMMOUNT; |
| n = convD2M(&dir, db, n); |
| if (n == 0) |
| error(Ebadarg); |
| return n; |
| } |
| break; |
| } |
| } |
| } |
| |
| long |
| devdirread(struct chan *c, char *d, long n, struct dirtab *tab, int ntab, |
| devgen_t * gen) |
| { |
| long m, dsz; |
| struct dir dir; |
| |
| dir.qid.path = 0; |
| for (m = 0; m < n; c->dri++) { |
| switch ((*gen) (c, NULL, tab, ntab, c->dri, &dir)) { |
| case -1: |
| return m; |
| |
| case 0: |
| break; |
| |
| case 1: |
| dsz = convD2M(&dir, (uint8_t *) d, n - m); |
| if (dsz <= BIT16SZ) { /* <= not < because this isn't stat; read is stuck */ |
| if (m == 0) |
| error(Eshort); |
| return m; |
| } |
| m += dsz; |
| d += dsz; |
| break; |
| } |
| } |
| |
| return m; |
| } |
| |
| /* |
| * error(Eperm) if open permission not granted for up->user. |
| */ |
| void devpermcheck(char *fileuid, int perm, int omode) |
| { |
| int t; |
| static int access[] = { 0400, 0200, 0600, 0100 }; |
| |
| if (strcmp(current->user, fileuid) == 0) |
| perm <<= 0; |
| else if (strcmp(current->user, eve) == 0) |
| perm <<= 3; |
| else |
| perm <<= 6; |
| |
| t = access[omode & 3]; |
| if ((t & perm) != t) |
| error(Eperm); |
| } |
| |
| struct chan *devopen(struct chan *c, int omode, struct dirtab *tab, int ntab, |
| devgen_t * gen) |
| { |
| int i; |
| struct dir dir; |
| |
| dir.qid.path = 0; |
| for (i = 0;; i++) { |
| switch ((*gen) (c, NULL, tab, ntab, i, &dir)) { |
| case -1: |
| goto Return; |
| case 0: |
| break; |
| case 1: |
| if (c->qid.path == dir.qid.path) { |
| devpermcheck(dir.uid, dir.mode, omode); |
| goto Return; |
| } |
| break; |
| } |
| } |
| Return: |
| c->offset = 0; |
| if ((c->qid.type & QTDIR) && omode != OREAD) |
| error(Eperm); |
| c->mode = openmode(omode); |
| c->flag |= COPEN; |
| return c; |
| } |
| |
| void devcreate(struct chan *a, char *b, int c, int d) |
| { |
| error(Eperm); |
| } |
| |
| /* no analog in akaros yet. */ |
| |
| struct block *devbread(struct chan *c, long n, int64_t offset) |
| { |
| ERRSTACK(2); |
| struct block *bp; |
| |
| bp = allocb(n); |
| if (bp == 0) |
| error(Enomem); |
| if (waserror()) { |
| freeb(bp); |
| nexterror(); |
| } |
| bp->wp += c->dev->read(c, bp->wp, n, offset); |
| poperror(); |
| return bp; |
| } |
| |
| long |
| devbwrite(struct chan *c, struct block *bp, int64_t offset) |
| { |
| ERRSTACK(2); |
| long n; |
| |
| if (waserror()) { |
| freeb(bp); |
| nexterror(); |
| } |
| n = c->dev->write(c, bp->rp, BLEN(bp), offset); |
| poperror(); |
| freeb(bp); |
| |
| return n; |
| } |
| |
| void devremove(struct chan *c) |
| { |
| error(Eperm); |
| } |
| |
| long devwstat(struct chan *c, uint8_t * a, long b) |
| { |
| error(Eperm); |
| return 0; |
| } |
| |
| void devpower(int onoff) |
| { |
| error(Eperm); |
| } |
| |
| int devconfig(int a, char *b, void *v) |
| { |
| error(Eperm); |
| return 0; |
| } |
| |
| char *devchaninfo(struct chan *chan, char *ret, size_t ret_l) |
| { |
| snprintf(ret, ret_l, "qid.path: %p, qid.type: %02x\n", chan->qid.path, |
| chan->qid.type); |
| return ret; |
| } |