ref: 3866717cbb020199d58171c1c0cdd7382a74ee82
dir: /emu/port/devsrv.c/
#include "dat.h"
#include "fns.h"
#include "error.h"
#include "interp.h"
#include <isa.h>
#include "runt.h"
typedef struct SrvFile SrvFile;
typedef struct Pending Pending;
/* request pending to a server, in case a server vanishes */
struct Pending
{
Pending* next;
Pending* prev;
int fid;
Channel* rc;
Channel* wc;
};
struct SrvFile
{
char* name;
char* user;
ulong perm;
Qid qid;
int ref;
/* root directory */
char* spec;
SrvFile* devlist;
SrvFile* entry;
/* file */
int opens;
int flags;
vlong length;
Channel* read;
Channel* write;
SrvFile* dir; /* parent directory */
Pending waitlist; /* pending requests from client opens */
};
enum
{
SORCLOSE = (1<<0),
SRDCLOSE = (1<<1),
SWRCLOSE = (1<<2),
SREMOVED = (1<<3),
};
typedef struct SrvDev SrvDev;
struct SrvDev
{
Type* Rread;
Type* Rwrite;
QLock l;
ulong pathgen;
SrvFile* devices;
};
static SrvDev dev;
void freechan(Heap*, int);
static void freerdchan(Heap*, int);
static void freewrchan(Heap*, int);
static void delwaiting(Pending*);
Type *Trdchan;
Type *Twrchan;
static int
srvgen(Chan *c, char *name, Dirtab *tab, int ntab, int s, Dir *dp)
{
SrvFile *f;
USED(name);
USED(tab);
USED(ntab);
if(s == DEVDOTDOT){
devdir(c, c->qid, "#s", 0, eve, 0555, dp);
return 1;
}
f = c->aux;
if((c->qid.type & QTDIR) == 0){
if(s > 0)
return -1;
devdir(c, f->qid, f->name, f->length, f->user, f->perm, dp);
return 1;
}
for(f = f->entry; f != nil; f = f->entry){
if(s-- == 0)
break;
}
if(f == nil)
return -1;
devdir(c, f->qid, f->name, f->length, f->user, f->perm, dp);
return 1;
}
static void
srvinit(void)
{
static uchar rmap[] = Sys_Rread_map;
static uchar wmap[] = Sys_Rwrite_map;
Trdchan = dtype(freerdchan, sizeof(Channel), Tchannel.map, Tchannel.np);
Twrchan = dtype(freewrchan, sizeof(Channel), Tchannel.map, Tchannel.np);
dev.pathgen = 1;
dev.Rread = dtype(freeheap, Sys_Rread_size, rmap, sizeof(rmap));
dev.Rwrite = dtype(freeheap, Sys_Rwrite_size, wmap, sizeof(wmap));
}
static int
srvcanattach(SrvFile *d)
{
if(strcmp(d->user, up->env->user) == 0)
return 1;
/*
* Need write permission in other to allow attaches if
* we are not the owner
*/
if(d->perm & 2)
return 1;
return 0;
}
static Chan*
srvattach(char *spec)
{
Chan *c;
SrvFile *d;
char srvname[16];
qlock(&dev.l);
if(waserror()){
qunlock(&dev.l);
nexterror();
}
if(spec[0] != '\0'){
for(d = dev.devices; d != nil; d = d->devlist){
if(strcmp(spec, d->spec) == 0){
if(!srvcanattach(d))
error(Eperm);
c = devattach('s', spec);
c->aux = d;
c->qid = d->qid;
d->ref++;
poperror();
qunlock(&dev.l);
return c;
}
}
}
d = malloc(sizeof(SrvFile));
if(d == nil)
error(Enomem);
d->ref = 1;
kstrdup(&d->spec, spec);
kstrdup(&d->user, up->env->user);
snprint(srvname, sizeof(srvname), "srv%ld", up->env->pgrp->pgrpid);
kstrdup(&d->name, srvname);
d->perm = DMDIR|0770;
mkqid(&d->qid, dev.pathgen++, 0, QTDIR);
d->devlist = dev.devices;
dev.devices = d;
poperror();
qunlock(&dev.l);
c = devattach('s', spec);
c->aux = d;
c->qid = d->qid;
return c;
}
static Walkqid*
srvwalk(Chan *c, Chan *nc, char **name, int nname)
{
SrvFile *d, *pd;
Walkqid *w;
pd = c->aux;
qlock(&dev.l);
if(waserror()){
qunlock(&dev.l);
nexterror();
}
w = devwalk(c, nc, name, nname, nil, 0, srvgen);
if(w != nil && w->clone != nil){
if(nname != 0){
for(d = pd->entry; d != nil; d = d->entry)
if(d->qid.path == w->clone->qid.path)
break;
if(d == nil)
panic("srvwalk");
if(w->clone == c)
pd->ref--;
}else
d = pd;
w->clone->aux = d;
d->ref++;
}
poperror();
qunlock(&dev.l);
return w;
}
static int
srvstat(Chan *c, uchar *db, int n)
{
qlock(&dev.l);
if(waserror()){
qunlock(&dev.l);
nexterror();
}
n = devstat(c, db, n, 0, 0, srvgen);
poperror();
qunlock(&dev.l);
return n;
}
static Chan*
srvopen(Chan *c, int omode)
{
SrvFile *sf;
openmode(omode); /* check it */
if(c->qid.type & QTDIR){
if(omode != OREAD)
error(Eisdir);
c->mode = omode;
c->flag |= COPEN;
c->offset = 0;
return c;
}
sf = c->aux;
qlock(&dev.l);
if(waserror()){
qunlock(&dev.l);
nexterror();
}
devpermcheck(sf->user, sf->perm, omode);
if(omode&ORCLOSE && strcmp(sf->user, up->env->user) != 0)
error(Eperm);
if(sf->perm & DMEXCL && sf->opens != 0)
error(Einuse);
sf->opens++;
if(omode&ORCLOSE)
sf->flags |= SORCLOSE;
poperror();
qunlock(&dev.l);
c->offset = 0;
c->flag |= COPEN;
c->mode = openmode(omode);
return c;
}
static int
srvwstat(Chan *c, uchar *dp, int n)
{
Dir *d;
SrvFile *sf, *f;
sf = c->aux;
if(strcmp(up->env->user, sf->user) != 0)
error(Eperm);
d = smalloc(sizeof(*d)+n);
if(waserror()){
free(d);
nexterror();
}
n = convM2D(dp, n, d, (char*)&d[1]);
if(n == 0)
error(Eshortstat);
if(!emptystr(d->name)){
if(sf->dir == nil)
error(Eperm);
validwstatname(d->name);
qlock(&dev.l);
for(f = sf->dir; f != nil; f = f->entry)
if(strcmp(f->name, d->name) == 0){
qunlock(&dev.l);
error(Eexist);
}
kstrdup(&sf->name, d->name);
qunlock(&dev.l);
}
if(d->mode != ~0UL)
sf->perm = d->mode & (DMEXCL|DMAPPEND|0777);
if(d->length != (vlong)-1)
sf->length = d->length;
poperror();
free(d);
return n;
}
static void
srvputdir(SrvFile *dir)
{
SrvFile **l, *d;
dir->ref--;
if(dir->ref != 0)
return;
for(l = &dev.devices; (d = *l) != nil; l = &d->devlist)
if(d == dir){
*l = d->devlist;
break;
}
free(dir->spec);
free(dir->user);
free(dir->name);
free(dir);
}
static void
srvunblock(SrvFile *sf, int fid)
{
Channel *d;
Sys_FileIO_read rreq;
Sys_FileIO_write wreq;
acquire();
if(waserror()){
release();
nexterror();
}
d = sf->read;
if(d != H){
rreq.t0 = 0;
rreq.t1 = 0;
rreq.t2 = fid;
rreq.t3 = H;
csendalt(d, &rreq, d->mid.t, -1);
}
d = sf->write;
if(d != H){
wreq.t0 = 0;
wreq.t1 = H;
wreq.t2 = fid;
wreq.t3 = H;
csendalt(d, &wreq, d->mid.t, -1);
}
poperror();
release();
}
static void
srvcancelreqs(SrvFile *sf)
{
Pending *w, *ws;
Sys_Rread rreply;
Sys_Rwrite wreply;
acquire();
ws = &sf->waitlist;
while((w = ws->next) != ws){
delwaiting(w);
if(waserror() == 0){
if(w->rc != nil){
rreply.t0 = H;
rreply.t1 = c2string(Ehungup, strlen(Ehungup));
csend(w->rc, &rreply);
}
if(w->wc != nil){
wreply.t0 = 0;
wreply.t1 = c2string(Ehungup, strlen(Ehungup));
csend(w->wc, &wreply);
}
poperror();
}
}
release();
}
static void
srvdelete(SrvFile *sf)
{
SrvFile *f, **l;
if((sf->flags & SREMOVED) == 0){
for(l = &sf->dir->entry; (f = *l) != nil; l = &f->entry){
if(sf == f){
*l = f->entry;
break;
}
}
sf->ref--;
sf->flags |= SREMOVED;
}
}
static void
srvchkref(SrvFile *sf)
{
if(sf->ref != 0)
return;
if(sf->dir != nil)
srvputdir(sf->dir);
free(sf->user);
free(sf->name);
free(sf);
}
static void
srvfree(SrvFile *sf, int flag)
{
sf->flags |= flag;
if((sf->flags & (SRDCLOSE | SWRCLOSE)) == (SRDCLOSE | SWRCLOSE)){
sf->ref--;
srvdelete(sf);
/* no further requests can arrive; return error to pending requests */
srvcancelreqs(sf);
srvchkref(sf);
}
}
static void
freerdchan(Heap *h, int swept)
{
SrvFile *sf;
release();
qlock(&dev.l);
sf = H2D(Channel*, h)->aux;
sf->read = H;
srvfree(sf, SRDCLOSE);
qunlock(&dev.l);
acquire();
freechan(h, swept);
}
static void
freewrchan(Heap *h, int swept)
{
SrvFile *sf;
release();
qlock(&dev.l);
sf = H2D(Channel*, h)->aux;
sf->write = H;
srvfree(sf, SWRCLOSE);
qunlock(&dev.l);
acquire();
freechan(h, swept);
}
static void
srvclunk(Chan *c, int remove)
{
int opens, noperm;
SrvFile *sf;
sf = c->aux;
qlock(&dev.l);
if(c->qid.type & QTDIR){
srvputdir(sf);
qunlock(&dev.l);
if(remove)
error(Eperm);
return;
}
opens = 0;
if(c->flag & COPEN){
opens = sf->opens--;
if(sf->read != H || sf->write != H)
srvunblock(sf, c->fid);
}
sf->ref--;
if(opens == 1){
if(sf->flags & SORCLOSE)
remove = 1;
}
noperm = 0;
if(remove && strcmp(sf->dir->user, up->env->user) != 0){
noperm = 1;
remove = 0;
}
if(remove)
srvdelete(sf);
srvchkref(sf);
qunlock(&dev.l);
if(noperm)
error(Eperm);
}
static void
srvclose(Chan *c)
{
srvclunk(c, 0);
}
static void
srvremove(Chan *c)
{
srvclunk(c, 1);
}
static void
addwaiting(SrvFile *sp, Pending *w)
{
Pending *sw;
sw = &sp->waitlist;
w->next = sw;
w->prev = sw->prev;
sw->prev->next = w;
sw->prev = w;
}
static void
delwaiting(Pending *w)
{
w->next->prev = w->prev;
w->prev->next = w->next;
}
static long
srvread(Chan *c, void *va, long count, vlong offset)
{
int l;
Heap * volatile h;
Array *a;
SrvFile *sp;
Channel *rc;
Channel *rd;
Pending wait;
Sys_Rread * volatile r;
Sys_FileIO_read req;
if(c->qid.type & QTDIR){
qlock(&dev.l);
if(waserror()){
qunlock(&dev.l);
nexterror();
}
l = devdirread(c, va, count, 0, 0, srvgen);
poperror();
qunlock(&dev.l);
return l;
}
sp = c->aux;
acquire();
if(waserror()){
release();
nexterror();
}
rd = sp->read;
if(rd == H)
error(Ehungup);
rc = cnewc(dev.Rread, movtmp, 1);
ptradd(D2H(rc));
if(waserror()){
ptrdel(D2H(rc));
destroy(rc);
nexterror();
}
req.t0 = offset;
req.t1 = count;
req.t2 = c->fid;
req.t3 = rc;
csend(rd, &req);
h = heap(dev.Rread);
r = H2D(Sys_Rread *, h);
ptradd(h);
if(waserror()){
ptrdel(h);
destroy(r);
nexterror();
}
wait.fid = c->fid;
wait.rc = rc;
wait.wc = nil;
addwaiting(sp, &wait);
if(waserror()){
delwaiting(&wait);
nexterror();
}
crecv(rc, r);
poperror();
delwaiting(&wait);
if(r->t1 != H)
error(string2c(r->t1));
a = r->t0;
l = 0;
if(a != H){
l = a->len;
if(l > count)
l = count;
memmove(va, a->data, l);
}
poperror();
ptrdel(h);
destroy(r);
poperror();
ptrdel(D2H(rc));
destroy(rc);
poperror();
release();
return l;
}
static long
srvwrite(Chan *c, void *va, long count, vlong offset)
{
long l;
Heap * volatile h;
SrvFile *sp;
Channel *wc;
Channel *wr;
Pending wait;
Sys_Rwrite * volatile w;
Sys_FileIO_write req;
if(c->qid.type & QTDIR)
error(Eperm);
acquire();
if(waserror()){
release();
nexterror();
}
sp = c->aux;
wr = sp->write;
if(wr == H)
error(Ehungup);
wc = cnewc(dev.Rwrite, movtmp, 1);
ptradd(D2H(wc));
if(waserror()){
ptrdel(D2H(wc));
destroy(wc);
nexterror();
}
req.t0 = offset;
req.t1 = mem2array(va, count);
req.t2 = c->fid;
req.t3 = wc;
ptradd(D2H(req.t1));
if(waserror()){
ptrdel(D2H(req.t1));
destroy(req.t1);
nexterror();
}
csend(wr, &req);
poperror();
ptrdel(D2H(req.t1));
destroy(req.t1);
h = heap(dev.Rwrite);
w = H2D(Sys_Rwrite *, h);
ptradd(h);
if(waserror()){
ptrdel(h);
destroy(w);
nexterror();
}
wait.fid = c->fid;
wait.rc = nil;
wait.wc = wc;
addwaiting(sp, &wait);
if(waserror()){
delwaiting(&wait);
nexterror();
}
crecv(wc, w);
poperror();
delwaiting(&wait);
if(w->t1 != H)
error(string2c(w->t1));
poperror();
ptrdel(h);
l = w->t0;
destroy(w);
poperror();
ptrdel(D2H(wc));
destroy(wc);
poperror();
release();
if(l < 0)
l = 0;
return l;
}
static void
srvretype(Channel *c, SrvFile *f, Type *t)
{
Heap *h;
h = D2H(c);
freetype(h->t);
h->t = t;
t->ref++;
c->aux = f;
}
int
srvf2c(char *dir, char *file, Sys_FileIO *io)
{
SrvFile *s, *f;
volatile struct { Chan *c; } c;
c.c = nil;
if(waserror()){
cclose(c.c);
return -1;
}
if(strchr(file, '/') != nil || strlen(file) >= 64 || strcmp(file, ".") == 0 || strcmp(file, "..") == 0)
error(Efilename);
c.c = namec(dir, Aaccess, 0, 0);
if((c.c->qid.type&QTDIR) == 0 || devtab[c.c->type]->dc != 's')
error("directory not a srv device");
s = c.c->aux;
qlock(&dev.l);
if(waserror()){
qunlock(&dev.l);
nexterror();
}
for(f = s->entry; f != nil; f = f->entry){
if(strcmp(f->name, file) == 0)
error(Eexist);
}
f = malloc(sizeof(SrvFile));
if(f == nil)
error(Enomem);
srvretype(io->read, f, Trdchan);
srvretype(io->write, f, Twrchan);
f->read = io->read;
f->write = io->write;
f->waitlist.next = &f->waitlist;
f->waitlist.prev = &f->waitlist;
kstrdup(&f->name, file);
kstrdup(&f->user, up->env->user);
f->perm = 0666 & (~0666 | (s->perm & 0666));
f->length = 0;
f->ref = 2;
mkqid(&f->qid, dev.pathgen++, 0, QTFILE);
f->entry = s->entry;
s->entry = f;
s->ref++;
f->dir = s;
poperror();
qunlock(&dev.l);
cclose(c.c);
poperror();
return 0;
}
Dev srvdevtab = {
's',
"srv",
srvinit,
srvattach,
srvwalk,
srvstat,
srvopen,
devcreate,
srvclose,
srvread,
devbread,
srvwrite,
devbwrite,
srvremove,
srvwstat
};