diff options
author | Jacob Moody <moody@posixcafe.org> | 2022-12-02 07:16:10 +0000 |
---|---|---|
committer | Jacob Moody <moody@posixcafe.org> | 2022-12-02 07:16:10 +0000 |
commit | e483c61242937b4258f60508292467050b56f5e9 (patch) | |
tree | 33ee177cd42c92fda11a0336c2b8c375c4fb5bb9 /sys/src/9/port/devsrv.c | |
parent | 87fa1a78e323cd68189519e3413403f4423491a4 (diff) |
kernel: private srv boards
Diffstat (limited to 'sys/src/9/port/devsrv.c')
-rw-r--r-- | sys/src/9/port/devsrv.c | 496 |
1 files changed, 390 insertions, 106 deletions
diff --git a/sys/src/9/port/devsrv.c b/sys/src/9/port/devsrv.c index 8128569a1..f7d0584b2 100644 --- a/sys/src/9/port/devsrv.c +++ b/sys/src/9/port/devsrv.c @@ -5,97 +5,278 @@ #include "fns.h" #include "../port/error.h" +#define SRVTYPE(x) (((uvlong)x)&0x3) +#define SRVPATH(x) (((uvlong)x)>>2) +#define SRVQID(x, t) ((((uvlong)x)<<2)|((t)&0x3)) -typedef struct Srv Srv; -struct Srv +typedef struct Link Link; +struct Link { - char *name; + void *link; + char *name; + ulong path; char *owner; ulong perm; +}; + +typedef struct Srv Srv; +struct Srv +{ + Link; Chan *chan; - Srv *link; - ulong path; }; -static QLock srvlk; -static Srv *srv; -static int qidpath; +typedef struct Board Board; +struct Board +{ + Link; + Ref; + + Board *parent; + Board *child; + Srv *srv; + int closed; +}; + +enum{ + Qsrv, + Qclone, + Qlease, +}; + +Board root; +RWlock srvlk; +ulong srvpath; + +static char Eexpired[] = "expired lease"; -static Srv* -srvlookup(char *name, ulong qidpath) +static void* +lookup(Link *l, char *name, ulong qidpath) { - Srv *sp; + Link *lp; - for(sp = srv; sp != nil; sp = sp->link) { - if(sp->path == qidpath || (name != nil && strcmp(sp->name, name) == 0)) - return sp; + 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 +boardclunk(Board *b, int close) +{ + Srv *sp, *prv; + Board *ch; + long ref; + + if(b == &root) + return; + + if(close){ + assert(b->closed == 0); + b->closed++; + for(sp = b->srv; sp != nil; sp = prv){ + prv = sp->link; + free(sp->owner); + free(sp->name); + if(sp->chan != nil) + cclose(sp->chan); + free(sp); + } + b->srv = nil; + } + ref = decref(b); + + /* + * 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 && ref == 0){ + //Root should never be closed + assert(b->parent != nil); + ch = remove((Link**)&b->parent->child, b->name, ~0UL); + assert(ch == b); + + b = ch->parent; + free(ch->name); + free(ch->owner); + free(ch); + } +} + static int srvgen(Chan *c, char *name, Dirtab*, int, int s, Dir *dp) { Srv *sp; + Board *b, *ch; Qid q; - if(s == DEVDOTDOT){ - devdir(c, c->qid, "#s", 0, eve, 0555, dp); - return 1; - } + if(name != nil && strlen(name) >= sizeof(up->genbuf)) + return -1; - qlock(&srvlk); - if(name != nil) - sp = srvlookup(name, -1); - else { - for(sp = srv; sp != nil && s > 0; sp = sp->link) + 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 = &root; + goto Child; + } + if(name != nil){ + if(strcmp("clone", name) == 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 || (name != nil && (strlen(sp->name) >= sizeof(up->genbuf)))) { - qunlock(&srvlk); + 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; } - mkqid(&q, sp->path, 0, QTFILE); - /* make sure name string continues to exist after we release lock */ - kstrcpy(up->genbuf, sp->name, sizeof up->genbuf); - devdir(c, q, up->genbuf, 0, sp->owner, sp->perm, dp); - qunlock(&srvlk); + + runlock(&srvlk); + poperror(); return 1; } static void srvinit(void) { - qidpath = 1; + srvpath = 0; + root.path = srvpath++; + root.name = "#s"; + root.perm = 0777; + kstrdup(&root.owner, eve); } static Chan* srvattach(char *spec) { - return devattach('s', spec); + Chan *c; + + c = devattach('s', spec); + c->aux = &root; + return c; } static Walkqid* srvwalk(Chan *c, Chan *nc, char **name, int nname) { - return devwalk(c, nc, name, nname, 0, 0, srvgen); + 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 == &root) + 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); } char* srvname(Chan *c) { + Board *b; Srv *sp; char *s; s = nil; - qlock(&srvlk); - for(sp = srv; sp != nil; sp = sp->link) { + b = &root; + rlock(&srvlk); + for(sp = b->srv; sp != nil; sp = sp->link) { if(sp->chan == c){ s = malloc(3+strlen(sp->name)+1); if(s != nil) @@ -103,40 +284,81 @@ srvname(Chan *c) break; } } - qunlock(&srvlk); + runlock(&srvlk); return s; } static Chan* srvopen(Chan *c, int omode) { + Board *b, *ch; Srv *sp; Chan *nc; + char buf[64]; + + if(omode&OTRUNC) + error(Eexist); + if(omode&ORCLOSE) + error(Eperm); + + b = c->aux; + if(SRVTYPE(c->qid.path) == Qclone){; + wlock(&srvlk); + if(waserror()){ + wunlock(&srvlk); + nexterror(); + } + if(b->closed) + error(Eexpired); + + ch = smalloc(sizeof *ch); + ch->ref = 1; + ch->perm = 0770; + kstrdup(&ch->owner, up->user); + do { + ch->path = srvpath++; + snprint(buf, sizeof buf, "%ld", ch->path); + } while(lookup(b->srv, buf, ~0UL) != nil); + + ch->parent = b; + kstrdup(&ch->name, buf); + + ch->link = b->child; + b->child = ch; + c->aux = ch; + c->qid.path = SRVQID(ch->path, Qlease); + c->mode = openmode(omode); + boardclunk(b, 0); + wunlock(&srvlk); + poperror(); + return c; + } + rlock(&srvlk); + if(waserror()){ + runlock(&srvlk); + nexterror(); + } if(c->qid.type == QTDIR){ if(omode & ORCLOSE) error(Eperm); if(omode != OREAD) error(Eisdir); - c->mode = omode; + devpermcheck(b->owner, b->perm, omode); + c->mode = openmode(omode); c->flag |= COPEN; c->offset = 0; + runlock(&srvlk); + poperror(); return c; } - qlock(&srvlk); - if(waserror()){ - qunlock(&srvlk); - nexterror(); - } + if(b->closed) + error(Eexpired); - sp = srvlookup(nil, c->qid.path); + sp = lookup(b->srv, nil, c->qid.path); if(sp == nil || sp->chan == nil) error(Eshutdown); - if(omode&OTRUNC) - error(Eexist); - if(omode&ORCLOSE) - error(Eperm); if(openmode(omode)!=sp->chan->mode && sp->chan->mode!=ORDWR) error(Eperm); devpermcheck(sp->owner, sp->perm, omode); @@ -144,7 +366,7 @@ srvopen(Chan *c, int omode) nc = sp->chan; incref(nc); - qunlock(&srvlk); + runlock(&srvlk); poperror(); cclose(c); @@ -154,6 +376,7 @@ srvopen(Chan *c, int omode) static Chan* srvcreate(Chan *c, char *name, int omode, ulong perm) { + Board *b; Srv *sp; if(openmode(omode) != OWRITE) @@ -162,31 +385,40 @@ srvcreate(Chan *c, char *name, int omode, ulong perm) if(strlen(name) >= sizeof(up->genbuf)) error(Etoolong); + if(strcmp("clone", name) == 0) + error("reserved name"); + sp = smalloc(sizeof *sp); kstrdup(&sp->name, name); kstrdup(&sp->owner, up->user); - qlock(&srvlk); + b = c->aux; + wlock(&srvlk); if(waserror()){ - qunlock(&srvlk); + wunlock(&srvlk); free(sp->owner); free(sp->name); free(sp); nexterror(); } - if(srvlookup(name, -1) != nil) + if(b->closed) + error(Eexpired); + 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 = qidpath++; + sp->path = srvpath++; - c->qid.path = sp->path; + c->qid.path = SRVQID(sp->path, Qsrv); c->qid.type = QTFILE; - sp->link = srv; - srv = sp; + sp->link = b->srv; + b->srv = sp; - qunlock(&srvlk); + wunlock(&srvlk); poperror(); c->flag |= COPEN; @@ -198,41 +430,35 @@ srvcreate(Chan *c, char *name, int omode, ulong perm) static void srvremove(Chan *c) { - Srv *sp, **l; - - if(c->qid.type == QTDIR) - error(Eperm); + Board *b; + Srv *sp; - qlock(&srvlk); + b = c->aux; + wlock(&srvlk); if(waserror()){ - qunlock(&srvlk); + boardclunk(b, 0); + wunlock(&srvlk); nexterror(); } - l = &srv; - for(sp = *l; sp != nil; sp = *l) { - if(sp->path == c->qid.path) - break; - l = &sp->link; + 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); - /* - * Only eve can remove system services. - */ - if(strcmp(sp->owner, eve) == 0 && !iseve()) - error(Eperm); - - /* - * No removing personal services. - */ - if((sp->perm&7) != 7 && strcmp(sp->owner, up->user) && !iseve()) + if(strcmp(sp->owner, up->user) != 0 && !iseve()) error(Eperm); - *l = sp->link; - sp->link = nil; + remove((Link**)&b->srv, nil, c->qid.path); - qunlock(&srvlk); + boardclunk(b, 0); + wunlock(&srvlk); poperror(); if(sp->chan != nil) @@ -245,11 +471,17 @@ srvremove(Chan *c) static int srvwstat(Chan *c, uchar *dp, int n) { + Board *b, *s; char *strs; - Srv *sp; Dir d; + Link *lp; - if(c->qid.type & QTDIR) + switch(SRVTYPE(c->qid.path)){ + case Qlease: + case Qclone: + error(Eperm); + } + if(c->qid.type == QTDIR && c->aux == &root) error(Eperm); strs = smalloc(n); @@ -261,32 +493,48 @@ srvwstat(Chan *c, uchar *dp, int n) if(n == 0) error(Eshortstat); - qlock(&srvlk); + b = c->aux; + wlock(&srvlk); if(waserror()){ - qunlock(&srvlk); + wunlock(&srvlk); nexterror(); } + if(b->closed) + error(Eexpired); - sp = srvlookup(nil, c->qid.path); - if(sp == nil) + if(c->qid.type == QTDIR) + lp = b; + else + lp = lookup(b->srv, nil, c->qid.path); + if(lp == nil) error(Enonexist); - if(strcmp(sp->owner, up->user) != 0 && !iseve()) + if(strcmp(lp->owner, up->user) != 0 && !iseve()) error(Eperm); - if(d.name != nil && *d.name && strcmp(sp->name, d.name) != 0) { + 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); - kstrdup(&sp->name, d.name); + + //Ensure new name doesn't conflict with old names + 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(&sp->owner, d.uid); + kstrdup(&lp->owner, d.uid); if(d.mode != ~0UL) - sp->perm = d.mode & 0777; + lp->perm = d.mode & 0777; - qunlock(&srvlk); + wunlock(&srvlk); poperror(); free(strs); @@ -298,22 +546,48 @@ srvwstat(Chan *c, uchar *dp, int n) static void srvclose(Chan *c) { - /* - * 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){ + Board *b; + int expired; + + expired = 0; + if(SRVTYPE(c->qid.path) == Qlease) + expired++; + else if(c->flag & CRCLOSE){ + /* + * 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; + wlock(&srvlk); + boardclunk(b, expired); + wunlock(&srvlk); } static long -srvread(Chan *c, void *va, long n, vlong) +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); } @@ -321,11 +595,15 @@ srvread(Chan *c, void *va, long n, vlong) 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 */ @@ -334,15 +612,18 @@ srvwrite(Chan *c, void *va, long n, vlong) c1 = fdtochan(fd, -1, 0, 1); /* error check and inc ref */ - qlock(&srvlk); + b = c->aux; + wlock(&srvlk); if(waserror()) { - qunlock(&srvlk); + wunlock(&srvlk); cclose(c1); nexterror(); } + if(b->closed) + error(Eexpired); if(c1->qid.type & QTAUTH) error("cannot post auth file in srv"); - sp = srvlookup(nil, c->qid.path); + sp = lookup(b->srv, nil, c->qid.path); if(sp == nil) error(Enonexist); @@ -351,7 +632,7 @@ srvwrite(Chan *c, void *va, long n, vlong) sp->chan = c1; - qunlock(&srvlk); + wunlock(&srvlk); poperror(); return n; } @@ -380,12 +661,15 @@ Dev srvdevtab = { void srvrenameuser(char *old, char *new) { + Board *b; Srv *sp; - qlock(&srvlk); - for(sp = srv; sp != nil; sp = sp->link) { + b = &root; + wlock(&srvlk); + kstrdup(&b->owner, new); + for(sp = b->srv; sp != nil; sp = sp->link) { if(sp->owner != nil && strcmp(old, sp->owner) == 0) kstrdup(&sp->owner, new); } - qunlock(&srvlk); + wunlock(&srvlk); } |