#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 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 voidsrvput(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 voidsrv9init(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 intsrv9stat(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) oserror(); 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) oserror(); 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] = "-S"; args[2] = up->genbuf; args[3] = nil; if(pipe(fd) < 0) oserror(); /* 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 voidsrv9close(Chan *c){ srvput(c->aux);} static longsrv9read(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) oserror(); return n;} static longsrv9write(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) oserror(); return n;} static voidsrv9create(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) oserror(); 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) oserror(); if(waserror()){ close(sfd); nexterror(); } osenter(); if(fprint(sfd, "%d", fd[1]) < 0){ osleave(); oserror(); } d = dirfstat(sfd); osleave(); if(d != nil){ path = d->qid.path; free(d); }else oserror(); 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 */};
#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 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)
oserror();
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)
oserror();
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] = "-S";
args[2] = up->genbuf;
args[3] = nil;
if(pipe(fd) < 0)
oserror();
/* 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)
oserror();
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)
oserror();
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)
oserror();
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)
oserror();
if(waserror()){
close(sfd);
nexterror();
}
osenter();
if(fprint(sfd, "%d", fd[1]) < 0){
osleave();
oserror();
}
d = dirfstat(sfd);
osleave();
if(d != nil){
path = d->qid.path;
free(d);
}else
oserror();
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 */
};