diff options
| author | Charles.Forsyth <devnull@localhost> | 2006-12-22 17:07:39 +0000 |
|---|---|---|
| committer | Charles.Forsyth <devnull@localhost> | 2006-12-22 17:07:39 +0000 |
| commit | 37da2899f40661e3e9631e497da8dc59b971cbd0 (patch) | |
| tree | cbc6d4680e347d906f5fa7fca73214418741df72 /emu/Plan9/devsrv9.c | |
| parent | 54bc8ff236ac10b3eaa928fd6bcfc0cdb2ba46ae (diff) | |
20060303a
Diffstat (limited to 'emu/Plan9/devsrv9.c')
| -rw-r--r-- | emu/Plan9/devsrv9.c | 403 |
1 files changed, 403 insertions, 0 deletions
diff --git a/emu/Plan9/devsrv9.c b/emu/Plan9/devsrv9.c new file mode 100644 index 00000000..74c97588 --- /dev/null +++ b/emu/Plan9/devsrv9.c @@ -0,0 +1,403 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" + +typedef struct Srv Srv; +struct Srv +{ + Ref; + int fd; /* fd for opened /srv or /srv/X, or -1 */ + int sfd; /* fd for created /srv entry or -1 */ + uvlong path; + Srv *next; +}; + +static QLock srv9lk; +static Srv *srv9; +static Srv *srvroot; + +static void +oserr(void) +{ + up->genbuf[0] = 0; + oserrstr(up->genbuf, sizeof(up->genbuf)); + error(up->genbuf); +} + +static char* +srvname(Chan *c) +{ + char *p; + + p = strrchr(c->name->s, '/'); + if(p == nil) + return ""; + return p+1; +} + +static Srv* +srvget(uvlong path) +{ + Srv *sv; + + qlock(&srv9lk); + for(sv = srv9; sv != nil; sv = sv->next) + if(sv->path == path){ + incref(sv); + qunlock(&srv9lk); + return sv; + } + sv = smalloc(sizeof(*sv)); + sv->path = path; + sv->fd = -1; + sv->sfd = -1; + sv->ref = 1; + sv->next = srv9; + srv9 = sv; + qunlock(&srv9lk); + return sv; +} + +static void +srvput(Srv *sv) +{ + Srv **l; + int fd, sfd; + + if(sv != nil && decref(sv) == 0){ + qlock(&srv9lk); + for(l = &srv9; *l != nil; l = &(*l)->next) + if(*l == sv){ + *l = sv->next; + break; + } + qunlock(&srv9lk); + fd = sv->fd; + sfd = sv->sfd; + free(sv); + if(sfd >= 0){ + osenter(); + close(sfd); + osleave(); + } + if(fd >= 0){ + osenter(); + close(fd); + osleave(); + } + } +} + +static void +srv9init(void) +{ + Srv *sv; + + sv = mallocz(sizeof(*srvroot), 1); + sv->path = 0; + sv->fd = -1; + sv->ref = 1; /* subsequently never reaches zero */ + srvroot = srv9 = sv; +} + +static Chan* +srv9attach(char *spec) +{ + Chan *c; + + if(*spec) + error(Ebadspec); + c = devattach(L'₪', spec); + if(c != nil){ + incref(srvroot); + c->aux = srvroot; + } + return c; +} + +static Walkqid* +srv9walk(Chan *c, Chan *nc, char **name, int nname) +{ + int j, alloc; + Walkqid *wq; + char *n; + Dir *d; + + if(nname > 0) + isdir(c); + + alloc = 0; + wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid)); + if(waserror()){ + if(alloc) + cclose(wq->clone); + free(wq); + return nil; + } + if(nc == nil){ + nc = devclone(c); + nc->type = 0; /* device doesn't know about this channel yet */ + alloc = 1; + } + wq->clone = nc; + + for(j=0; j<nname; j++){ + if(!(nc->qid.type&QTDIR)){ + if(j==0) + error(Enotdir); + break; + } + n = name[j]; + if(strcmp(n, ".") != 0 && strcmp(n, "..") != 0){ + snprint(up->genbuf, sizeof(up->genbuf), "/srv/%s", n); + d = dirstat(up->genbuf); + if(d == nil){ + if(j == 0) + error(Enonexist); + kstrcpy(up->env->errstr, Enonexist, ERRMAX); + break; + } + nc->qid = d->qid; + free(d); + } + wq->qid[wq->nqid++] = nc->qid; + } + poperror(); + if(wq->nqid < nname){ + if(alloc) + cclose(wq->clone); + wq->clone = nil; + }else{ + /* attach cloned channel to device */ + wq->clone->type = c->type; + if(wq->clone != c) + nc->aux = srvget(nc->qid.path); + } + return wq; +} + +static int +srv9stat(Chan *c, uchar *db, int n) +{ + Srv *sv; + Dir d; + + if(c->qid.type & QTDIR){ + devdir(c, c->qid, "#₪", 0, eve, 0775, &d); + n = convD2M(&d, db, n); + if(n == 0) + error(Eshortstat); + return n; + } + sv = c->aux; + if(sv->fd >= 0){ + osenter(); + n = fstat(sv->fd, db, n); + osleave(); + }else{ + osenter(); + n = stat(srvname(c), db, n); + osleave(); + } + return n; +} + +static Chan* +srv9open(Chan *c, int omode) +{ + Srv *sv; + char *args[10]; + int fd[2], i, ifd, is9p; + Dir *d; + + sv = c->aux; + if(c->qid.type == QTDIR){ + osenter(); + sv->fd = open("/srv", omode); + osleave(); + if(sv->fd < 0) + oserr(); + c->mode = omode; + c->flag |= COPEN; + c->offset = 0; + return c; + } + + if(omode&OTRUNC || openmode(omode) != ORDWR) + error(Eperm); + if(sv->fd < 0){ + snprint(up->genbuf, sizeof(up->genbuf), "/srv/%s", srvname(c)); + + /* check permission */ + osenter(); + ifd = open(up->genbuf, omode); + osleave(); + if(ifd < 0) + oserr(); + osenter(); + d = dirfstat(ifd); + is9p = d != nil && d->qid.type & QTMOUNT; + free(d); + osleave(); + + if(is9p){ + close(ifd); + + /* spawn exportfs */ + args[0] = "exportfs"; + args[1] = "-dS"; + args[2] = up->genbuf; + args[3] = nil; + if(pipe(fd) < 0) + oserr(); + /* TO DO: without RFMEM there's a copy made of each page touched by any kproc until the exec */ + switch(rfork(RFPROC|RFNOWAIT|RFREND|RFFDG|RFNAMEG|RFENVG)){ /* no sharing except NOTEG */ + case -1: + oserrstr(up->genbuf, sizeof(up->genbuf)); + close(fd[0]); + close(fd[1]); + error(up->genbuf); + case 0: + for(i=3; i<MAXNFD; i++) + if(i != fd[1]) + close(i); + dup(fd[1], 0); + if(fd[0] != 0) + close(fd[0]); + dup(0, 1); + exec("/bin/exportfs", args); + exits("exportfs failed"); + default: + sv->fd = fd[0]; + close(fd[1]); + break; + } + }else + sv->fd = ifd; + } + + c->mode = ORDWR; + c->offset = 0; + c->flag |= COPEN; + return c; +} + +static void +srv9close(Chan *c) +{ + srvput(c->aux); +} + +static long +srv9read(Chan *c, void *va, long n, vlong off) +{ + Srv *sv; + + sv = c->aux; + osenter(); + n = pread(sv->fd, va, n, off); + osleave(); + if(n < 0) + oserr(); + return n; +} + +static long +srv9write(Chan *c, void *va, long n, vlong off) +{ + Srv *sv; + + sv = c->aux; + osenter(); + n = pwrite(sv->fd, va, n, off); + osleave(); + if(n == 0) + error(Ehungup); + if(n < 0) + oserr(); + return n; +} + +static void +srv9create(Chan *c, char *name, int omode, ulong perm) +{ + Srv *sv; + int sfd, fd[2]; + vlong path; + Dir *d; + + if(openmode(omode) != ORDWR) + error(Eperm); + + if(pipe(fd) < 0) + oserr(); + if(waserror()){ + close(fd[0]); + close(fd[1]); + nexterror(); + } + + snprint(up->genbuf, sizeof(up->genbuf), "/srv/%s", name); + osenter(); + sfd = create(up->genbuf, OWRITE|ORCLOSE, perm); + osleave(); + if(sfd < 0) + oserr(); + if(waserror()){ + close(sfd); + nexterror(); + } + osenter(); + if(fprint(sfd, "%d", fd[1]) < 0){ + osleave(); + oserr(); + } + d = dirfstat(sfd); + osleave(); + if(d != nil){ + path = d->qid.path; + free(d); + }else + oserr(); + + poperror(); + poperror(); + close(fd[1]); + + if(waserror()){ + close(sfd); + close(fd[0]); + nexterror(); + } + sv = srvget(path); + sv->fd = fd[0]; + sv->sfd = sfd; + poperror(); + + srvput((Srv*)c->aux); + c->qid.type = QTFILE; + c->qid.path = path; + c->aux = sv; + c->flag |= COPEN; + c->mode = ORDWR; + c->offset = 0; +} + +Dev srv9devtab = { + L'₪', + "srv9", + + srv9init, + srv9attach, + srv9walk, + srv9stat, + srv9open, + srv9create, /* TO DO */ + srv9close, + srv9read, + devbread, + srv9write, + devbwrite, + devremove, /* TO DO */ + devwstat, /* TO DO */ +}; |
