| #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> |
| |
| |
| struct srv |
| { |
| char *name; |
| char *owner; |
| uint32_t perm; |
| struct chan *chan; |
| struct srv *link; |
| uint32_t path; |
| }; |
| |
| static qlock_t srvlk; |
| static struct srv *srv; |
| static int qidpath; |
| |
| static int |
| srvgen(struct chan *c, char *unused_char_p_t, struct dirtab*unused_dirtab, int unused_int, int s, struct dir *dp) |
| { |
| struct srv *sp; |
| struct qid q; |
| |
| if(s == DEVDOTDOT){ |
| devdir(c, c->qid, "#s", 0, eve, 0555, dp); |
| return 1; |
| } |
| |
| qlock(&srvlk); |
| for(sp = srv; sp && s; sp = sp->link) |
| s--; |
| |
| if(sp == 0) { |
| qunlock(&srvlk); |
| return -1; |
| } |
| |
| mkqid(&q, sp->path, 0, QTFILE); |
| /* make sure name string continues to exist after we release lock */ |
| strncpy(get_cur_genbuf(), sp->name, GENBUF_SZ); |
| devdir(c, q, get_cur_genbuf(), 0, sp->owner, sp->perm, dp); |
| qunlock(&srvlk); |
| return 1; |
| } |
| |
| static void |
| srvinit(void) |
| { |
| qidpath = 1; |
| qlock_init(&srvlk); |
| } |
| |
| static struct chan* |
| srvattach(char *spec) |
| { |
| return devattach('s', spec); |
| } |
| |
| static struct walkqid* |
| srvwalk(struct chan *c, struct chan *nc, char **name, int nname) |
| { |
| return devwalk(c, nc, name, nname, 0, 0, srvgen); |
| } |
| |
| static struct srv* |
| srvlookup(char *name, uint32_t qidpath) |
| { |
| struct srv *sp; |
| for(sp = srv; sp; sp = sp->link) |
| if(sp->path == qidpath || (name && strcmp(sp->name, name) == 0)) |
| return sp; |
| return NULL; |
| } |
| |
| static long |
| srvstat(struct chan *c, uint8_t *db, long n) |
| { |
| return devstat(c, db, n, 0, 0, srvgen); |
| } |
| |
| char* |
| srvname(struct chan *c) |
| { |
| struct srv *sp; |
| char *s; |
| |
| for(sp = srv; sp; sp = sp->link) |
| if(sp->chan == c){ |
| int len = 3 + strlen(sp->name) + 1; |
| s = kzmalloc(len, 0); |
| snprintf(s, len, "#s/%s", sp->name); |
| return s; |
| } |
| return NULL; |
| } |
| |
| static struct chan* |
| srvopen(struct chan *c, int omode) |
| { |
| ERRSTACK(2); |
| struct srv *sp; |
| |
| if(c->qid.type == QTDIR){ |
| if(omode & ORCLOSE) |
| error(Eperm); |
| if(omode != OREAD) |
| error(Eisdir); |
| c->mode = omode; |
| c->flag |= COPEN; |
| c->offset = 0; |
| return c; |
| } |
| qlock(&srvlk); |
| if(waserror()){ |
| qunlock(&srvlk); |
| nexterror(); |
| } |
| |
| sp = srvlookup(NULL, c->qid.path); |
| if(sp == 0 || sp->chan == 0) |
| error(Eshutdown); |
| |
| /* FYI: plan9 used to bail out for O_TRUNCs here */ |
| |
| if(openmode(omode)!=sp->chan->mode && sp->chan->mode!=ORDWR) |
| error(Eperm); |
| devpermcheck(sp->owner, sp->perm, omode); |
| |
| cclose(c); |
| kref_get(&sp->chan->ref, 1); |
| qunlock(&srvlk); |
| poperror(); |
| return sp->chan; |
| } |
| |
| static void |
| srvcreate(struct chan *c, char *name, int omode, int perm) |
| { |
| ERRSTACK(2); |
| struct srv *sp; |
| char *sname; |
| |
| #warning "fix the OWRITE thing in srv once we are sure it will work" |
| if(0 && openmode(omode) != OWRITE) |
| error(Eperm); |
| |
| if(omode & OCEXEC) /* can't happen */ |
| panic("someone broke namec"); |
| |
| sp = kzmalloc(sizeof *sp, 0); |
| sname = kzmalloc(strlen(name) + 1, 0); |
| |
| qlock(&srvlk); |
| if(waserror()){ |
| kfree(sname); |
| kfree(sp); |
| qunlock(&srvlk); |
| nexterror(); |
| } |
| if(sp == NULL || sname == NULL) |
| error(Enomem); |
| if(srvlookup(name, -1)) |
| error(Eexist); |
| |
| sp->path = qidpath++; |
| sp->link = srv; |
| strncpy(sname, name, strlen(name) + 1); |
| sp->name = sname; |
| c->qid.type = QTFILE; |
| c->qid.path = sp->path; |
| srv = sp; |
| qunlock(&srvlk); |
| poperror(); |
| |
| kstrdup(&sp->owner, "eve"/*up->user*/); |
| sp->perm = perm & 0777; |
| sp->perm |= 0666; /* hack in free-for-all permissions TODO: secure this */ |
| |
| c->flag |= COPEN; |
| c->mode = OWRITE; |
| } |
| |
| static void |
| srvremove(struct chan *c) |
| { |
| ERRSTACK(2); |
| struct srv *sp, **l; |
| |
| if(c->qid.type == QTDIR) |
| error(Eperm); |
| |
| qlock(&srvlk); |
| if(waserror()){ |
| qunlock(&srvlk); |
| nexterror(); |
| } |
| l = &srv; |
| for(sp = *l; sp; sp = sp->link) { |
| if(sp->path == c->qid.path) |
| break; |
| |
| l = &sp->link; |
| } |
| if(sp == 0) |
| error(Enonexist); |
| #warning "we're ignoring permissions in #s:remove" |
| /* |
| * Only eve can remove system services. |
| * No one can remove #s/boot. |
| */ |
| if(0&&strcmp(sp->owner, eve) == 0 && !iseve()) |
| error(Eperm); |
| if(strcmp(sp->name, "boot") == 0) |
| error(Eperm); |
| |
| /* |
| * No removing personal services. |
| */ |
| if(0&&(sp->perm&7) != 7 && strcmp(sp->owner, "eve"/*up->user*/) && !iseve()) |
| error(Eperm); |
| |
| *l = sp->link; |
| qunlock(&srvlk); |
| poperror(); |
| |
| if(sp->chan) |
| cclose(sp->chan); |
| kfree(sp->owner); |
| kfree(sp->name); |
| kfree(sp); |
| } |
| |
| static long |
| srvwstat(struct chan *c, uint8_t *dp, long n) |
| { |
| ERRSTACK(2); |
| struct dir d; |
| struct srv *sp; |
| char *strs; |
| |
| if(c->qid.type & QTDIR) |
| error(Eperm); |
| |
| strs = NULL; |
| qlock(&srvlk); |
| if(waserror()){ |
| qunlock(&srvlk); |
| kfree(strs); |
| nexterror(); |
| } |
| |
| sp = srvlookup(NULL, c->qid.path); |
| if(sp == 0) |
| error(Enonexist); |
| |
| if(strcmp(sp->owner, "eve"/*up->user*/) != 0 && !iseve()) |
| error(Eperm); |
| |
| strs = kzmalloc(n, 0); |
| n = convM2D(dp, n, &d, strs); |
| if(n == 0) |
| error(Eshortstat); |
| if(d.mode != ~0UL) |
| sp->perm = d.mode & 0777; |
| if(d.uid && *d.uid) |
| kstrdup(&sp->owner, d.uid); |
| if(d.name && *d.name && strcmp(sp->name, d.name) != 0) { |
| if(strchr(d.name, '/') != NULL) |
| error(Ebadchar); |
| kstrdup(&sp->name, d.name); |
| } |
| |
| qunlock(&srvlk); |
| kfree(strs); |
| poperror(); |
| return n; |
| } |
| |
| static void |
| srvclose(struct chan *c) |
| { |
| ERRSTACK(1); |
| /* |
| * in theory we need to override any changes in removability |
| * since open, but since all that's checked is the owner, |
| * which is immutable, all is well. |
| */ |
| if(c->flag & CRCLOSE){ |
| if(waserror()) |
| return; |
| srvremove(c); |
| poperror(); |
| } |
| } |
| |
| static long |
| srvread(struct chan *c, void *va, long n, int64_t unused) |
| { |
| isdir(c); |
| return devdirread(c, va, n, 0, 0, srvgen); |
| } |
| |
| static long |
| srvwrite(struct chan *c, void *va, long n, int64_t unused) |
| { |
| ERRSTACK(2); |
| struct srv *sp; |
| struct chan *c1; |
| int fd; |
| char buf[32]; |
| |
| if(n >= sizeof buf) |
| error(Egreg); |
| memmove(buf, va, n); /* so we can NUL-terminate */ |
| buf[n] = 0; |
| fd = strtoul(buf, 0, 0); |
| |
| c1 = fdtochan(fd, -1, 0, 1); /* error check and inc ref */ |
| |
| qlock(&srvlk); |
| if(waserror()) { |
| qunlock(&srvlk); |
| cclose(c1); |
| nexterror(); |
| } |
| if(c1->flag & (CCEXEC|CRCLOSE)) |
| error("posted fd has remove-on-close or close-on-exec"); |
| if(c1->qid.type & QTAUTH) |
| error("cannot post auth file in srv"); |
| sp = srvlookup(NULL, c->qid.path); |
| if(sp == 0) |
| error(Enonexist); |
| |
| if(sp->chan) |
| error(Ebadusefd); |
| |
| sp->chan = c1; |
| qunlock(&srvlk); |
| poperror(); |
| return n; |
| } |
| |
| struct dev srvdevtab = { |
| 's', |
| "srv", |
| |
| devreset, |
| srvinit, |
| devshutdown, |
| srvattach, |
| srvwalk, |
| srvstat, |
| srvopen, |
| srvcreate, |
| srvclose, |
| srvread, |
| devbread, |
| srvwrite, |
| devbwrite, |
| srvremove, |
| srvwstat, |
| devpower, |
| devconfig, |
| devchaninfo, |
| }; |