#include <9pm/u.h>
#include <9pm/libc.h>
#include <9pm/fcall.h>
#include <9pm/thread.h>
#include <9pm/ns.h>
enum
{
DBG9p = (1<<0),
DBGproc = (1<<1),
DBGflush = (1<<2),
excheck = 0
};
static int exdebug = 0;
typedef struct Fid Fid;
typedef struct Qidt Qidt;
typedef struct Export Export;
typedef struct Exq Exq;
enum
{
Nfidhash = 32, /* power of 2 */
Nqidhash = 32, /* power of 2 */
PATHHI = 0x7fffffff,
MAXRPC = IOHDRSZ+8192,
};
struct Export
{
struct {
Lock l;
int ref;
} r;
Exq* work;
QLock fidlock;
Fid* fid[Nfidhash];
Qidt* qidt[Nqidhash];
Qidt* qidp[Nqidhash];
Chan* io;
Chan* root;
Fgrp* fgrp;
Pgrp* pgrp;
int async;
int version;
char *user;
Queue *q;
};
struct Fid
{
Fid* next;
Fid** last;
Chan* chan;
int fid;
Qidt *qidt; /* claim to Qid */
int ref; /* fcalls using the fid; locked by Export.Lock */
int attached; /* fid attached or cloned but not clunked */
};
struct Qidt
{
Qidt* next;
Qidt** last;
Qidt* pnext;
Qidt** plast;
int ref;
int type;
int dev;
ulong path; /* orig */
ulong uniqpath; /* allocated */
};
struct Exq
{
Lock lk;
int nointr;
int noresponse; /* don't respond to this one */
Exq* next;
int shut; /* has been noted for shutdown */
Export* export;
void* slave;
Fcall rpc;
uchar *buf;
int buflen;
Block *b;
};
struct
{
Lock l;
QLock qwait;
Rendez rwait;
Exq *head; /* work waiting for a slave */
Exq *tail;
}exq;
struct
{
Lock l;
Exq* free;
} exqalloc;
struct
{
Lock l;
Fid* free;
} fidalloc;
struct
{
Lock l;
Qidt* free;
int path;
} qidalloc;
static void exshutdown(Export*);
static void exflush(Export*, int, int);
static void exslave(void*);
static void exfree(Export*);
static void exportproc(void*);
static int exportsrv(Export*);
static char* Exversion(Export*, Fcall*);
static char* Exattach(Export*, Fcall*);
static char* Exclunk(Export*, Fcall*);
static char* Excreate(Export*, Fcall*);
static char* Exopen(Export*, Fcall*);
static char* Exread(Export*, Fcall*);
static char* Exremove(Export*, Fcall*);
static char* Exauth(Export*, Fcall*);
static char* Exstat(Export*, Fcall*);
static char* Exwalk(Export*, Fcall*);
static char* Exwrite(Export*, Fcall*);
static char* Exwstat(Export*, Fcall*);
static char *(*fcalls[Tmax])(Export*, Fcall*);
static char Enofid[] = "no such fid";
static char Eseekdir[] = "can't seek on a directory";
static char Ereaddir[] = "unaligned read of a directory";
static char Eversion[] = "Bad 9P2000 version";
static int excloses;
static int exopens;
static int collisions;
static int exqs;
static int fids;
static int qidts;
static int slaves;
int
exportfs(int fd, int async)
{
Chan *c;
Pgrp *pg;
Export *fs;
Mhead *mh;
if(waserror())
return -1;
c = fdtochan(up->fgrp, fd, ORDWR, 1, 1);
poperror();
c->flag |= CMSG;
fs = malloc(sizeof(Export));
fs->r.ref = 1;
kstrdup(&fs->user, pm_conf.eve);
fs->fgrp = dupfgrp(nil);
pg = up->pgrp;
fs->pgrp = pg;
incref(&pg->ref);
fs->root = up->slash;
incref(&fs->root->ref);
mh = nil;
domount(&fs->root, &mh);
fs->io = c;
if(fs->io->iounit == 0)
fs->io->iounit = MAXRPC; /* reasonable default; may change in Exversion */
fs->async = async;
fs->q = qopen(10*MAXRPC, 0, nil, nil);
if(async)
nsaddproc("exportfs", exportproc, fs, 0);
else
return exportsrv(fs);
return 0;
}
static void
exqfree(Exq *e)
{
uchar *p;
p = nil;
lock(&exqalloc.l);
e->next = exqalloc.free;
exqalloc.free = e;
if(e->buflen > 2*MAXRPC) {
p = e->buf;
e->buf = nil;
e->buflen = 0;
}
unlock(&exqalloc.l);
free(p);
}
static void
freefid(Fid *f)
{
lock(&fidalloc.l);
f->next = fidalloc.free;
fidalloc.free = f;
unlock(&fidalloc.l);
}
static void
freeqid(Qidt *q)
{
lock(&qidalloc.l);
q->next = qidalloc.free;
qidalloc.free = q;
unlock(&qidalloc.l);
}
static void
exportinit(void)
{
lock(&exq.l);
if(fcalls[Tauth] != nil) {
unlock(&exq.l);
return;
}
fmtinstall('F', fcallconv);
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[Tversion] = Exversion;
fcalls[Twstat] = Exwstat;
unlock(&exq.l);
}
static int
exrpcread(Export *fs, Exq *q)
{
int i, t, len, hlen;
Block *b, **l, *nb;
q->rpc.type = 0;
q->rpc.tag = 0;
/* read at least length, type, and tag and pullup to a single block */
while(qlen(fs->q) < BIT32SZ+BIT8SZ+BIT16SZ){
b = devtab[fs->io->type]->bread(fs->io, 2*MAXRPC, 0);
if(b == nil)
return -1;
if(BLEN(b) == 0) { /* EOF */
freeb(b);
return -1;
}
qaddlist(fs->q, b);
}
nb = pullupqueue(fs->q, BIT32SZ+BIT8SZ+BIT16SZ);
len = GBIT32(nb->rp);
/* read in the rest of the message */
while(qlen(fs->q) < len){
b = devtab[fs->io->type]->bread(fs->io, 2*MAXRPC, 0);
if(b == nil)
return -1;
qaddlist(fs->q, b);
}
/* pullup the header (i.e. everything except data) */
t = nb->rp[BIT32SZ];
switch(t){
case Twrite:
hlen = BIT32SZ+BIT8SZ+BIT16SZ+BIT32SZ+BIT64SZ+BIT32SZ;
break;
default:
hlen = len;
break;
}
nb = pullupqueue(fs->q, hlen);
if(convM2S(nb->rp, len, &q->rpc) <= 0){
/* bad message, dump it */
print("exrpcread: convM2S failed\n");
qdiscard(fs->q, len);
return -1;
}
/* hang the data off of the Exq struct */
l = &q->b;
*l = nil;
do {
b = qremove(fs->q);
if(hlen > 0){
b->rp += hlen;
len -= hlen;
hlen = 0;
}
i = BLEN(b);
if(i <= len){
len -= i;
*l = b;
l = &(b->next);
} else {
/* split block and put unused bit back */
nb = allocb(i-len);
memmove(nb->wp, b->rp+len, i-len);
b->wp = b->rp+len;
nb->wp += i-len;
qputback(fs->q, nb);
*l = b;
return 0;
}
}while(len > 0);
return 0;
}
static void
exportproc(void *a)
{
Export *fs = a;
if(exportsrv(fs) < 0)
iprint("exportsrv error: %r\n");
pexit("mount shut down", 0);
}
static int
exportsrv(Export *fs)
{
Exq *q;
int n;
exportinit();
exopens++;
for(;;){
lock(&exqalloc.l);
q = exqalloc.free;
if(q != nil)
exqalloc.free = q->next;
unlock(&exqalloc.l);
if(q == nil) {
exqs++;
q = malloc(sizeof(Exq));
if(q == nil) {
werrstr(Enomem);
goto bad;
}
}
if(q->buflen < fs->io->iounit) {
q->buflen = fs->io->iounit;
q->buf = realloc(q->buf, q->buflen);
}
if(waserror())
goto bad;
n = exrpcread(fs, q);
poperror();
if(n < 0)
goto bad;
if(exdebug&DBG9p)
iprint("export %d <- %F\n", getpid(), &q->rpc);
switch(q->rpc.type){
case Tflush:
exflush(fs, q->rpc.tag, q->rpc.oldtag);
exqfree(q);
continue;
case Twrite:
q->b = bl2mem(q->buf, q->b, q->rpc.count);
q->rpc.data = (char*)q->buf;
break;
}
q->export = fs;
lock(&fs->r.l);
fs->r.ref++;
unlock(&fs->r.l);
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) {
n = nsaddproc("exportfs", exslave, nil, 0);
if(exdebug&DBGproc)
iprint("launch export (pid=%ux)\n", n);
}
rendwakeup(&exq.rwait);
}
bad:
if(q != nil)
exqfree(q);
exshutdown(fs);
exfree(fs);
if(excheck) {
iprint("exportfs:\n");
iprint("%d opens, %d closes\n", exopens, excloses);
iprint("%d slaves, %d qid collisions\n", slaves, collisions);
iprint("%d e %d f %d q\n", exqs, fids, qidts);
}
return -1;
}
static void
exflush(Export *fs, int flushtag, int tag)
{
Exq *q, **last;
int n;
Fcall fc;
char buf[MAXRPC];
/* hasn't been started? */
lock(&exq.l);
last = &exq.head;
for(q = exq.head; q != nil; q = q->next){
if(q->export == fs && q->rpc.tag == tag){
*last = q->next;
unlock(&exq.l);
exfree(fs);
exqfree(q);
goto Respond;
}
last = &q->next;
}
unlock(&exq.l);
/* in progress? */
lock(&fs->r.l);
for(q = fs->work; q != nil; q = q->next){
if(q->rpc.tag == tag && !q->noresponse){
lock(&q->lk);
q->noresponse = 1;
if(!q->nointr)
swiproc(q->slave, 0);
unlock(&q->lk);
unlock(&fs->r.l);
goto Respond;
return;
}
}
unlock(&fs->r.l);
if(exdebug&DBGflush)
iprint("exflush: did not find rpc: %d\n", tag);
Respond:
fc.type = Rflush;
fc.tag = flushtag;
n = convS2M(&fc, (uchar*)buf, sizeof buf);
if(exdebug&DBGflush)
iprint("exflush -> %F\n", &fc);
if(!waserror()){
devtab[fs->io->type]->_write(fs->io, buf, n, 0);
poperror();
}
}
static void
exshutdown(Export *fs)
{
Exq *q, **last;
lock(&exq.l);
last = &exq.head;
for(q = exq.head; q != nil; q = *last){
if(q->export == fs){
*last = q->next;
exfree(fs);
exqfree(q);
continue;
}
last = &q->next;
}
unlock(&exq.l);
rescan:
lock(&fs->r.l);
for(q = fs->work; q != nil; q = q->next) {
if(!q->shut) {
q->shut = 1;
unlock(&fs->r.l);
swiproc(q->slave, 0);
goto rescan;
}
}
unlock(&fs->r.l);
}
static void
decrqid(Qidt *q)
{
if(q == nil)
return;
q->ref--;
if(q->ref > 0)
return;
*q->last = q->next;
if(q->next != nil)
q->next->last = q->last;
*q->plast = q->pnext;
if(q->pnext != nil)
q->pnext->plast = q->plast;
freeqid(q);
}
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;
if(excheck && f->ref != 0)
print("exfree ref\n");
if(f->chan != nil)
cclose(f->chan);
freefid(f);
}
}
}
static void
exfreeqids(Export *fs)
{
Qidt *q, *n;
int i;
for(i = 0; i < Nqidhash; i++){
for(q = fs->qidt[i]; q != nil; q = n){
n = q->next;
freeqid(q);
}
}
}
static void
exfree(Export *fs)
{
lock(&fs->r.l);
if(--fs->r.ref != 0){
unlock(&fs->r.l);
return;
}
unlock(&fs->r.l);
closefgrp(fs->fgrp);
closepgrp(fs->pgrp);
cclose(fs->root);
cclose(fs->io);
exfreefids(fs);
exfreeqids(fs);
qfree(fs->q);
free(fs);
excloses++;
}
static int
exwork(void *a)
{
USED(a);
return exq.head != nil;
}
static void
exslave(void *a)
{
Export *volatile fs;
Exq *volatile q, *t, **last;
char *volatile err;
int n;
USED(a);
slaves++;
if(exdebug&DBGproc)
iprint("new exslave %d\n", getpid());
for(;;){
if(exdebug&DBGproc)
iprint("exslave %d waiting for work\n", getpid());
qlock(&exq.qwait);
rendsleep(&exq.rwait, exwork, nil);
lock(&exq.l);
q = exq.head;
if(q == nil) {
unlock(&exq.l);
qunlock(&exq.qwait);
continue;
}
exq.head = q->next;
q->slave = up;
unlock(&exq.l);
qunlock(&exq.qwait);
q->noresponse = 0;
q->nointr = 0;
fs = q->export;
lock(&fs->r.l);
q->next = fs->work;
fs->work = q;
unlock(&fs->r.l);
up->fgrp = q->export->fgrp;
up->pgrp = q->export->pgrp;
// kstrdup(&up->user, q->export->user);
if(exdebug&DBGproc)
iprint("exslave %d dispatched %F\n", getpid(), &q->rpc);
if(waserror()){
iprint("exslave err %r\n");
err = up->err;
goto Err;
}
if(q->rpc.type >= Tmax || !fcalls[q->rpc.type])
err = "bad fcall type";
else
err = (*fcalls[q->rpc.type])(fs, &q->rpc);
poperror();
Err:;
freeblist(q->b);
lock(&fs->r.l);
notkilled();
last = &fs->work;
for(t = fs->work; t != nil; t = t->next){
if(t == q){
*last = q->next;
break;
}
last = &t->next;
}
if(q->shut) {
unlock(&fs->r.l);
exfree(q->export);
exqfree(q);
continue;
}
unlock(&fs->r.l);
q->rpc.type++;
if(err){
q->rpc.type = Rerror;
q->rpc.ename = err;
}
n = convS2M(&q->rpc, q->buf, q->buflen);
if(n < 0)
panic("bad message type in exslave");
if(exdebug&DBG9p)
iprint("exslave %d -> %F\n", getpid(), &q->rpc);
switch(q->rpc.type) {
case Rread:
free(q->rpc.data);
break;
case Rstat:
free(q->rpc.stat);
break;
}
lock(&q->lk);
if(q->noresponse == 0){
q->nointr = 1;
up->killed = 0;
if(!waserror()){
devtab[fs->io->type]->_write(fs->io, q->buf, n, 0);
poperror();
}
}
unlock(&q->lk);
/*
* exflush might set noresponse at this point, but
* setting noresponse means don't send a response now;
* it's okay that we sent a response already.
*/
if(exdebug&DBGproc)
iprint("exslave %d written %d\n", getpid(), q->rpc.tag);
exfree(q->export);
exqfree(q);
}
iprint("exslave shut down");
pexit("exslave shut down", 0);
}
static int
pathhash(uvlong vpath)
{
ulong path;
path = (ulong)vpath ^ (ulong)(vpath>>32);
return (path^(path>>8)^(path>>16)^(path>>24)) & (Nqidhash-1);
}
static Qid
Exrmtqid(Export *fs, Fid *f)
{
Qid r;
Chan *c;
Qidt *q;
uvlong path;
int h0, h1;
c = f->chan;
r.vers = c->qid.vers;
r.type = c->qid.type;
path = c->qid.path;
h0 = pathhash(path);
qlock(&fs->fidlock);
decrqid(f->qidt);
f->qidt = nil;
/* qidt hashes (type, dev, path) */
for(q = fs->qidt[h0]; q != nil; q = q->next) {
if(q->type==c->type && q->dev==c->dev && q->path==path) {
q->ref++;
f->qidt = q;
qunlock(&fs->fidlock);
r.path = q->uniqpath;
return r;
}
}
h1 = h0;
for (;;) {
/* qidp hashes uniqpath */
for(q = fs->qidp[h1]; q != nil; q = q->next)
if(q->uniqpath == path)
break;
if(q == nil)
break;
collisions++;
lock(&qidalloc.l);
path = qidalloc.path;
if(path == 0)
path = PATHHI;
qidalloc.path = path - 1;
unlock(&qidalloc.l);
h1 = pathhash(path);
}
r.path = path;
lock(&qidalloc.l);
q = qidalloc.free;
if(q != nil)
qidalloc.free = q->next;
unlock(&qidalloc.l);
if(q == nil) {
qidts++;
q = malloc(sizeof(Qidt));
if(q == nil) {
qunlock(&fs->fidlock);
return r;
}
}
q->next = fs->qidt[h0];
if(q->next != nil)
q->next->last = &q->next;
q->last = &fs->qidt[h0];
fs->qidt[h0] = q;
q->pnext = fs->qidp[h1];
if(q->pnext != nil)
q->pnext->plast = &q->pnext;
q->plast = &fs->qidp[h1];
fs->qidp[h1] = q;
q->ref = 1;
q->type = c->type;
q->dev = c->dev;
q->path = c->qid.path;
q->uniqpath = path;
f->qidt = q;
qunlock(&fs->fidlock);
return r;
}
static Fid*
Exmkfid(Export *fs, int fid)
{
ulong h;
Fid *f, *nf;
lock(&fidalloc.l);
nf = fidalloc.free;
if(nf != nil)
fidalloc.free = nf->next;
unlock(&fidalloc.l);
if(nf == nil) {
fids++;
nf = malloc(sizeof(Fid));
if(nf == nil)
return nil;
}
qlock(&fs->fidlock);
h = fid & (Nfidhash-1);
for(f = fs->fid[h]; f != nil; f = f->next){
if(f->fid == fid){
qunlock(&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->chan = nil;
nf->qidt = nil;
qunlock(&fs->fidlock);
return nf;
}
static Fid*
Exgetfid(Export *fs, int fid)
{
Fid *f;
ulong h;
qlock(&fs->fidlock);
h = fid & (Nfidhash-1);
for(f = fs->fid[h]; f; f = f->next) {
if(f->fid == fid){
if(f->attached == 0)
break;
f->ref++;
qunlock(&fs->fidlock);
return f;
}
}
qunlock(&fs->fidlock);
return nil;
}
static void
Exputfid(Export *fs, Fid *f)
{
Chan *c;
qlock(&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;
decrqid(f->qidt);
qunlock(&fs->fidlock);
if(c != nil)
cclose(c);
freefid(f);
return;
}
qunlock(&fs->fidlock);
}
static char*
Exversion(Export *e, Fcall *rpc)
{
e->io->iounit = rpc->msize;
if(strncmp(rpc->version, "9P2000", 6) != 0)
return Eversion;
return nil;
}
static char*
Exauth(Export *e, Fcall *rpc)
{
USED(e);
USED(rpc);
return "authentication not supported";
}
static char*
Exattach(Export *fs, Fcall *rpc)
{
Fid *f;
f = Exmkfid(fs, rpc->fid);
if(f == nil)
return Einuse;
if(waserror()){
f->attached = 0;
Exputfid(fs, f);
return up->err;
}
f->chan = cclone(fs->root);
poperror();
rpc->qid = Exrmtqid(fs, f);
Exputfid(fs, f);
return nil;
}
static Fid*
exclonefid(Export *fs, Fid *f, int new)
{
Fid *n;
n = Exmkfid(fs, new);
if(n == nil) {
n = Exgetfid(fs, new);
if(n == nil)
return nil;
n->attached = 0;
Exputfid(fs, n);
n = Exmkfid(fs, new);
if(n == nil)
return nil;
}
if(waserror())
return nil;
n->chan = cclone(f->chan);
poperror();
return n;
}
static char*
Exwalk(Export *fs, Fcall *rpc)
{
char *e;
Chan *c;
Fid *f, *nf;
int i, nwqid;
Qid wqid[MAXWELEM];
f = Exgetfid(fs, rpc->fid);
if(f == nil)
return Enofid;
nf = nil;
if(rpc->newfid != rpc->fid){
nf = exclonefid(fs, f, rpc->newfid);
f = nf;
if(f == nil)
panic("Exwalk");
}
nwqid = 0;
e = nil;
c = f->chan;
for(i = 0; i < rpc->nwname; i++){
if(i == MAXWELEM){
e = "Too many path elements";
break;
}
if(waserror()){
e = up->err;
break;
}
if(walk(&c, &rpc->wname[i], 1, 0) < 0) {
poperror();
e = Enonexist;
c = nil;
break;
}
poperror();
wqid[nwqid++] = c->qid;
}
f->chan = c;
if(nf != nil && (e != nil || nwqid != rpc->nwname)) {
nf->attached = 0;
Exputfid(fs, nf);
}
Exputfid(fs, f);
if(e != nil)
return e;
rpc->qid = Exrmtqid(fs, f);
rpc->nwqid = nwqid;
memmove(rpc->wqid, wqid, nwqid*sizeof(Qid));
return nil;
}
static char*
Exclunk(Export *fs, Fcall *rpc)
{
Fid *f;
f = Exgetfid(fs, rpc->fid);
if(f != nil){
f->attached = 0;
Exputfid(fs, f);
}
return nil;
}
static char*
Exopen(Export *fs, Fcall *rpc)
{
Fid *f;
Chan *c;
f = Exgetfid(fs, rpc->fid);
if(f == nil)
return Enofid;
if(waserror()){
Exputfid(fs, f);
return up->err;
}
c = f->chan;
c = devtab[c->type]->_open(c, rpc->mode);
poperror();
f->chan = c;
rpc->qid = Exrmtqid(fs, f);
rpc->iounit = c->iounit;
Exputfid(fs, f);
return nil;
}
extern Chan* cunique(Chan*);
extern int findmount(Chan *volatile *, Mhead *volatile *, int, int, Qid);
extern Chan* createdir(Chan*, Mhead*);
static char*
Excreate(Export *fs, Fcall *rpc)
{
Fid *f;
Mhead *volatile m;
Chan *c, *volatile cnew;
f = Exgetfid(fs, rpc->fid);
if(f == nil)
return Enofid;
if(waserror()){
Exputfid(fs, f);
return up->err;
}
c = f->chan;
m = nil;
cnew = nil; /* is this assignment necessary? */
if(!waserror()){ /* try create */
if(findmount(&cnew, &m, c->type, c->dev, c->qid))
cnew = createdir(cnew, m);
else{
cnew = c;
incref(&cnew->ref);
}
cnew = cunique(cnew);
devtab[cnew->type]->_create(cnew, rpc->name, rpc->mode&~(OEXCL|OCEXEC), rpc->perm);
poperror();
if(rpc->mode & OCEXEC)
cnew->flag |= CCEXEC;
if(rpc->mode & ORCLOSE)
cnew->flag |= CRCLOSE;
if(m)
putmhead(m);
cclose(c);
c = cnew;
}else{ /* create failed */
cclose(cnew);
if(m)
putmhead(m);
if(rpc->mode & OEXCL)
nexterror();
if(walk(&c, &rpc->name, 1, 0) < 0)
nexterror();
rpc->mode |= OTRUNC;
c = devtab[c->type]->_open(c, rpc->mode);
}
poperror();
f->chan = c;
rpc->qid = Exrmtqid(fs, f);
rpc->iounit = c->iounit;
Exputfid(fs, f);
return nil;
}
static char*
Exread(Export *fs, Fcall *rpc)
{
Fid *f;
Chan *c;
Lock *cl;
int n, dir, count, seeking, maxread;
f = Exgetfid(fs, rpc->fid);
if(f == nil)
return Enofid;
n = rpc->count;
maxread = fs->io->iounit-IOHDRSZ;
if(n > maxread)
n = maxread;
c = f->chan;
dir = c->qid.type & QTDIR;
if(dir){
if(rpc->offset < c->offset){
Exputfid(fs, f);
return Eseekdir;
}
}
rpc->data = malloc(n);
if(rpc->data == nil)
return Enomem;
if(waserror()) {
Exputfid(fs, f);
return up->err;
}
do{
seeking = 0;
if(dir && rpc->offset > c->offset){
count = rpc->offset - c->offset;
if(count > maxread)
count = maxread;
seeking = 1;
}else if(dir && rpc->offset < c->offset){
return Eseekdir;
}else{
count = n;
c->offset = rpc->offset;
}
if(dir && c->umh != nil)
count = unionread(c, rpc->data, count);
else {
count = devtab[c->type]->_read(c, rpc->data, count, c->offset);
lock(&c->lk);
c->offset += count;
unlock(&c->lk);
}
}while(count > 0 && seeking);
rpc->count = count;
poperror();
Exputfid(fs, f);
return nil;
}
static char*
Exwrite(Export *fs, Fcall *rpc)
{
Fid *f;
Chan *c;
int n, maxwrite;
n = rpc->count;
maxwrite = fs->io->iounit-IOHDRSZ;
if(n > maxwrite)
n = maxwrite;
f = Exgetfid(fs, rpc->fid);
if(f == nil)
return Enofid;
if(waserror()){
Exputfid(fs, f);
return up->err;
}
c = f->chan;
if(c->qid.type & QTDIR)
error(Eisdir);
rpc->count = devtab[c->type]->_write(c, rpc->data, n, rpc->offset);
poperror();
Exputfid(fs, f);
return nil;
}
static char*
Exstat(Export *fs, Fcall *rpc)
{
int n;
Fid *f;
Chan *c;
f = Exgetfid(fs, rpc->fid);
if(f == nil)
return Enofid;
if(waserror()){
Exputfid(fs, f);
return up->err;
}
c = f->chan;
n = fs->io->iounit;
rpc->stat = malloc(n);
rpc->nstat = devtab[c->type]->_stat(c, rpc->stat, n);
poperror();
Exputfid(fs, f);
return nil;
}
static char*
Exwstat(Export *fs, Fcall *rpc)
{
Fid *f;
Chan *c;
f = Exgetfid(fs, rpc->fid);
if(f == nil)
return Enofid;
if(waserror()){
Exputfid(fs, f);
return up->err;
}
c = f->chan;
devtab[c->type]->_wstat(c, rpc->stat, rpc->nstat);
poperror();
Exputfid(fs, f);
return nil;
}
static char*
Exremove(Export *fs, Fcall *rpc)
{
Fid *f;
Chan *c;
f = Exgetfid(fs, rpc->fid);
if(f == nil)
return Enofid;
if(waserror()){
f->attached = 0;
Exputfid(fs, f);
return up->err;
}
c = f->chan;
devtab[c->type]->_remove(c);
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;
f->attached = 0;
Exputfid(fs, f);
return nil;
}
syntax highlighted by Code2HTML, v. 0.9.1