ref: babf901b4a508c3ec5d1f89655f10377bbdf9637
dir: /emu/Plan9/devsrv9.c/
#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 */
};