#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "../port/error.h"
typedef struct Fid Fid;
typedef struct Export Export;
typedef struct Exq Exq;
typedef struct Uqid Uqid;
enum
{
Nfidhash = 32,
Nqidhash = 32,
QIDMASK = ((vlong)1<<48)-1,
MAXFDATA = 8192,
MAXRPC = IOHDRSZ+MAXFDATA,
MSGHDRSZ = BIT32SZ+BIT8SZ+BIT16SZ
};
struct Export
{
Lock;
Ref r;
Exq* work;
Lock fidlock;
Fid* fid[Nfidhash];
QLock qidlock;
Uqid* qids[Nqidhash];
ulong pathgen;
Chan* io;
Chan* root;
Pgrp* pgrp;
Egrp* egrp;
Fgrp* fgrp;
int async;
int readonly;
int msize;
char* user;
};
struct Fid
{
Fid* next;
Fid** last;
Chan* chan;
int fid;
int ref; /* fcalls using the fid; locked by Export.Lock */
vlong offset; /* last offset used (within directory) */
int attached; /* fid attached or cloned but not clunked */
Uqid* qid; /* generated qid */
};
struct Uqid
{
Ref;
int type;
int dev;
vlong oldpath;
vlong newpath;
Uqid* next;
};
struct Exq
{
Lock;
int busy; /* fcall in progress */
int finished; /* will do no more work on this request or flushes */
Exq* next;
int shut; /* has been noted for shutdown */
Exq* flush; /* queued flush requests */
Exq* flusht; /* tail of flush queue */
Export* export;
Proc* slave;
Fcall in, out;
/* int msize; */
uchar buf[MAXRPC];
};
struct
{
Lock l;
QLock qwait;
Rendez rwait;
Exq *head; /* work waiting for a slave */
Exq *tail;
}exq;
static void exshutdown(Export*);
static int exflushed(Export*, Exq*);
static void exslave(void*);
static void exfree(Export*);
static void exfreeq(Exq*);
static void exportproc(void*);
static void exreply(Exq*, char*);
static int exisroot(Export*, Chan*);
static Uqid* uqidalloc(Export*, Chaninfo*);
static void freeuqid(Export*, Uqid*);
static void swiproc(Proc*, int);
static void notkilled(void);
extern Chan* cunique(Chan*);
extern Chan* createdir(Chan*, Mhead*);
extern Path* addelem(Path*, char*, Chan*);
static char* Exversion(Export*, Fcall*, Fcall*);
static char* Exauth(Export*, Fcall*, Fcall*);
static char* Exattach(Export*, Fcall*, Fcall*);
static char* Exclunk(Export*, Fcall*, Fcall*);
static char* Excreate(Export*, Fcall*, Fcall*);
static char* Exopen(Export*, Fcall*, Fcall*);
static char* Exread(Export*, Fcall*, Fcall*);
static char* Exremove(Export*, Fcall*, Fcall*);
static char* Exstat(Export*, Fcall*, Fcall*);
static char* Exwalk(Export*, Fcall*, Fcall*);
static char* Exwrite(Export*, Fcall*, Fcall*);
static char* Exwstat(Export*, Fcall*, Fcall*);
static char *(*fcalls[Tmax])(Export*, Fcall*, Fcall*);
static char *tnames[Tmax];
static char Enofid[] = "no such fid";
static char Eseekdir[] = "can't seek on a directory";
static char Eopen[] = "walk of open fid";
static char Emode[] = "open/create -- unknown mode";
static char Edupfid[] = "fid in use";
static char Eaccess[] = "read/write -- not open in suitable mode";
static char Ecount[] = "read/write -- count too big";
int exdebug = 0;
static void kwerrstr(char*, ...);
#pragma varargck argpos kwerrstr 1
int export(int, char*, int);
long
sysexport(ulong *arg)
{
int fd;
char *dir;
int async;
fd = arg[0];
validaddr(arg[1], 1, 0);
dir = (char*)arg[1];
async = arg[2];
return export(fd, dir, async);
}
int
export(int fd, char *dir, int async)
{
Chan *c, *dc;
Pgrp *pg;
Egrp *eg;
Export *fs;
c = fdtochan(fd, ORDWR, 1, 1);
if(waserror()){
cclose(c);
nexterror();
}
dc = namec(dir, Atodir, 0, 0);
if(waserror()){
cclose(dc);
nexterror();
}
fs = malloc(sizeof(Export));
if(fs == nil)
error(Enomem);
fs->r.ref = 1;
pg = up->pgrp;
fs->pgrp = pg;
incref(pg);
eg = up->egrp;
fs->egrp = eg;
if(eg != nil)
incref(eg);
fs->fgrp = dupfgrp(nil);
kstrdup(&fs->user, up->user);
fs->root = dc;
fs->io = c;
fs->pathgen = 0;
fs->msize = 0;
c->flag |= CMSG;
fs->async = async;
if(async){
kproc("exportfs", exportproc, fs);
poperror();
poperror();
}else{
poperror();
poperror();
exportproc(fs);
}
return 0;
}
static void
exportinit(void)
{
lock(&exq.l);
if(fcalls[Tversion] != nil) {
unlock(&exq.l);
return;
}
fcalls[Tversion] = Exversion;
fcalls[Tauth] = Exauth;
fcalls[Tattach] = Exattach;
fcalls[Twalk] = Exwalk;
fcalls[Topen] = Exopen;
fcalls[Tcreate] = Excreate;
fcalls[Tread] = Exread;
fcalls[Twrite] = Exwrite;
fcalls[Tclunk] = Exclunk;
fcalls[Tremove] = Exremove;
fcalls[Tstat] = Exstat;
fcalls[Twstat] = Exwstat;
tnames[Tversion] = "Tversion";
tnames[Tauth] = "Tauth";
tnames[Tattach] = "Tattach";
tnames[Twalk] = "Twalk";
tnames[Topen] = "Topen";
tnames[Tcreate] = "Tcreate";
tnames[Tread] = "Tread";
tnames[Twrite] = "Twrite";
tnames[Tclunk] = "Tclunk";
tnames[Tremove] = "Tremove";
tnames[Tstat] = "Tstat";
tnames[Twstat] = "Twstat";
unlock(&exq.l);
}
static int
exisroot(Export *fs, Chan *c)
{
return eqchan(fs->root, c, 1);
}
static int
exreadn(Chan *c, void *buf, int n)
{
int nr, t;
if(waserror())
return -1;
for(nr = 0; nr < n;){
t = devtab[c->type]->read(c, (char*)buf+nr, n-nr, 0);
if(t <= 0)
break;
nr += t;
}
poperror();
return nr;
}
static int
exreadmsg(Chan *c, void *a, uint n)
{
int m, len;
uchar *buf;
buf = a;
m = exreadn(c, buf, BIT32SZ);
if(m < BIT32SZ){
if(m < 0)
return -1;
return 0;
}
len = GBIT32(buf);
if(len <= BIT32SZ || len > n){
kwerrstr("bad length in 9P message header");
return -1;
}
len -= BIT32SZ;
m = exreadn(c, buf+BIT32SZ, len);
if(m < len){
if(m < 0)
return -1;
return 0;
}
return BIT32SZ+m;
}
static void
exportproc(void *a)
{
Exq *q;
int async;
int n, type;
Export *fs = a;
exportinit();
fmtinstall('H', encodefmt);
for(;;){
for(n=0;; n++){
q = mallocz(sizeof(Exq), 0); /* we don't use smalloc to avoid memset */
if(q != nil || n > 6000)
break;
if(n%600 == 0)
print("exportproc %ld: waiting for memory for request\n", up->pid);
tsleep(&up->sleep, return0, nil, 100);
}
if(q == nil){
kwerrstr("out of memory: read request");
n = -1;
break;
}
memset(q, 0, sizeof(*q)-sizeof(q->buf));
n = exreadmsg(fs->io, q->buf, MAXRPC); /* TO DO: avoid copy */
if(n <= 0)
break;
if(convM2S(q->buf, n, &q->in) != n){
print("bad T: %.*H\n", n > 30 ? 30 : n, q->buf);
kwerrstr("bad T-message");
n = -1;
break;
}
type = q->in.type;
if(type < Tversion || type >= Tmax || (type&1) || type == Terror){
kwerrstr("invalid T-message type %d", type);
n = -1;
break;
}
if(exdebug)
print("export %ld <- %F\n", up->pid, &q->in);
q->out.type = type+1;
q->out.tag = q->in.tag;
q->export = fs;
incref(&fs->r);
if(fs->readonly){
switch(type){
case Topen:
if((q->in.mode & (ORCLOSE|OTRUNC|3)) == OREAD)
break;
/* FALL THROUGH */
case Tcreate:
case Twrite:
case Tremove:
case Twstat:
q->out.type = Rerror;
q->out.ename = "file system read only";
exreply(q, "exportproc");
exfreeq(q);
continue;
}
}
if(q->in.type == Tflush){
if(exflushed(fs, q)){
/* not yet started or not found (flush arrived after reply); we reply */
if(exdebug)
print("export: flush %d\n", q->in.oldtag);
exreply(q, "exportproc");
exfreeq(q);
}
continue;
}
lock(&exq.l);
if(exq.head == nil)
exq.head = q;
else
exq.tail->next = q;
q->next = nil;
exq.tail = q;
unlock(&exq.l);
if(exq.qwait.head == nil)
kproc("exslave", exslave, nil);
wakeup(&exq.rwait);
}
if(exdebug){
if(n < 0)
print("exportproc %ld shut down: %s\n", up->pid, up->errstr);
else
print("exportproc %ld shut down\n", up->pid);
}
free(q);
exshutdown(fs);
async = fs->async;
exfree(fs);
if(async)
pexit("mount shut down", 0);
}
static int
exflushed(Export *fs, Exq *fq)
{
Exq *q, **last;
ulong pid;
/* not yet started? */
lock(&exq.l);
for(last = &exq.head; (q = *last) != nil; last = &q->next)
if(q->export == fs && q->in.tag == fq->in.oldtag){
*last = q->next;
unlock(&exq.l);
/* not yet started: discard, and Rflush */
exfreeq(q);
return 1;
}
unlock(&exq.l);
/* tricky case: in progress */
lock(fs);
for(q = fs->work; q != nil; q = q->next)
if(q->in.tag == fq->in.oldtag){
pid = 0;
lock(q);
if(q->finished){
/* slave replied and emptied its flush queue; we can Rflush now */
unlock(q);
return 1;
}
/* append to slave's flush queue */
fq->next = nil;
if(q->flush != nil)
q->flusht->next = fq;
else
q->flush = fq;
q->flusht = fq;
if(q->busy){
pid = q->slave->pid;
swiproc(q->slave, 0);
}
unlock(q);
unlock(fs);
if(exdebug && pid)
print("export: swiproc %ld to flush %d\n", pid, fq->in.oldtag);
return 0;
}
unlock(fs);
/* not found */
return 1;
}
static void
exfreeq(Exq *q)
{
Exq *fq;
while((fq = q->flush) != nil){
q->flush = fq->next;
exfree(fq->export);
free(fq);
}
exfree(q->export);
free(q);
}
static void
exshutdown(Export *fs)
{
Exq *q, **last;
/* work not started */
lock(&exq.l);
for(last = &exq.head; (q = *last) != nil;)
if(q->export == fs){
*last = q->next;
exfreeq(q);
}else
last = &q->next;
unlock(&exq.l);
/* tell slaves to abandon work in progress */
lock(fs);
while((q = fs->work) != nil){
fs->work = q->next;
lock(q);
q->shut = 1;
swiproc(q->slave, 0); /* whether busy or not */
unlock(q);
}
unlock(fs);
}
static void
exfreefids(Export *fs)
{
Fid *f, *n;
int i;
for(i = 0; i < Nfidhash; i++){
for(f = fs->fid[i]; f != nil; f = n){
n = f->next;
f->attached = 0;
if(f->ref == 0) {
if(f->chan != nil)
cclose(f->chan);
freeuqid(fs, f->qid);
free(f);
} else
print("exfreefids: busy fid\n");
}
}
}
static void
exfree(Export *fs)
{
if(exdebug)
print("export p/s %ld free %p ref %ld\n", up->pid, fs, fs->r.ref);
if(decref(&fs->r) != 0)
return;
closepgrp(fs->pgrp);
closeegrp(fs->egrp);
closefgrp(fs->fgrp);
cclose(fs->root);
cclose(fs->io);
exfreefids(fs);
free(fs->user);
free(fs);
}
static int
exwork(void*)
{
return exq.head != nil;
}
static void
exslave(void*)
{
Export *fs;
Exq *q, *t, *fq, **last;
char *err;
for(;;){
up->psstate = "Idle";
qlock(&exq.qwait);
if(waserror()){
qunlock(&exq.qwait);
continue;
}
sleep(&exq.rwait, exwork, nil);
poperror();
lock(&exq.l);
q = exq.head;
if(q == nil) {
unlock(&exq.l);
qunlock(&exq.qwait);
continue;
}
exq.head = q->next;
qunlock(&exq.qwait);
/*
* put the job on the work queue before it's
* visible as off of the head queue, so it's always
* findable for flushes and shutdown
*/
notkilled();
q->slave = up;
q->busy = 1; /* fcall in progress: interruptible */
fs = q->export;
lock(fs);
q->next = fs->work;
fs->work = q;
unlock(fs);
unlock(&exq.l);
up->pgrp = q->export->pgrp;
up->egrp = q->export->egrp;
up->fgrp = q->export->fgrp;
kstrdup(&up->user, q->export->user);
if(exdebug > 1)
print("exslave %ld dispatch %F\n", up->pid, &q->in);
if(waserror()){
print("exslave %ld err %s\n", up->pid, up->errstr); /* shouldn't happen */
err = up->errstr;
}else{
if(q->in.type >= Tmax || !fcalls[q->in.type]){
snprint(up->genbuf, sizeof(up->genbuf), "unknown message: %F", &q->in);
err = up->genbuf;
}else{
switch(q->in.type){
case Tread:
q->out.data = (char*)q->buf + IOHDRSZ;
break;
case Tstat:
q->out.stat = q->buf + MSGHDRSZ+STATFIXLEN;
q->out.nstat = sizeof(q->buf)-(MSGHDRSZ+STATFIXLEN);
break;
}
up->psstate = tnames[q->in.type];
err = (*fcalls[q->in.type])(fs, &q->in, &q->out);
up->psstate = nil;
}
poperror();
}
/*
* if the fcall completed without error we must reply,
* even if a flush is pending (because the underlying server
* might have changed state), unless the export has shut down completely.
* must also reply to each flush in order, and only after the original reply (if sent).
*/
lock(q);
notkilled();
q->busy = 0; /* operation complete */
if(!q->shut){
if(q->flush == nil || err == nil){
unlock(q);
q->out.type = q->in.type+1;
q->out.tag = q->in.tag;
if(err){
q->out.type = Rerror;
q->out.ename = err;
}
exreply(q, "exslave");
lock(q);
}
while((fq = q->flush) != nil && !q->shut){
q->flush = fq->next;
unlock(q);
exreply(fq, "exslave");
exfreeq(fq);
lock(q);
}
}
q->finished = 1; /* promise not to send any more */
unlock(q);
lock(fs);
for(last = &fs->work; (t = *last) != nil; last = &t->next)
if(t == q){
*last = q->next;
break;
}
unlock(fs);
notkilled();
exfreeq(q);
}
print("exslave %ld shut down", up->pid); /* not reached */
pexit("exslave shut down", 0);
}
static void
exreply(Exq *q, char *who)
{
Export *fs;
Fcall *r;
int n;
fs = q->export;
r = &q->out;
n = fs->msize;
if(q->in.type == Tversion && r->type == Rerror)
n = MAXRPC;
n = convS2M(r, q->buf, n);
if(n == 0){
r->type = Rerror;
if(fs->msize == 0)
r->ename = "Tversion not seen";
else
r->ename = "failed to convert R-message";
n = convS2M(r, q->buf, MAXRPC);
}
if(exdebug)
print("%s %ld -> %F\n", who, up->pid, r);
if(!waserror()){
devtab[fs->io->type]->write(fs->io, q->buf, n, 0);
poperror();
}
}
static int
exiounit(Export *fs, Chan *c)
{
int iounit;
iounit = fs->msize-IOHDRSZ;
if(c->iounit != 0 && c->iounit < fs->msize)
iounit = c->iounit;
return iounit;
}
static Qid
Exrmtqid(Chaninfo *c, Uqid *qid)
{
Qid q;
q.path = qid->newpath;
q.vers = c->qid.vers;
q.type = c->qid.type;
return q;
}
static Fid*
Exmkfid(Export *fs, ulong fid)
{
ulong h;
Fid *f, *nf;
nf = malloc(sizeof(Fid));
if(nf == nil)
return nil;
lock(&fs->fidlock);
h = fid % Nfidhash;
for(f = fs->fid[h]; f != nil; f = f->next){
if(f->fid == fid){
unlock(&fs->fidlock);
free(nf);
return nil;
}
}
nf->next = fs->fid[h];
if(nf->next != nil)
nf->next->last = &nf->next;
nf->last = &fs->fid[h];
fs->fid[h] = nf;
nf->fid = fid;
nf->ref = 1;
nf->attached = 1;
nf->offset = 0;
nf->chan = nil;
nf->qid = nil;
unlock(&fs->fidlock);
return nf;
}
static Fid*
Exgetfid(Export *fs, ulong fid)
{
Fid *f;
ulong h;
lock(&fs->fidlock);
h = fid % Nfidhash;
for(f = fs->fid[h]; f; f = f->next) {
if(f->fid == fid){
if(f->attached == 0)
break;
f->ref++;
unlock(&fs->fidlock);
return f;
}
}
unlock(&fs->fidlock);
return nil;
}
static void
Exputfid(Export *fs, Fid *f)
{
Chan *c;
lock(&fs->fidlock);
f->ref--;
if(f->ref == 0 && f->attached == 0){
c = f->chan;
f->chan = nil;
*f->last = f->next;
if(f->next != nil)
f->next->last = f->last;
unlock(&fs->fidlock);
if(c != nil)
cclose(c);
freeuqid(fs, f->qid);
free(f);
return;
}
unlock(&fs->fidlock);
}
static Chan*
exmount(Chan *c, Mhead **mp, int dopath)
{
/* XXX */
Chan *nc;
Path *opath;
nc = nil;
if((c->flag & COPEN) == 0 && findmount(&nc, mp, c->type, c->dev, c->qid)){
if(waserror()){
cclose(nc);
nexterror();
}
nc = cunique(nc);
poperror();
if(dopath){
opath = c->path;
incref(opath);
pathclose(nc->path);
nc->path = opath;
}
return nc;
}
incref(c);
return c;
}
static char*
Exversion(Export *fs, Fcall *t, Fcall *r)
{
char *p;
static char version[] = VERSION9P;
r->msize = t->msize;
if(r->msize > MAXRPC)
r->msize = MAXRPC;
if(r->msize < 64)
return "message size too small";
if((p = strchr(t->version, '.')) != nil)
*p = 0;
if(strncmp(t->version, "9P", 2) ==0 && strcmp(version, t->version) <= 0){
r->version = version;
fs->msize = r->msize;
}else
r->version = "unknown";
return nil;
}
static char*
Exauth(Export *fs, Fcall *t, Fcall *r)
{
USED(fs);
USED(t);
USED(r);
return "authentication not required";
}
static char*
Exattach(Export *fs, Fcall *t, Fcall *r)
{
Fid *f;
f = Exmkfid(fs, t->fid);
if(f == nil)
return Edupfid;
if(waserror()){
f->attached = 0;
Exputfid(fs, f);
return up->errstr;
}
f->chan = cclone(fs->root);
f->qid = uqidalloc(fs, f->chan);
poperror();
r->qid = Exrmtqid(f->chan, f->qid);
Exputfid(fs, f);
return nil;
}
static char*
Exclunk(Export *fs, Fcall *t, Fcall *r)
{
Fid *f;
USED(r);
f = Exgetfid(fs, t->fid);
if(f == nil)
return Enofid;
f->attached = 0;
Exputfid(fs, f);
return nil;
}
static int
namestowalk(char **name, int nname)
{
int i;
for(i=0; i<nname; i++){
if(strcmp(name[i], "..") == 0 || strcmp(name[i], ".") == 0){
if(i == 0)
return 1;
return i;
}
}
return i;
}
static char*
Exwalk(Export *fs, Fcall *t, Fcall *r)
{
Fid *f, *nf;
Chan *c;
Chaninfo info[MAXWELEM];
int ninfo;
Uqid *qid;
int i, n;
f = Exgetfid(fs, t->fid);
if(f == nil)
return Enofid;
if(f->chan->flag & COPEN){
Exputfid(fs, f);
return Eopen;
}
if(waserror())
return up->errstr;
c = cclone(f->chan);
poperror();
ninfo = 0;
for(i=0; i<t->nwname; i+=n){
n = namestowalk(t->wname+i, t->nwname-i);
if(n==1 &&
(strcmp(t->wname[i], ".") == 0 ||
(strcmp(t->wname[i], "..") == 0 && exisroot(fs, c)))){
if(i > 0 && !(info[i-1].qid.type&QTDIR))
break;
/* no walk - dot or dotdot in root */
info[0].qid = c->qid;
info[0].dev = c->dev;
info[0].type = c->type;
ninfo = i+1;
}else{
if(walkq(&c, t->wname+i, n, 0, nil, info+i, &ninfo) < 0){
if(i == 0 && ninfo == 0){
cclose(c);
Exputfid(fs, f);
return up->errstr;
}
ninfo = i+ninfo;
break;
}
}
}
qid = f->qid;
incref(qid);
r->nwqid = ninfo;
for(i=0; i<ninfo; i++){
freeuqid(fs, qid);
qid = uqidalloc(fs, &info[i]);
r->wqid[i] = Exrmtqid(&info[i], qid);
}
if(t->newfid != t->fid){
if(r->nwqid == t->nwname){
nf = Exmkfid(fs, t->newfid);
if(nf == nil){
cclose(c);
freeuqid(fs, qid);
Exputfid(fs, f);
return Edupfid;
}
nf->chan = c;
nf->qid = qid;
Exputfid(fs, nf);
}
}else{
cclose(f->chan);
f->chan = c;
freeuqid(fs, f->qid);
f->qid = qid;
}
Exputfid(fs, f);
return nil;
}
static char*
Exopen(Export *fs, Fcall *t, Fcall *r)
{
Fid *f;
Chan *c;
Uqid *qid;
Mhead *m;
f = Exgetfid(fs, t->fid);
if(f == nil)
return Enofid;
if(f->chan->flag & COPEN){
Exputfid(fs, f);
return Emode;
}
m = nil;
c = exmount(f->chan, &m, 1);
if(waserror()){
cclose(c);
Exputfid(fs, f);
return up->errstr;
}
/* only save the mount head if it's a multiple element union */
if(m && m->mount && m->mount->next)
c->umh = m;
else
putmhead(m);
c = devtab[c->type]->open(c, t->mode);
if(t->mode & ORCLOSE)
c->flag |= CRCLOSE;
qid = uqidalloc(fs, c);
poperror();
freeuqid(fs, f->qid);
cclose(f->chan);
f->chan = c;
f->qid = qid;
f->offset = 0;
r->qid = Exrmtqid(c, f->qid);
r->iounit = exiounit(fs, c);
Exputfid(fs, f);
return nil;
}
static char*
Excreate(Export *fs, Fcall *t, Fcall *r)
{
Fid *f;
Chan *cnew, *c;
Uqid *qid;
Mhead *m;
f = Exgetfid(fs, t->fid);
if(f == nil)
return Enofid;
if(f->chan->flag & COPEN){
Exputfid(fs, f);
return Emode;
}
if(waserror()){
Exputfid(fs, f);
return up->errstr;
}
validname(t->name, 0);
if(t->name[0] == '.' && (t->name[1] == '\0' || t->name[1] == '.' && t->name[2] == '\0'))
error(Efilename); /* underlying server should check, but stop it here */
c = f->chan;
incref(c);
m = nil;
cnew = nil;
if(waserror()){
if(cnew)
cclose(cnew);
if(m)
putmhead(m);
cclose(c);
nexterror();
}
if(findmount(&cnew, &m, c->type, c->dev, c->qid))
cnew = createdir(cnew, m);
else{
cnew = c;
incref(cnew);
}
cnew = cunique(cnew);
pathclose(cnew->path);
cnew->path = c->path;
incref(cnew->path);
devtab[cnew->type]->create(cnew, t->name, t->mode, t->perm);
poperror();
if(m)
putmhead(m);
cclose(c);
c = cnew;
c->path = addelem(c->path, t->name, nil);
if(t->mode & ORCLOSE)
c->flag |= CRCLOSE;
qid = uqidalloc(fs, c);
poperror();
cclose(f->chan);
f->chan = c;
freeuqid(fs, f->qid);
f->qid = qid;
r->qid = Exrmtqid(c, f->qid);
r->iounit = exiounit(fs, c);
Exputfid(fs, f);
return nil;
}
static char*
Exread(Export *fs, Fcall *t, Fcall *r)
{
Fid *f;
Chan *c;
long off;
int dir, n, seek;
f = Exgetfid(fs, t->fid);
if(f == nil)
return Enofid;
if(waserror()) {
Exputfid(fs, f);
return up->errstr;
}
c = f->chan;
if((c->flag & COPEN) == 0)
error(Emode);
if(c->mode != OREAD && c->mode != ORDWR)
error(Eaccess);
if(t->count < 0 || t->count > fs->msize-IOHDRSZ)
error(Ecount);
if(t->offset < 0)
error(Enegoff);
dir = c->qid.type & QTDIR;
if(dir && t->offset != f->offset){
if(t->offset != 0)
error(Eseekdir);
f->offset = 0;
c->uri = 0;
c->dri = 0;
}
for(;;){
n = t->count;
seek = 0;
off = t->offset;
if(dir && f->offset != off){
off = f->offset;
n = t->offset - off;
if(n > MAXFDATA)
n = MAXFDATA;
seek = 1;
}
if(dir && c->umh != nil){
if(0)
print("union read %d uri %d dri %d\n", seek, c->uri, c->dri);
n = unionread(c, r->data, n);
}
else{
c->offset = off;
n = devtab[c->type]->read(c, r->data, n, off);
lock(c);
c->offset += n;
unlock(c);
}
f->offset = off + n;
if(n == 0 || !seek)
break;
}
r->count = n;
poperror();
Exputfid(fs, f);
return nil;
}
static char*
Exwrite(Export *fs, Fcall *t, Fcall *r)
{
Fid *f;
Chan *c;
f = Exgetfid(fs, t->fid);
if(f == nil)
return Enofid;
if(waserror()){
Exputfid(fs, f);
return up->errstr;
}
c = f->chan;
if((c->flag & COPEN) == 0)
error(Emode);
if(c->mode != OWRITE && c->mode != ORDWR)
error(Eaccess);
if(c->qid.type & QTDIR)
error(Eisdir);
if(t->count < 0 || t->count > fs->msize-IOHDRSZ)
error(Ecount);
if(t->offset < 0)
error(Enegoff);
r->count = devtab[c->type]->write(c, t->data, t->count, t->offset);
poperror();
Exputfid(fs, f);
return nil;
}
static char*
Exstat(Export *fs, Fcall *t, Fcall *r)
{
Fid *f;
Chan *c;
int n;
f = Exgetfid(fs, t->fid);
if(f == nil)
return Enofid;
c = exmount(f->chan, nil, 1);
if(waserror()){
cclose(c);
Exputfid(fs, f);
return up->errstr;
}
n = devtab[c->type]->stat(c, r->stat, r->nstat);
if(n <= BIT16SZ)
error(Eshortstat);
r->nstat = n;
poperror();
cclose(c);
Exputfid(fs, f);
return nil;
}
static char*
Exwstat(Export *fs, Fcall *t, Fcall *r)
{
Fid *f;
Chan *c;
USED(r);
f = Exgetfid(fs, t->fid);
if(f == nil)
return Enofid;
if(waserror()){
Exputfid(fs, f);
return up->errstr;
}
validstat(t->stat, t->nstat); /* check name */
c = exmount(f->chan, nil, 0);
if(waserror()){
cclose(c);
nexterror();
}
devtab[c->type]->wstat(c, t->stat, t->nstat);
poperror();
cclose(c);
poperror();
Exputfid(fs, f);
return nil;
}
static char*
Exremove(Export *fs, Fcall *t, Fcall *r)
{
Fid *f;
Chan *c;
USED(r);
f = Exgetfid(fs, t->fid);
if(f == nil)
return Enofid;
if(waserror()){
f->attached = 0;
Exputfid(fs, f);
return up->errstr;
}
c = exmount(f->chan, nil, 0);
if(waserror()){
c->type = 0; /* see below */
cclose(c);
nexterror();
}
devtab[c->type]->remove(c);
poperror();
poperror();
/*
* chan is already clunked by remove.
* however, we need to recover the chan,
* and follow sysremove's lead in making it point to root.
*/
c->type = 0;
cclose(c);
f->attached = 0;
Exputfid(fs, f);
return nil;
}
/*
* unique path generation
*/
static int
uqidhash(vlong path)
{
ulong p;
p = (ulong)path;
return ((p>>16) ^ (p>>8) ^ p) & (Nqidhash-1);
}
static Uqid **
uqidlook(Uqid **tab, Chaninfo *c, vlong path)
{
Uqid **hp, *q;
for(hp = &tab[uqidhash(path)]; (q = *hp) != nil; hp = &q->next)
if(q->type == c->type && q->dev == c->dev && q->oldpath == path)
break;
return hp;
}
static int
uqidexists(Uqid **tab, vlong path)
{
int i;
Uqid *q;
for(i=0; i<Nqidhash; i++)
for(q = tab[i]; q != nil; q = q->next)
if(q->newpath == path)
return 1;
return 0;
}
static Uqid *
uqidalloc(Export *fs, Chaninfo *c)
{
Uqid **hp, *q;
qlock(&fs->qidlock);
hp = uqidlook(fs->qids, c, c->qid.path);
if((q = *hp) != nil){
incref(q);
qunlock(&fs->qidlock);
return q;
}
q = mallocz(sizeof(*q), 1);
if(q == nil){
qunlock(&fs->qidlock);
error(Enomem);
}
q->ref = 1;
q->type = c->type;
q->dev = c->dev;
q->oldpath = c->qid.path;
q->newpath = c->qid.path;
while(uqidexists(fs->qids, q->newpath)){
if(++fs->pathgen >= (1<<16))
fs->pathgen = 1;
q->newpath = ((vlong)fs->pathgen<<48) | (q->newpath & QIDMASK);
}
q->next = nil;
*hp = q;
qunlock(&fs->qidlock);
return q;
}
static void
freeuqid(Export *fs, Uqid *q)
{
Uqid **hp;
if(q == nil)
return;
qlock(&fs->qidlock);
if(decref(q) == 0){
hp = &fs->qids[uqidhash(q->oldpath)];
for(; *hp != nil; hp = &(*hp)->next)
if(*hp == q){
*hp = q->next;
free(q);
break;
}
}
qunlock(&fs->qidlock);
}
static void
swiproc(Proc *p, int)
{
int s;
Rendez *r;
if(p == nil)
return;
s = splhi();
lock(&p->rlock);
p->notepending = 1;
r = p->r;
if(r != nil){
lock(r);
if(r->p == p){
r->p = nil;
ready(p);
}
unlock(r);
}
unlock(&p->rlock);
splx(s);
}
static void
notkilled(void)
{
}
static void
kwerrstr(char *fmt, ...)
{
va_list arg;
va_start(arg, fmt);
vsnprint(up->errstr, ERRMAX, fmt, arg);
va_end(arg);
}
|