diff 990ceeef3bfd9d56e2e6dd39cf5ac185b1a2de08 uncommitted --- a/sys/src/9/port/devsrv.c +++ b/sys/src/9/port/devsrv.c @@ -5,38 +5,144 @@ #include "fns.h" #include "../port/error.h" +#include "netif.h" +typedef struct Link Link; +struct Link +{ + void *link; + char *name; + ulong path; +}; + typedef struct Srv Srv; struct Srv { - char *name; + Link; + char *owner; ulong perm; Chan *chan; - Srv *link; - ulong path; }; -static QLock srvlk; -static Srv *srv; -static int qidpath; +typedef struct Board Board; +struct Board +{ + Link; + QLock; -static Srv* -srvlookup(char *name, ulong qidpath) + Board *parent; + Board *child; + Srv *srv; + long id; + int qidpath; + int closed; +}; + +struct{ + QLock; + long path; +} boards; + +enum{ + Qboard, + Qclone, + Qlease, + + Qend, +}; + +Board root; + +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) + qidpath = NETTYPE(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) + qidpath = NETTYPE(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 +boardclose(Board *b) +{ + Srv *sp, *prv; + Board *ch; + + assert(b != &root); + + qlock(b); + 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); + } + + /* + * All children must be walkable from the root. + * So any board is kept as long as it's not a leaf. + * When a leaf is free'd we walk up the tree and clean + * up those patiently waiting for us. + */ + while(b->closed && b->child == nil){ + //Root should never be closed + assert(b->parent != nil); + ch = remove((Link**)&b->parent->child, b->name, b->path); + assert(ch == b); + print("deleted %ld, parent child is now %p\n", ch->id, ch->parent->child); + + b = ch->parent; + free(ch->name); + qunlock(ch); + free(ch); + if(b == nil) + return; + qlock(b); + } + qunlock(b); +} + static int srvgen(Chan *c, char *name, Dirtab*, int, int s, Dir *dp) { Srv *sp; + Board *b, *ch; Qid q; if(s == DEVDOTDOT){ @@ -43,23 +149,57 @@ 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) - s--; - } - if(sp == nil || (name != nil && (strlen(sp->name) >= sizeof(up->genbuf)))) { - qunlock(&srvlk); + b = c->aux; + ch = nil; + mkqid(&q, ~0L, 0, QTFILE); + qlock(b); + if(waserror()){ + qunlock(b); 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); + if(b->closed) + error("expired lease"); + if(name != nil){ + if(strcmp("clone", name) == 0){ + goto Clone; + } else { + 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(ch != nil){ + kstrcpy(up->genbuf, ch->name, sizeof up->genbuf); + q.vers = ch->id; + q.path = NETQID(q.vers, ch->path); + q.type = QTDIR; + devdir(c, q, up->genbuf, 0, eve, 0555|DMDIR, dp); + } else if(sp != nil){ + kstrcpy(up->genbuf, sp->name, sizeof up->genbuf); + q.vers = NETID(c->qid.path); + q.path = NETQID(q.vers, sp->path); + devdir(c, q, up->genbuf, 0, sp->owner, sp->perm, dp); + } else if(0){ +Clone: + q.vers = NETID(c->qid.path); + q.path = NETQID(q.vers, Qclone); + devdir(c, q, "clone", 0, eve, 0444, dp); + } else + error(Enonexist); + + qunlock(b); + poperror(); return 1; } @@ -66,13 +206,17 @@ static void srvinit(void) { - qidpath = 1; + root.qidpath = Qend; } static Chan* srvattach(char *spec) { - return devattach('s', spec); + Chan *c; + + c = devattach('s', spec); + c->aux = &root; + return c; } static Walkqid* @@ -90,12 +234,14 @@ char* srvname(Chan *c) { + Board *b; Srv *sp; char *s; s = nil; - qlock(&srvlk); - for(sp = srv; sp != nil; sp = sp->link) { + b = &root; + qlock(b); + for(sp = b->srv; sp != nil; sp = sp->link) { if(sp->chan == c){ s = malloc(3+strlen(sp->name)+1); if(s != nil) @@ -103,7 +249,7 @@ break; } } - qunlock(&srvlk); + qunlock(b); return s; } @@ -110,8 +256,10 @@ static Chan* srvopen(Chan *c, int omode) { + Board *b, *ch; Srv *sp; Chan *nc; + char buf[64]; if(c->qid.type == QTDIR){ if(omode & ORCLOSE) @@ -123,20 +271,46 @@ c->offset = 0; return c; } - qlock(&srvlk); + if(omode&OTRUNC) + error(Eexist); + if(omode&ORCLOSE) + error(Eperm); + + b = c->aux; + qlock(b); if(waserror()){ - qunlock(&srvlk); + qunlock(b); nexterror(); } + if(b->closed) + error("expired lease"); + if(NETTYPE(c->qid.path) == Qclone){ + ch = smalloc(sizeof *ch); + ch->qidpath = Qend; + qlock(&boards); + ch->id = ++boards.path; + qunlock(&boards); - sp = srvlookup(nil, c->qid.path); + ch->parent = b; + ch->path = b->qidpath++; + snprint(buf, sizeof buf, "%ld", ch->id); + kstrdup(&ch->name, buf); + + ch->link = b->child; + b->child = ch; + c->aux = ch; + c->qid.vers = ch->id; + c->qid.path = NETQID(ch->id, Qlease); + + poperror(); + qunlock(b); + return c; + } + + 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 +318,7 @@ nc = sp->chan; incref(nc); - qunlock(&srvlk); + qunlock(b); poperror(); cclose(c); @@ -154,6 +328,7 @@ static Chan* srvcreate(Chan *c, char *name, int omode, ulong perm) { + Board *b; Srv *sp; if(openmode(omode) != OWRITE) @@ -166,27 +341,31 @@ kstrdup(&sp->name, name); kstrdup(&sp->owner, up->user); - qlock(&srvlk); + b = c->aux; + qlock(b); if(waserror()){ - qunlock(&srvlk); + qunlock(b); free(sp->owner); free(sp->name); free(sp); nexterror(); } - if(srvlookup(name, -1) != nil) + if(b->closed) + error("expired lease"); + if(lookup(b->srv, name, ~0UL) != nil) error(Eexist); sp->perm = perm&0777; - sp->path = qidpath++; + sp->path = b->qidpath++; - c->qid.path = sp->path; + c->qid.path = NETQID(b->id, sp->path); + c->qid.vers = b->id; c->qid.type = QTFILE; - sp->link = srv; - srv = sp; + sp->link = b->srv; + b->srv = sp; - qunlock(&srvlk); + qunlock(b); poperror(); c->flag |= COPEN; @@ -198,22 +377,19 @@ static void srvremove(Chan *c) { - Srv *sp, **l; + Board *b; + Srv *sp; if(c->qid.type == QTDIR) error(Eperm); - qlock(&srvlk); + b = c->aux; + qlock(b); if(waserror()){ - qunlock(&srvlk); + qunlock(b); nexterror(); } - l = &srv; - for(sp = *l; sp != nil; sp = *l) { - if(sp->path == c->qid.path) - break; - l = &sp->link; - } + sp = lookup(b->srv, nil, c->qid.path); if(sp == nil) error(Enonexist); @@ -229,10 +405,9 @@ if((sp->perm&7) != 7 && strcmp(sp->owner, up->user) && !iseve()) error(Eperm); - *l = sp->link; - sp->link = nil; + remove((Link**)&b->srv, nil, c->qid.path); - qunlock(&srvlk); + qunlock(b); poperror(); if(sp->chan != nil) @@ -245,6 +420,7 @@ static int srvwstat(Chan *c, uchar *dp, int n) { + Board *b; char *strs; Srv *sp; Dir d; @@ -261,13 +437,14 @@ if(n == 0) error(Eshortstat); - qlock(&srvlk); + b = c->aux; + qlock(b); if(waserror()){ - qunlock(&srvlk); + qunlock(b); nexterror(); } - sp = srvlookup(nil, c->qid.path); + sp = lookup(b->srv, nil, c->qid.path); if(sp == nil) error(Enonexist); @@ -286,7 +463,7 @@ if(d.mode != ~0UL) sp->perm = d.mode & 0777; - qunlock(&srvlk); + qunlock(b); poperror(); free(strs); @@ -298,6 +475,10 @@ static void srvclose(Chan *c) { + if(NETTYPE(c->qid.path) == Qlease){ + boardclose(c->aux); + return; + } /* * in theory we need to override any changes in removability * since open, but since all that's checked is the owner, @@ -312,8 +493,17 @@ } static long -srvread(Chan *c, void *va, long n, vlong) +srvread(Chan *c, void *va, long n, vlong off) { + Board *b; + + if(NETTYPE(c->qid.path) == Qlease){ + b = c->aux; + qlock(b); + n = readstr((ulong)off, va, n, b->name); + qunlock(b); + return n; + } isdir(c); return devdirread(c, va, n, 0, 0, srvgen); } @@ -321,6 +511,7 @@ static long srvwrite(Chan *c, void *va, long n, vlong) { + Board *b; Srv *sp; Chan *c1; int fd; @@ -334,15 +525,16 @@ c1 = fdtochan(fd, -1, 0, 1); /* error check and inc ref */ - qlock(&srvlk); + b = c->aux; + qlock(b); if(waserror()) { - qunlock(&srvlk); + qunlock(b); cclose(c1); nexterror(); } 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 +543,7 @@ sp->chan = c1; - qunlock(&srvlk); + qunlock(b); poperror(); return n; } @@ -380,12 +572,14 @@ void srvrenameuser(char *old, char *new) { + Board *b; Srv *sp; - qlock(&srvlk); - for(sp = srv; sp != nil; sp = sp->link) { + b = &root; + qlock(b); + for(sp = b->srv; sp != nil; sp = sp->link) { if(sp->owner != nil && strcmp(old, sp->owner) == 0) kstrdup(&sp->owner, new); } - qunlock(&srvlk); + qunlock(b); }