ref: 4d69aacea023546a7150d92e147e531c38de822f
dir: /sys/src/9/port/devsrv.c/
#include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "../port/error.h" #define SRVTYPE(x) (((uint)x)&0x3) #define SRVPATH(x) (((uvlong)x)>>2) #define SRVQID(x, t) ((((uvlong)x)<<2)|((t)&0x3)) typedef struct Link Link; struct Link { void *link; char *name; char *owner; ulong perm; ulong path; }; typedef struct Srv Srv; struct Srv { Link; Chan *chan; }; typedef struct Board Board; struct Board { Link; Ref; int closed; Srv *srv; /* tree linkage */ Board *parent; Board *child; /* all boards list */ Board *prev; Board *next; }; enum{ Qsrv, Qclone, Qlease, }; static char clone[] = "clone"; static RWlock srvlk; static ulong srvpath; static Board srvroot; static void* lookup(Link *l, char *name, ulong qidpath) { Link *lp; if(qidpath != ~0UL){ assert(SRVTYPE(qidpath) == Qsrv); qidpath = SRVPATH(qidpath); } for(lp = l; lp != nil; lp = lp->link){ if(qidpath != ~0UL && lp->path == qidpath) return lp; if(name != nil && strcmp(lp->name, name) == 0) return lp; } return nil; } static void* remove(Link **l, char *name, ulong qidpath) { Link *lp; Link **last; if(qidpath != ~0UL){ assert(SRVTYPE(qidpath) == Qsrv); qidpath = SRVPATH(qidpath); } last = l; for(lp = *l; lp != nil; lp = lp->link){ if(qidpath != ~0UL && lp->path == qidpath) break; if(name != nil && strcmp(lp->name, name) == 0) break; last = &lp->link; } if(lp == nil) return nil; *last = lp->link; lp->link = nil; return lp; } static void freelink(Link *l) { free(l->name); free(l->owner); free(l); } /* always called with srvlock wlock'ed */ static void boardclunk(Board *b) { Board *ch; /* srvroot is not ref-counted nor clunkable */ if(b == &srvroot) return; if(decref(b)) return; /* * All boards must be walkable from root. So a board * is allowed to sit at zero references as long as it * still has active children. For leaf nodes we then * have to walk up the tree to clear now empty parents. */ while(b->closed && b->child == nil){ assert(b->srv == nil); assert(b->parent != nil); /* unlink from parent board */ ch = remove((Link**)&b->parent->child, b->name, ~0UL); assert(ch == b); /* unlink from all boards list */ ch->prev->next = ch->next; ch->next->prev = ch->prev; b = ch->parent; freelink(ch); } } static int srvgen(Chan *c, char *name, Dirtab*, int, int s, Dir *dp) { Srv *sp; Board *b, *ch; Qid q; if(name != nil && strlen(name) >= sizeof(up->genbuf)) return -1; b = c->aux; ch = nil; mkqid(&q, ~0L, 0, QTFILE); rlock(&srvlk); if(waserror()){ runlock(&srvlk); nexterror(); } switch(s){ case -2: /* dot */ ch = b; goto Child; case DEVDOTDOT: ch = b->parent; if(ch == nil) ch = &srvroot; goto Child; } if(name != nil){ if(strcmp(name, clone) == 0) goto Clone; sp = lookup(b->srv, name, ~0UL); if(sp == nil) ch = lookup(b->child, name, ~0UL); } else { if(s == 0) goto Clone; s--; for(sp = b->srv; sp != nil && s > 0; sp = sp->link) s--; for(ch = b->child; ch != nil && s > 0; ch = ch->link) s--; } if(sp != nil){ kstrcpy(up->genbuf, sp->name, sizeof up->genbuf); q.path = SRVQID(sp->path, Qsrv); devdir(c, q, up->genbuf, 0, sp->owner, sp->perm, dp); } else if(ch != nil){ Child: if(name != nil || s == DEVDOTDOT){ devpermcheck(ch->owner, ch->perm, OEXEC); c->aux = ch; } kstrcpy(up->genbuf, ch->name, sizeof up->genbuf); q.path = SRVQID(ch->path, Qsrv); q.type = QTDIR; devdir(c, q, up->genbuf, 0, ch->owner, ch->perm|DMDIR, dp); } else if(0){ Clone: q.path = SRVQID(SRVPATH(c->qid.path), Qclone); devdir(c, q, clone, 0, eve, 0444, dp); } else { runlock(&srvlk); poperror(); return -1; } runlock(&srvlk); poperror(); return 1; } static void srvinit(void) { srvroot.next = &srvroot; srvroot.prev = &srvroot; srvroot.path = srvpath++; srvroot.name = "#s"; srvroot.perm = 0777; kstrdup(&srvroot.owner, eve); } static Chan* srvattach(char *spec) { Chan *c; c = devattach('s', spec); c->aux = &srvroot; return c; } static Walkqid* srvwalk(Chan *c, Chan *nc, char **name, int nname) { Board *b; Walkqid *wq; wq = devwalk(c, nc, name, nname, 0, 0, srvgen); if(wq == nil || wq->clone == nil) return wq; b = wq->clone->aux; if(b == &srvroot) return wq; incref(b); return wq; } static int srvstat(Chan *c, uchar *db, int n) { Dir d; /* devstat cheats for dir stats, we care about our dir perms */ if(c->qid.type == QTDIR){ srvgen(c, nil, nil, 0, -2, &d); n = convD2M(&d, db, n); if(n == 0) error(Ebadarg); return n; } return devstat(c, db, n, 0, 0, srvgen); } static Chan* srvopen(Chan *c, int omode) { Board *b, *ch; Srv *sp; Chan *nc; char buf[64]; int mode; if(omode&OTRUNC) error(Eexist); if(omode&ORCLOSE) error(Eperm); mode = openmode(omode); b = c->aux; if(SRVTYPE(c->qid.path) == Qclone){ wlock(&srvlk); if(waserror()){ wunlock(&srvlk); nexterror(); } if(b->closed) error(Eshutdown); ch = smalloc(sizeof *ch); ch->ref = 1; ch->perm = 0770; do { ch->path = srvpath++; snprint(buf, sizeof buf, "%ld", ch->path); } while(lookup(b->srv, buf, ~0UL) != nil); kstrdup(&ch->name, buf); kstrdup(&ch->owner, up->user); /* link to all boards list */ ch->next = &srvroot; ch->prev = srvroot.prev; ch->next->prev = ch; ch->prev->next = ch; /* link to parent board */ ch->parent = b; ch->link = b->child; b->child = ch; c->aux = ch; c->qid.path = SRVQID(ch->path, Qlease); c->mode = mode; boardclunk(b); wunlock(&srvlk); poperror(); return c; } rlock(&srvlk); if(waserror()){ runlock(&srvlk); nexterror(); } if(c->qid.type == QTDIR){ if(omode != OREAD) error(Eisdir); devpermcheck(b->owner, b->perm, omode); c->mode = mode; c->flag |= COPEN; c->offset = 0; runlock(&srvlk); poperror(); return c; } if(b->closed) error(Eshutdown); sp = lookup(b->srv, nil, c->qid.path); if(sp == nil) error(Eshutdown); nc = sp->chan; if(nc == nil) error(Eshutdown); if(mode != nc->mode && nc->mode != ORDWR) error(Eperm); devpermcheck(sp->owner, sp->perm, omode); incref(nc); runlock(&srvlk); poperror(); cclose(c); return nc; } static Chan* srvcreate(Chan *c, char *name, int omode, ulong perm) { Board *b; Srv *sp; if(openmode(omode) != OWRITE) error(Eperm); if(strlen(name) >= sizeof(up->genbuf)) error(Etoolong); if(strcmp(name, clone) == 0) error(Eexist); sp = smalloc(sizeof *sp); kstrdup(&sp->name, name); kstrdup(&sp->owner, up->user); b = c->aux; wlock(&srvlk); if(waserror()){ wunlock(&srvlk); freelink(sp); nexterror(); } if(b->closed) error(Eshutdown); devpermcheck(b->owner, b->perm, OWRITE); if(lookup(b->srv, name, ~0UL) != nil) error(Eexist); if(lookup(b->child, name, ~0UL) != nil) error(Eexist); sp->perm = perm&0777; sp->path = srvpath++; c->qid.path = SRVQID(sp->path, Qsrv); c->qid.type = QTFILE; sp->link = b->srv; b->srv = sp; wunlock(&srvlk); poperror(); c->flag |= COPEN; c->mode = OWRITE; return c; } static void srvremove(Chan *c) { Board *b; Srv *sp; b = c->aux; wlock(&srvlk); if(waserror()){ boardclunk(b); wunlock(&srvlk); nexterror(); } if(c->qid.type == QTDIR) error(Eperm); switch(SRVTYPE(c->qid.path)){ case Qlease: case Qclone: error(Eperm); } sp = lookup(b->srv, nil, c->qid.path); if(sp == nil) error(Enonexist); if(strcmp(sp->owner, up->user) != 0 && !iseve()) error(Eperm); remove((Link**)&b->srv, nil, c->qid.path); boardclunk(b); wunlock(&srvlk); poperror(); if(sp->chan != nil) cclose(sp->chan); freelink(sp); } static int srvwstat(Chan *c, uchar *dp, int n) { Board *b, *s; char *strs; Dir d; Link *lp; switch(SRVTYPE(c->qid.path)){ case Qlease: case Qclone: error(Eperm); } if(c->qid.type == QTDIR && c->aux == &srvroot) error(Eperm); strs = smalloc(n); if(waserror()){ free(strs); nexterror(); } n = convM2D(dp, n, &d, strs); if(n == 0) error(Eshortstat); b = c->aux; wlock(&srvlk); if(waserror()){ wunlock(&srvlk); nexterror(); } if(b->closed) error(Eshutdown); if(c->qid.type == QTDIR) lp = b; else lp = lookup(b->srv, nil, c->qid.path); if(lp == nil) error(Enonexist); if(strcmp(lp->owner, up->user) != 0 && !iseve()) error(Eperm); if(d.name != nil && *d.name && strcmp(lp->name, d.name) != 0) { if(strchr(d.name, '/') != nil) error(Ebadchar); if(strlen(d.name) >= sizeof(up->genbuf)) error(Etoolong); /* Ensure new name doesn't conflict with old names */ if(strcmp(d.name, clone) == 0) error(Eexist); if(c->qid.type == QTDIR) s = b->parent; else s = b; if(lookup(s->srv, d.name, ~0UL) != nil) error(Eexist); if(lookup(s->child, d.name, ~0UL) != nil) error(Eexist); kstrdup(&lp->name, d.name); } if(d.uid != nil && *d.uid) kstrdup(&lp->owner, d.uid); if(d.mode != ~0UL) lp->perm = d.mode & 0777; wunlock(&srvlk); poperror(); free(strs); poperror(); return n; } static void srvclose(Chan *c) { Srv *sp, *link; Board *b; if((c->flag & CRCLOSE) != 0 && SRVTYPE(c->qid.path) != Qlease){ /* * 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(waserror()) return; srvremove(c); poperror(); return; } b = c->aux; if(b == &srvroot) return; wlock(&srvlk); if(SRVTYPE(c->qid.path) != Qlease){ boardclunk(b); wunlock(&srvlk); return; } /* free later after releasing srvlk */ sp = b->srv; b->srv = nil; b->closed++; boardclunk(b); wunlock(&srvlk); for(; sp != nil; sp = link){ link = sp->link; if(sp->chan != nil) ccloseq(sp->chan); freelink(sp); } } static long srvread(Chan *c, void *va, long n, vlong off) { Board *b; if(SRVTYPE(c->qid.path) == Qlease){ b = c->aux; rlock(&srvlk); if(waserror()){ runlock(&srvlk); nexterror(); } n = readstr((ulong)off, va, n, b->name); runlock(&srvlk); poperror(); return n; } isdir(c); return devdirread(c, va, n, 0, 0, srvgen); } static long srvwrite(Chan *c, void *va, long n, vlong) { Board *b; Srv *sp; Chan *c1; int fd; char buf[32]; if(SRVTYPE(c->qid.path) == Qlease) error(Eperm); if(n >= sizeof buf) error(Etoobig); 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 */ b = c->aux; wlock(&srvlk); if(waserror()) { wunlock(&srvlk); cclose(c1); nexterror(); } if(b->closed) error(Eshutdown); if(c1->qid.type & QTAUTH) error("cannot post auth file in srv"); sp = lookup(b->srv, nil, c->qid.path); if(sp == nil) error(Enonexist); if(sp->chan != nil) error(Ebadusefd); sp->chan = c1; if(c1->srvname == nil) kstrdup(&c1->srvname, c->path->s); wunlock(&srvlk); poperror(); return n; } Dev srvdevtab = { 's', "srv", devreset, srvinit, devshutdown, srvattach, srvwalk, srvstat, srvopen, srvcreate, srvclose, srvread, devbread, srvwrite, devbwrite, srvremove, srvwstat, }; void srvrenameuser(char *old, char *new) { Board *b; Srv *sp; wlock(&srvlk); b = &srvroot; do { if(strcmp(b->owner, old) == 0) kstrdup(&b->owner, new); for(sp = b->srv; sp != nil; sp = sp->link) if(strcmp(sp->owner, old) == 0) kstrdup(&sp->owner, new); b = b->next; } while(b != &srvroot); wunlock(&srvlk); }