static void rread(Fcall* f) { ulong n, rn, nn, delta; Dir d; Fid* fp; if (!isfdir(f, &fp)) return; if (f->count == 0) goto done; cleannames(); for (n = nn = 0; n < f->count; n += rn){ rn = convM2D((uchar*)f->data + n, f->count - n, &d, statbuf); if (rn <= BIT16SZ) break; d.name = importname(d.name); //dprint("⇒ %D\n", &d); nn += convD2M(&d, (uchar*)dirbuf + nn, sizeof(dirbuf) - nn); } delta = nn - n; setaux(fp, getaux(fp) + delta); f->count = nn; f->data = dirbuf; done: closefid(fp); }
void closereq(Req *r) { int i; if(r == nil) return; if(chatty9p > 1) fprint(2, "closereq %p %d\n", r, r->ref.ref); if(decref(&r->ref) == 0){ if(r->fid) closefid(r->fid); if(r->newfid) closefid(r->newfid); if(r->afid) closefid(r->afid); if(r->oldreq) closereq(r->oldreq); for(i=0; i<r->nflush; i++) respond(r->flush[i], nil); free(r->flush); switch(r->ifcall.type){ case Tstat: free(r->ofcall.stat); free(r->d.name); free(r->d.uid); free(r->d.gid); free(r->d.muid); break; } if(r->pool->destroy) r->pool->destroy(r); free(r->buf); free(r->rbuf); free(r); } }
void closereq(Req *r) { if(r == nil) return; if(chatty9p > 1) fprint(2, "closereq %p %ld\n", r, r->ref.ref); if(decref(&r->ref) == 0) { if(r->fid) closefid(r->fid); if(r->newfid) closefid(r->newfid); if(r->afid) closefid(r->afid); if(r->oldreq) closereq(r->oldreq); if(r->nflush) fprint(2, "closereq: flushes remaining\n"); free(r->flush); switch(r->ifcall.type) { case Tstat: free(r->ofcall.stat); free(r->d.name); free(r->d.uid); free(r->d.gid); free(r->d.muid); break; } if(r->pool->destroy) r->pool->destroy(r); free(r->buf); free(r->rbuf); free(r); } }
static int isfdir(Fcall* f, Fid **fpp) { Fid* fp; int r; fp = lookupfid(fidpool, f->fid); if (fp == nil) return 0; r = (fp->qid.type&QTDIR); if (r) *fpp = fp; else closefid(fp); return r; }
// Dir read is tricky. // We have to change the user supplied offset to match the sizes // seen by the server. Sizes seen by client are greater than those // seen by server since the change from ' ' to '␣' adds 2 bytes. static void tread(Fcall* f) { Fid* fp; fp = nil; if (!isfdir(f, &fp)) return; f->count /= 3; // sizes will grow upon return. if (fp == nil) sysfatal("can't find fid\n"); if (f->offset == 0) setaux(fp, 0); f->offset -= getaux(fp); // cumulative size delta closefid(fp); }
static void rwalk(Req *r, char *error) { if(error || r->ofcall.nwqid < r->ifcall.nwname){ if(r->ifcall.fid != r->ifcall.newfid && r->newfid) closefid(removefid(r->srv->fpool, r->newfid->fid)); if (r->ofcall.nwqid==0){ if(error==nil && r->ifcall.nwname!=0) r->error = Enotfound; }else r->error = nil; /* No error on partial walks */ }else{ if(r->ofcall.nwqid == 0){ /* Just a clone */ r->newfid->qid = r->fid->qid; }else{ /* if file trees are in use, filewalk took care of the rest */ r->newfid->qid = r->ofcall.wqid[r->ofcall.nwqid-1]; } } }
static void service(int cfd, int sfd, int dfd) { Fcall f; int r; Fid* fp; fidpool = allocfidpool(nop); for(;;){ fp = nil; r = getfcall(cfd, &f); if (r <= 0){ fprint(dfd, "trfs: getfcall %r\n"); break; } if(verbose) fprint(dfd , "c→s %F\n", &f); switch(f.type){ case Tclunk: case Tremove: // BUG in lib9p? removefid leaks fid. // is that what it should do? fp = lookupfid(fidpool, f.fid); if (fp != nil){ removefid(fidpool, f.fid); closefid(fp); closefid(fp); fp = nil; } break; case Tcreate: tcreate(&f); // and also... case Topen: fp = allocfid(fidpool, f.fid); fp->aux = 0; break; case Tread: tread(&f); break; case Twalk: twalk(&f); break; case Twstat: twstat(&f); break; } if(verbose && debug) fprint(dfd , "c→s %F\n", &f); if (putfcall(sfd, &f) < 0) fprint(dfd , "can't putfcall: %r\n"); r = getfcall(sfd, &f); if (r <= 0){ fprint(dfd, "trfs: 2nd getfcall %r\n"); break; } if (verbose) fprint(dfd, "c←s %F\n", &f); switch(f.type){ case Ropen: case Rcreate: fp->qid = f.qid; break; case Rread: rread(&f); break; case Rstat: rstat(&f); break; } if(verbose && debug) fprint(dfd , "c←s %F\n", &f); if (putfcall(cfd, &f) < 0) fprint(dfd , "can't 2n dputfcall: %r\n"); if (fp != nil) closefid(fp); } }
void respond(Req *r, char *error) { int i, m, n; char errbuf[ERRMAX]; Srv *srv; srv = r->srv; assert(srv != nil); if(r->responded){ assert(r->pool); goto free; } assert(r->responded == 0); r->error = error; switch(r->ifcall.type){ default: assert(0); /* * Flush is special. If the handler says so, we return * without further processing. Respond will be called * again once it is safe. */ case Tflush: if(rflush(r, error)<0) return; break; case Tversion: rversion(r, error); break; case Tauth: rauth(r, error); break; case Tattach: rattach(r, error); break; case Twalk: rwalk(r, error); break; case Topen: ropen(r, error); break; case Tcreate: rcreate(r, error); break; case Tread: rread(r, error); break; case Twrite: rwrite(r, error); break; case Tclunk: rclunk(r, error); break; case Tremove: rremove(r, error, errbuf); break; case Tstat: rstat(r, error); break; case Twstat: rwstat(r, error); break; } r->ofcall.tag = r->ifcall.tag; r->ofcall.type = r->ifcall.type+1; if(r->error) setfcallerror(&r->ofcall, r->error); if(chatty9p) fprint(2, "-%d-> %F\n", srv->outfd, &r->ofcall); qlock(&srv->wlock); n = convS2M(&r->ofcall, srv->wbuf, srv->msize); if(n <= 0){ fprint(2, "n = %d %F\n", n, &r->ofcall); abort(); } assert(n > 2); /* * There is a race here - we must remove the entry before * the write, so that if the client is very fast and reuses the * tag, the read loop won't think it is still in use. * * By removing the entry before the write, we open up a * race with incoming Tflush messages. Specifically, an * incoming Tflush might not see r even though it has not * yet been responded to. It would then send an Rflush * immediately, potentially before we do the write. This can't * happen because we already old srv->wlock, so nothing * is going out on the wire before this write. */ if(r->pool) /* not a fake */ closereq(removereq(r->pool, r->ifcall.tag)); qlock(&r->lk); r->responded = 1; if(r->pool) if(r->ref.ref == 1+r->nflush) if(r->fid){ /* * There are no references other than in our r->flush array, * so no one else should be accessing r concurrently. * Close the fid now, before responding to the message. * * If the client is behaving (there are no outstanding T-messages * that reference r->fid) and the message is a Tclunk or Tremove, * then this closefid will call destroyfid. * * This means destroyfid can't piddle around * indefinitely (we're holding srv->wlock!), but it provides * for tighter semantics as to when destroyfid is called. * * LANL has observed cases where waiting until after the write * can delay a closefid on a Twrite for many 9P transactions, * so that a handful of transactions can happen including a Tclunk * and a Topen, and the original fid will still not be destroyed. */ closefid(r->fid); r->fid = nil; } qunlock(&r->lk); m = write(srv->outfd, srv->wbuf, n); if(m != n) sysfatal("lib9p srv: write %d returned %d on fd %d: %r", n, m, srv->outfd); qunlock(&srv->wlock); free: qlock(&r->lk); /* no one will add flushes now */ for(i=0; i<r->nflush; i++){ r->flush[i]->oldreq = nil; /* so it doesn't try to lock us! */ respond(r->flush[i], nil); } free(r->flush); r->flush = nil; r->nflush = 0; qunlock(&r->lk); if(r->pool) closereq(r); else free(r); }
static void rattach(Req *r, char *error) { if(error && r->fid) closefid(removefid(r->srv->fpool, r->fid->fid)); }